move actions to independent tools
This commit is contained in:
parent
de683e1dfa
commit
c070ecf0b4
11 changed files with 1020 additions and 713 deletions
|
@ -1,6 +1,8 @@
|
|||
0.8.1 - dev
|
||||
- [ ] orphan media cleaner
|
||||
- [ ] ajouter dossier /var dans la liste des caches
|
||||
- move actions to independent tools
|
||||
- add var folder
|
||||
- update admin page to a lighter interface
|
||||
|
||||
0.8 - 2021.09.07
|
||||
- update DC official values (themes,settings,tables...)
|
||||
|
|
15
_admin.php
15
_admin.php
|
@ -15,6 +15,8 @@ if (!defined('DC_CONTEXT_ADMIN')) {
|
|||
return null;
|
||||
}
|
||||
|
||||
$core->blog->settings->addNamespace('dcAdvancedCleaner');
|
||||
|
||||
$_menu['Plugins']->addItem(
|
||||
__('Advanced cleaner'),
|
||||
$core->adminurl->get('admin.plugin.dcAdvancedCleaner'),
|
||||
|
@ -25,16 +27,3 @@ $_menu['Plugins']->addItem(
|
|||
),
|
||||
$core->auth->isSuperAdmin()
|
||||
);
|
||||
|
||||
$core->addBehavior('adminDashboardFavorites', 'dcAdvancedCleanerDashboardFavorites');
|
||||
|
||||
function dcAdvancedCleanerDashboardFavorites(dcCore $core, $favs)
|
||||
{
|
||||
$favs->register('dcAdvancedCleaner', [
|
||||
'title' => __('Advanced cleaner'),
|
||||
'url' => $core->adminurl->get('admin.plugin.dcAdvancedCleaner'),
|
||||
'small-icon' => dcPage::getPF('dcAdvancedCleaner/icon.png'),
|
||||
'large-icon' => dcPage::getPF('dcAdvancedCleaner/icon-big.png'),
|
||||
'permissions' => $core->auth->isSuperAdmin()
|
||||
]);
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
if (!defined('DC_CONTEXT_MODULE')) {
|
||||
return null;
|
||||
}
|
||||
if (!$core->auth->isSuperAdmin()) {
|
||||
if (!dPage::checkSuper()) {
|
||||
return null;
|
||||
}
|
||||
if (!empty($_POST['save'])) {
|
||||
|
|
36
_prepend.php
36
_prepend.php
|
@ -14,22 +14,42 @@
|
|||
if (!defined('DC_RC_PATH')) {
|
||||
return null;
|
||||
}
|
||||
if (!defined('DC_CONTEXT_ADMIN')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$d = dirname(__FILE__) . '/inc/';
|
||||
|
||||
$core->blog->settings->addNamespace('dcAdvancedCleaner');
|
||||
|
||||
# dcac class
|
||||
$__autoload['advancedCleaner'] = $d . 'class.advanced.cleaner.php';
|
||||
$__autoload['dcAdvancedCleaner'] = $d . 'class.dc.advanced.cleaner.php';
|
||||
$__autoload['behaviorsDcAdvancedCleaner'] = $d . 'lib.dc.advanced.cleaner.behaviors.php';
|
||||
$__autoload['dcUninstaller'] = $d . 'class.dc.uninstaller.php';
|
||||
$__autoload['dcAdvancedCleanerActivityReportBehaviors'] = $d . 'lib.dc.advanced.cleaner.activityreport.php';
|
||||
|
||||
$core->addBehavior('pluginsToolsTabs',
|
||||
['behaviorsDcAdvancedCleaner', 'pluginsToolsTabs']);
|
||||
$core->addBehavior('pluginsBeforeDelete',
|
||||
['behaviorsDcAdvancedCleaner', 'pluginsBeforeDelete']);
|
||||
$core->addBehavior('themeBeforeDelete',
|
||||
['behaviorsDcAdvancedCleaner', 'themeBeforeDelete']);
|
||||
# cleaners class
|
||||
$__autoload['advancedCleanerVersions'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerSettings'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerTables'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerThemes'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerPlugins'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerCaches'] = $d . 'lib.advanced.cleaner.php';
|
||||
$__autoload['advancedCleanerVars'] = $d . 'lib.advanced.cleaner.php';
|
||||
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerVersions', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerSettings', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerTables', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerThemes', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerPlugins', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerCaches', 'create']);
|
||||
$core->addBehavior('advancedCleanerAdd', ['advancedCleanerVars', 'create']);
|
||||
|
||||
# dcac behaviors
|
||||
$core->addBehavior('adminDashboardFavorites', ['behaviorsDcAdvancedCleaner', 'adminDashboardFavorites']);
|
||||
$core->addBehavior('pluginsToolsTabs', ['behaviorsDcAdvancedCleaner', 'pluginsToolsTabs']);
|
||||
$core->addBehavior('adminModulesListDoActions', ['behaviorsDcAdvancedCleaner', 'adminModulesListDoActions']);
|
||||
$core->addBehavior('pluginsBeforeDelete', ['behaviorsDcAdvancedCleaner', 'pluginsBeforeDelete']);
|
||||
$core->addBehavior('themeBeforeDelete', ['behaviorsDcAdvancedCleaner', 'themeBeforeDelete']);
|
||||
|
||||
if (defined('ACTIVITY_REPORT')) {
|
||||
dcAdvancedCleanerActivityReportBehaviors::add($core);
|
||||
|
|
183
inc/class.advanced.cleaner.php
Normal file
183
inc/class.advanced.cleaner.php
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/**
|
||||
* @brief dcAdvancedCleaner, 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
|
||||
*/
|
||||
|
||||
abstract class advancedCleaner
|
||||
{
|
||||
protected $core;
|
||||
|
||||
private static $exclude = [
|
||||
'.', '..', '__MACOSX', '.svn', 'CVS', '.DS_Store', 'Thumbs.db'
|
||||
];
|
||||
|
||||
private $properties = [
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'desc' => ''
|
||||
];
|
||||
|
||||
private $actions = [];
|
||||
|
||||
final public function __construct(dcCore $core)
|
||||
{
|
||||
$this->core = $core;
|
||||
|
||||
$this->init();
|
||||
|
||||
}
|
||||
|
||||
public static function create(arrayObject $o, dcCore $core)
|
||||
{
|
||||
$c = get_called_class();
|
||||
$o->append(new $c($core));
|
||||
}
|
||||
|
||||
final public function __get(string $property)
|
||||
{
|
||||
return $this->getProperty($property);
|
||||
}
|
||||
|
||||
final public function getProperty(string $property)
|
||||
{
|
||||
return $this->properties[$property] ?? null;
|
||||
}
|
||||
|
||||
final protected function setProperties($property, $value = null): bool
|
||||
{
|
||||
$properties = is_array($property) ? $property : [$property => $value];
|
||||
foreach($properties as $k => $v) {
|
||||
if (isset($this->properties[$k])) {
|
||||
$this->properties[$k] = (string) $v;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final public function getActions()
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
final protected function setActions($action, $name = null): bool
|
||||
{
|
||||
$actions = is_array($action) ? $action : [$action => $name];
|
||||
foreach($actions as $k => $v) {
|
||||
$this->actions[$k] = (string) $v;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract protected function init(): bool;
|
||||
|
||||
abstract public function error($action): string;
|
||||
|
||||
abstract public function official(): array;
|
||||
|
||||
abstract public function get(): array;
|
||||
|
||||
abstract public function set($action, $ns): bool;
|
||||
|
||||
# helpers
|
||||
|
||||
protected static function getDirs($roots)
|
||||
{
|
||||
if (!is_array($roots)) {
|
||||
$roots = [$roots];
|
||||
}
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
foreach ($roots as $root) {
|
||||
$dirs = files::scanDir($root);
|
||||
foreach($dirs as $k) {
|
||||
if ('.' == $k || '..' == $k || !is_dir($root .'/' . $k)) {
|
||||
continue;
|
||||
}
|
||||
$rs[$i]['key'] = $k;
|
||||
$rs[$i]['value'] = count(self::scanDir($root . '/' . $k));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $rs;
|
||||
}
|
||||
|
||||
protected static function delDir($roots, $folder, $delfolder = true)
|
||||
{
|
||||
if (strpos($folder,'/')) {
|
||||
return false;
|
||||
}
|
||||
if (!is_array($roots)) {
|
||||
$roots = [$roots];
|
||||
}
|
||||
foreach ($roots as $root) {
|
||||
if (file_exists($root . '/' . $folder))
|
||||
return self::delTree($root . '/' . $folder, $delfolder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function scanDir($path, $dir = '', $res = [])
|
||||
{
|
||||
$exclude = self::$exclude;
|
||||
|
||||
$path = path::real($path);
|
||||
if (!is_dir($path) || !is_readable($path)) {
|
||||
return [];
|
||||
}
|
||||
$files = files::scandir($path);
|
||||
|
||||
foreach($files AS $file) {
|
||||
if (in_array($file, $exclude)) {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($path . '/' . $file)) {
|
||||
$res[] = $file;
|
||||
$res = self::scanDir($path . '/' . $file, $dir . '/' . $file, $res);
|
||||
} else {
|
||||
$res[] = empty($dir) ? $file : $dir . '/' . $file;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected static function delTree($dir, $delroot = true)
|
||||
{
|
||||
if (!is_dir($dir) || !is_readable($dir)) {
|
||||
return false;
|
||||
}
|
||||
if (substr($dir,-1) != '/') {
|
||||
$dir .= '/';
|
||||
}
|
||||
if (($d = @dir($dir)) === false) {
|
||||
return false;
|
||||
}
|
||||
while (($entryname = $d->read()) !== false) {
|
||||
if ($entryname != '.' && $entryname != '..') {
|
||||
if (is_dir($dir . '/' . $entryname)) {
|
||||
if (!self::delTree($dir . '/' . $entryname)) {return false;
|
||||
}
|
||||
} else {
|
||||
if (!@unlink($dir . '/' . $entryname)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$d->close();
|
||||
|
||||
if ($delroot) {
|
||||
return @rmdir($dir);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,397 +17,60 @@ if (!defined('DC_ADMIN_CONTEXT')) {
|
|||
|
||||
class dcAdvancedCleaner
|
||||
{
|
||||
protected static $errors = [
|
||||
'settings' => [
|
||||
'delete_global' => 'Failed to delete global settings',
|
||||
'delete_local' => 'Failed to delete local settings',
|
||||
'delete_all' => 'Failed to delete all settings'
|
||||
],
|
||||
'tables' => [
|
||||
'empty' => 'Failed to empty table',
|
||||
'delete' => 'Failed to delete table'
|
||||
],
|
||||
'plugins' => [
|
||||
'empty' => 'Failed to empty plugin folder',
|
||||
'delete' => 'Failed to delete plugin folder'
|
||||
],
|
||||
'themes' => [
|
||||
'empty' => 'Failed to empty themes folder',
|
||||
'delete' => 'Failed to delete themes folder'
|
||||
],
|
||||
'caches' => [
|
||||
'empty' => 'Failed to empty cache folder',
|
||||
'delete' => 'Failed to delete cache folder'
|
||||
],
|
||||
'versions' => [
|
||||
'delete' => 'Failed to delete version'
|
||||
]
|
||||
];
|
||||
protected $core;
|
||||
protected $cleaners = [];
|
||||
|
||||
public static $dotclear = [
|
||||
'settings' => [
|
||||
'akismet', 'antispam', 'breadcrumb', 'dcckeditor', 'dclegacyeditor', 'maintenance',
|
||||
'pages', 'pings', 'system', 'themes', 'widgets'
|
||||
],
|
||||
'tables' => [
|
||||
'blog', 'category', 'comment', 'link', 'log', 'media',
|
||||
'meta', 'permissions', 'ping', 'post','post_media', 'pref', 'session',
|
||||
'setting', 'spamrule', 'user', 'version'
|
||||
],
|
||||
'plugins' => [
|
||||
'aboutConfig', 'akismet', 'antispam', 'attachments', 'blogroll', 'blowupConfig',
|
||||
'breadcrumb', 'dcCKEditor', 'dclegacy', 'dcLegacyEditor', 'fairTrackbacks', 'importExport', 'maintenance',
|
||||
'pages', 'pings', 'simpleMenu', 'tags', 'themeEditor', 'userPref', 'widgets'
|
||||
],
|
||||
'themes' => [
|
||||
'default', 'customCSS', 'blueSilence', 'berlin', 'ductile'
|
||||
],
|
||||
'caches' => [
|
||||
'cbfeed', 'cbtpl', 'dcrepo', 'versions'
|
||||
],
|
||||
'versions' => [
|
||||
'antispam', 'blogroll', 'blowupConfig', 'core', 'dcCKEditor', 'dcLegacyEditor', 'pages', 'pings', 'simpleMenu', 'tags', 'widgets'
|
||||
]
|
||||
];
|
||||
|
||||
public static $exclude = [
|
||||
'.', '..', '__MACOSX', '.svn', 'CVS', '.DS_Store', 'Thumbs.db'
|
||||
];
|
||||
|
||||
public static function getOfficial($type = '')
|
||||
public function __construct($core)
|
||||
{
|
||||
$official = array_merge(
|
||||
self::$dotclear,
|
||||
[
|
||||
'plugins' => explode(',', DC_DISTRIB_PLUGINS),
|
||||
'themes' => explode(',', DC_DISTRIB_THEMES)
|
||||
]
|
||||
);
|
||||
if (empty($type)) {
|
||||
return $official;
|
||||
$this->core = $core;
|
||||
$cleaners = new arrayObject();
|
||||
|
||||
try {
|
||||
$this->core->callBehavior('advancedCleanerAdd', $cleaners, $this->core);
|
||||
|
||||
foreach($cleaners as $cleaner) {
|
||||
if ($cleaner instanceOf advancedCleaner && !isset($this->cleaners[$cleaner->id])) {
|
||||
$this->cleaners[$cleaner->id] = $cleaner;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$core->error->add($e->getMessage());
|
||||
}
|
||||
return array_key_exists($type, $official) ? $official[$type] : [];
|
||||
}
|
||||
|
||||
public static function getSettings($core)
|
||||
public function get($type = null, $silent = false)
|
||||
{
|
||||
$res = $core->con->select(
|
||||
'SELECT setting_ns ' .
|
||||
'FROM ' . $core->prefix . 'setting ' .
|
||||
'WHERE blog_id IS NULL ' .
|
||||
"OR blog_id IS NOT NULL " .
|
||||
'GROUP BY setting_ns'
|
||||
);
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
while($res->fetch()) {
|
||||
$rs[$i]['key'] = $res->setting_ns;
|
||||
$rs[$i]['value'] = $core->con->select(
|
||||
'SELECT count(*) FROM ' . $core->prefix .'setting ' .
|
||||
"WHERE setting_ns = '" . $res->setting_ns ."' " .
|
||||
"AND (blog_id IS NULL OR blog_id IS NOT NULL) " .
|
||||
"GROUP BY setting_ns "
|
||||
)->f(0);
|
||||
$i++;
|
||||
if (null === $type) {
|
||||
return $this->cleaners;
|
||||
}
|
||||
return $rs;
|
||||
if (isset($this->cleaners[$type])) {
|
||||
return $this->cleaners[$type];
|
||||
}
|
||||
if ($silent) {
|
||||
return false;
|
||||
}
|
||||
throw new exception(sprintf(__('unknow cleaner type %s'), $type));
|
||||
}
|
||||
|
||||
protected static function deleteGlobalSettings($core, $entry)
|
||||
public function set($type, $action, $ns)
|
||||
{
|
||||
$core->con->execute(
|
||||
'DELETE FROM ' .$core->prefix . 'setting ' .
|
||||
'WHERE blog_id IS NULL ' .
|
||||
"AND setting_ns = '" . $core->con->escape($entry) . "' "
|
||||
);
|
||||
if (!isset($this->cleaners[$type])) {
|
||||
throw new exception(sprintf(__('unknow cleaner type %s'), $type));
|
||||
}
|
||||
|
||||
protected static function deleteLocalSettings($core, $entry)
|
||||
{
|
||||
$core->con->execute(
|
||||
'DELETE FROM ' . $core->prefix . 'setting ' .
|
||||
"WHERE blog_id = '" . $core->con->escape($core->blog->id) . "' " .
|
||||
"AND setting_ns = '" . $core->con->escape($entry) . "' "
|
||||
);
|
||||
}
|
||||
|
||||
protected static function deleteAllSettings($core, $entry)
|
||||
{
|
||||
$core->con->execute(
|
||||
'DELETE FROM ' .$core->prefix . 'setting ' .
|
||||
"WHERE setting_ns = '" . $core->con->escape($entry) . "' " .
|
||||
"AND (blog_id IS NULL OR blog_id != '') "
|
||||
);
|
||||
}
|
||||
|
||||
public static function getTables($core)
|
||||
{
|
||||
$object = dbSchema::init($core->con);
|
||||
$res = $object->getTables();
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
foreach($res as $k => $v) {
|
||||
if ('' != $core->prefix) {
|
||||
if (!preg_match('/^' . preg_quote($core->prefix) . '(.*?)$/', $v, $m)) {
|
||||
continue;
|
||||
}
|
||||
$v = $m[1];
|
||||
}
|
||||
$rs[$i]['key'] = $v;
|
||||
$rs[$i]['value'] = $core->con->select('SELECT count(*) FROM ' . $res[$k])->f(0);
|
||||
$i++;
|
||||
}
|
||||
return $rs;
|
||||
}
|
||||
|
||||
protected static function emptyTable($core, $entry)
|
||||
{
|
||||
$core->con->execute(
|
||||
'DELETE FROM ' . $core->con->escapeSystem($core->prefix . $entry)
|
||||
);
|
||||
}
|
||||
|
||||
protected static function deleteTable($core, $entry)
|
||||
{
|
||||
self::emptyTable($core,$entry);
|
||||
|
||||
$core->con->execute(
|
||||
'DROP TABLE ' . $core->con->escapeSystem($core->prefix . $entry)
|
||||
);
|
||||
}
|
||||
|
||||
public static function getVersions($core)
|
||||
{
|
||||
$res = $core->con->select('SELECT * FROM ' . $core->prefix . 'version');
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
while ($res->fetch()) {
|
||||
$rs[$i]['key'] = $res->module;
|
||||
$rs[$i]['value'] = $res->version;
|
||||
$i++;
|
||||
}
|
||||
return $rs;
|
||||
}
|
||||
|
||||
protected static function deleteVersion($core, $entry)
|
||||
{
|
||||
$core->con->execute(
|
||||
'DELETE FROM '. $core->prefix . 'version ' .
|
||||
"WHERE module = '" . $core->con->escape($entry) . "' "
|
||||
);
|
||||
}
|
||||
|
||||
public static function getPlugins($core)
|
||||
{
|
||||
$res = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
|
||||
return self::getDirs($res);
|
||||
}
|
||||
|
||||
protected static function emptyPlugin($core, $entry)
|
||||
{
|
||||
$res = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
|
||||
self::delDir($res, $entry, false);
|
||||
}
|
||||
|
||||
protected static function deletePlugin($core, $entry)
|
||||
{
|
||||
$res = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
|
||||
self::delDir($res, $entry, true);
|
||||
}
|
||||
|
||||
public static function getThemes($core)
|
||||
{
|
||||
return self::getDirs($core->blog->themes_path);
|
||||
}
|
||||
|
||||
protected static function emptyTheme($core, $entry)
|
||||
{
|
||||
self::delDir($core->blog->themes_path, $entry, false);
|
||||
}
|
||||
|
||||
protected static function deleteTheme($core, $entry)
|
||||
{
|
||||
self::delDir($core->blog->themes_path, $entry, true);
|
||||
}
|
||||
|
||||
public static function getCaches($core)
|
||||
{
|
||||
return self::getDirs(DC_TPL_CACHE);
|
||||
}
|
||||
|
||||
protected static function emptyCache($core, $entry)
|
||||
{
|
||||
self::delDir(DC_TPL_CACHE, $entry, false);
|
||||
}
|
||||
|
||||
protected static function deleteCache($core, $entry)
|
||||
{
|
||||
self::delDir(DC_TPL_CACHE, $entry, true);
|
||||
}
|
||||
|
||||
public static function execute($core, $type, $action, $ns)
|
||||
{
|
||||
if (strtolower($ns) == 'dcadvancedcleaner') {
|
||||
throw new exception("dcAdvancedCleaner can't remove itself");
|
||||
throw new exception(__("dcAdvancedCleaner can't remove itself"));
|
||||
}
|
||||
|
||||
# BEHAVIOR dcAdvancedCleanerBeforeAction
|
||||
$core->callBehavior('dcAdvancedCleanerBeforeAction', $type, $action, $ns);
|
||||
$this->core->callBehavior('dcAdvancedCleanerBeforeAction', $type, $action, $ns);
|
||||
|
||||
try {
|
||||
# Delete global settings
|
||||
if ($type == 'settings' && $action == 'delete_global') {
|
||||
self::deleteGlobalSettings($core, $ns);
|
||||
}
|
||||
# Delete local settings
|
||||
if ($type == 'settings' && $action == 'delete_local') {
|
||||
self::deleteLocalSettings($core, $ns);
|
||||
}
|
||||
# Delete all settings
|
||||
if ($type == 'settings' && $action == 'delete_all') {
|
||||
self::deleteAllSettings($core, $ns);
|
||||
}
|
||||
# Empty tables
|
||||
if ($type == 'tables' && $action == 'empty') {
|
||||
self::emptyTable($core, $ns);
|
||||
}
|
||||
# Delete tables
|
||||
if ($type == 'tables' && $action == 'delete') {
|
||||
self::deleteTable($core, $ns);
|
||||
}
|
||||
# Delete versions
|
||||
if ($type == 'versions' && $action == 'delete') {
|
||||
self::deleteVersion($core, $ns);
|
||||
}
|
||||
# Empty plugins
|
||||
if ($type == 'plugins' && $action == 'empty') {
|
||||
self::emptyPlugin($core, $ns);
|
||||
}
|
||||
# Delete plugins
|
||||
if ($type == 'plugins' && $action == 'delete') {
|
||||
self::deletePlugin($core, $ns);
|
||||
}
|
||||
# Empty themes
|
||||
if ($type == 'themes' && $action == 'empty') {
|
||||
self::emptyTheme($core, $ns);
|
||||
}
|
||||
# Delete themes
|
||||
if ($type == 'themes' && $action == 'delete') {
|
||||
self::deleteTheme($core, $ns);
|
||||
}
|
||||
# Empty caches
|
||||
if ($type == 'caches' && $action == 'empty') {
|
||||
self::emptyCache($core, $ns);
|
||||
}
|
||||
# Delete caches
|
||||
if ($type == 'caches' && $action == 'delete') {
|
||||
self::deleteCache($core, $ns);
|
||||
}
|
||||
return true;
|
||||
} catch(Exception $e) {
|
||||
$errors = self::$errors;
|
||||
if (isset($errors[$type][$action])) {
|
||||
throw new Exception(__($errors[$type][$action]));
|
||||
}
|
||||
else {
|
||||
throw new Exception(sprintf(__('Cannot execute "%s" of type "%s"'), $action, $type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$ret = $this->cleaners[$type]->set($action, $ns);
|
||||
|
||||
if ($ret === false) {
|
||||
$msg = $this->cleaners[$type]->error($action);
|
||||
throw new Exception($msg ?? __('Unknow error'));
|
||||
}
|
||||
|
||||
protected static function getDirs($roots)
|
||||
{
|
||||
if (!is_array($roots)) {
|
||||
$roots = [$roots];
|
||||
}
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
foreach ($roots as $root) {
|
||||
$dirs = files::scanDir($root);
|
||||
foreach($dirs as $k) {
|
||||
if ('.' == $k || '..' == $k || !is_dir($root .'/' . $k)) {
|
||||
continue;
|
||||
}
|
||||
$rs[$i]['key'] = $k;
|
||||
$rs[$i]['value'] = count(self::scanDir($root . '/' . $k));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $rs;
|
||||
}
|
||||
|
||||
protected static function delDir($roots, $folder, $delfolder = true)
|
||||
{
|
||||
if (strpos($folder,'/')) {
|
||||
return false;
|
||||
}
|
||||
if (!is_array($roots)) {
|
||||
$roots = [$roots];
|
||||
}
|
||||
foreach ($roots as $root) {
|
||||
if (file_exists($root . '/' . $folder))
|
||||
return self::delTree($root . '/' . $folder, $delfolder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function scanDir($path, $dir = '', $res = [])
|
||||
{
|
||||
$exclude = self::$exclude;
|
||||
|
||||
$path = path::real($path);
|
||||
if (!is_dir($path) || !is_readable($path)) {
|
||||
return [];
|
||||
}
|
||||
$files = files::scandir($path);
|
||||
|
||||
foreach($files AS $file) {
|
||||
if (in_array($file, $exclude)) {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($path . '/' . $file)) {
|
||||
$res[] = $file;
|
||||
$res = self::scanDir($path . '/' . $file, $dir . '/' . $file, $res);
|
||||
} else {
|
||||
$res[] = empty($dir) ? $file : $dir . '/' . $file;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected static function delTree($dir, $delroot = true)
|
||||
{
|
||||
if (!is_dir($dir) || !is_readable($dir)) {
|
||||
return false;
|
||||
}
|
||||
if (substr($dir,-1) != '/') {
|
||||
$dir .= '/';
|
||||
}
|
||||
if (($d = @dir($dir)) === false) {
|
||||
return false;
|
||||
}
|
||||
while (($entryname = $d->read()) !== false) {
|
||||
if ($entryname != '.' && $entryname != '..') {
|
||||
if (is_dir($dir . '/' . $entryname)) {
|
||||
if (!self::delTree($dir . '/' . $entryname)) {return false;
|
||||
}
|
||||
} else {
|
||||
if (!@unlink($dir . '/' . $entryname)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$d->close();
|
||||
|
||||
if ($delroot) {
|
||||
return @rmdir($dir);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,88 +15,74 @@ if (!defined('DC_ADMIN_CONTEXT')) {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Modules uninstall features handler
|
||||
# Localized l10n
|
||||
__('delete table');
|
||||
__('delete cache files');
|
||||
__('delete plugin files');
|
||||
__('delete theme files');
|
||||
__('delete the version number');
|
||||
__('Uninstall extensions');
|
||||
__('delete %s blog settings');
|
||||
__('delete %s global settings');
|
||||
__('delete all %s settings');
|
||||
__('delete %s table');
|
||||
__('delete %s version number');
|
||||
__('delete %s plugin files');
|
||||
__('delete %s theme file');
|
||||
__('delete %s cache files');
|
||||
|
||||
Provides an object to handle modules uninstall features
|
||||
(themes or plugins).
|
||||
This class used dcAdvancedCleaner.
|
||||
/**
|
||||
* @brief Modules uninstall features handler
|
||||
*
|
||||
* Provides an object to handle modules uninstall features
|
||||
* (themes or plugins).
|
||||
* This class used dcAdvancedCleaner.
|
||||
*/
|
||||
class dcUninstaller
|
||||
{
|
||||
protected $path;
|
||||
|
||||
protected $modules = []; ///< <b>array</b> Modules informations array
|
||||
protected $modules = [];
|
||||
protected $actions = ['user' => [], 'callback' => []];
|
||||
protected $callbacks = ['user' => [], 'callback' => []];
|
||||
|
||||
protected $id = null;
|
||||
protected $mroot = null;
|
||||
|
||||
/**
|
||||
Array of all allowed properties to uninstall parts of modules.
|
||||
'settings' : settings set on dcSettings,
|
||||
'tables' : if module creates table,
|
||||
'plugins' : if module has files on plugin path,
|
||||
'themes' : if module has files on theme path, (on current blog)
|
||||
'caches' : if module has files on DC caches path,
|
||||
'versions' : if module set a versions on DC table 'version'
|
||||
*/
|
||||
protected static $allowed_properties = [
|
||||
'versions' => [
|
||||
'delete' => 'delete version in dc'
|
||||
],
|
||||
'settings' => [
|
||||
'delete_global' => 'delete global settings',
|
||||
'delete_local' => 'delete local settings',
|
||||
'delete_all' => 'delete all settings'
|
||||
],
|
||||
'tables' => [
|
||||
'empty' => 'empty table',
|
||||
'delete' => 'delete table'
|
||||
],
|
||||
'plugins' => [
|
||||
'empty' => 'empty plugin folder',
|
||||
'delete' => 'delete plugin folder'
|
||||
],
|
||||
'themes' => [
|
||||
'empty' => 'empty theme folder',
|
||||
'delete' => 'delete theme folder'
|
||||
],
|
||||
'caches' => [
|
||||
'empty' => 'empty cache folder',
|
||||
'delete' => 'delete cache folder'
|
||||
]
|
||||
];
|
||||
|
||||
protected static $priority_properties = [
|
||||
'versions','settings','tables','themes','plugins','caches'
|
||||
];
|
||||
|
||||
public $core; ///< <b>dcCore</b> dcCore instance
|
||||
public $core;
|
||||
private $ac;
|
||||
private $allowed_actions = null;
|
||||
|
||||
/**
|
||||
Object constructor.
|
||||
|
||||
@param core <b>dcCore</b> dcCore instance
|
||||
* Object constructor.
|
||||
*
|
||||
* @param dcCore $core dcCore instance
|
||||
*/
|
||||
public function __construct(dcCore $core)
|
||||
{
|
||||
$this->core =& $core;
|
||||
$this->ac = new dcAdvancedCleaner($core);
|
||||
|
||||
$res = [];
|
||||
foreach($this->ac->get() as $cleaner) {
|
||||
$res[$cleaner->id] = $cleaner->getActions();
|
||||
}
|
||||
$this->allowed_actions = $res;
|
||||
}
|
||||
|
||||
public static function getAllowedProperties()
|
||||
public function getAllowedActions()
|
||||
{
|
||||
return self::$allowed_properties;
|
||||
return $this->allowed_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
Loads modules.
|
||||
Files _defines.php and _uninstall.php must be present on module
|
||||
to be recognized.
|
||||
(path separator depends on your OS).
|
||||
|
||||
@param path <b>string</b> Separated list of paths
|
||||
* Loads modules.
|
||||
*
|
||||
* Files _defines.php and _uninstall.php must be present on module
|
||||
* to be recognized.
|
||||
* (path separator depends on your OS).
|
||||
*
|
||||
* @param string $path Separated list of paths
|
||||
*/
|
||||
public function loadModules($path)
|
||||
{
|
||||
|
@ -127,11 +113,12 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Load one module.
|
||||
Files _defines.php and _uninstall.php must be present on module
|
||||
to be recognized.
|
||||
|
||||
@param root <b>string</b> path of module
|
||||
* Load one module.
|
||||
*
|
||||
* Files _defines.php and _uninstall.php must be present on module
|
||||
* to be recognized.
|
||||
*
|
||||
* @param string $root path of module
|
||||
*/
|
||||
public function loadModule($root)
|
||||
{
|
||||
|
@ -150,12 +137,12 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
This method registers a module in modules list.
|
||||
|
||||
@param name <b>string</b> Module name
|
||||
@param desc <b>string</b> Module description
|
||||
@param author <b>string</b> Module author name
|
||||
@param version <b>string</b> Module version
|
||||
* This method registers a module in modules list.
|
||||
*
|
||||
* @param string $name Module name
|
||||
* @param string $desc Module description
|
||||
* @param string $author Module author name
|
||||
* @param string $version Module version
|
||||
*/
|
||||
public function registerModule($name, $desc, $author, $version, $properties = [])
|
||||
{
|
||||
|
@ -172,11 +159,12 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Returns all modules associative array or only one module if <var>$id</var>
|
||||
is present.
|
||||
|
||||
@param id <b>string</b> Optionnal module ID
|
||||
@return <b>array</b>
|
||||
* Returns all modules associative array or only one module if <var>$id</var>
|
||||
* is present.
|
||||
*
|
||||
* @param string $id Optionnal module ID
|
||||
*
|
||||
* @return array Modules
|
||||
*/
|
||||
public function getModules($id = null)
|
||||
{
|
||||
|
@ -187,10 +175,11 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Returns true if the module with ID <var>$id</var> exists.
|
||||
|
||||
@param id <b>string</b> Module ID
|
||||
@return <b>boolean</b>
|
||||
* Returns true if the module with ID <var>$id</var> exists.
|
||||
*
|
||||
* @param string $idModule ID
|
||||
*
|
||||
* @return boolean Success
|
||||
*/
|
||||
public function moduleExists($id)
|
||||
{
|
||||
|
@ -198,13 +187,14 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Add a predefined action to unsintall features.
|
||||
This action is set in _uninstall.php.
|
||||
|
||||
@param type <b>string</b> Type of action (from $allowed_properties)
|
||||
@param action <b>string</b> Action (from $allowed_properties)
|
||||
@param ns <b>string</b> Name of setting related to module.
|
||||
@param desc <b>string</b> Description of action
|
||||
* Add a predefined action to unsintall features.
|
||||
*
|
||||
* This action is set in _uninstall.php.
|
||||
*
|
||||
* @param string $type Type of action (from $allowed_actions)
|
||||
* @param string $action Action (from $allowed_actions)
|
||||
* @param string $ns Name of setting related to module.
|
||||
* @param string $desc Description of action
|
||||
*/
|
||||
protected function addUserAction($type, $action, $ns, $desc = '')
|
||||
{
|
||||
|
@ -226,7 +216,7 @@ class dcUninstaller
|
|||
if (empty($type) || empty($ns)) {
|
||||
return null;
|
||||
}
|
||||
if (!isset(self::$allowed_properties[$type][$action])) {
|
||||
if (!isset($this->allowed_actions[$type][$action])) {
|
||||
return null;
|
||||
}
|
||||
if (empty($desc)) {
|
||||
|
@ -240,11 +230,10 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Returns modules <var>$id</var> predefined actions associative array
|
||||
ordered by priority
|
||||
|
||||
@param id <b>string</b> Optionnal module ID
|
||||
@return <b>array</b>
|
||||
* Returns modules <var>$id</var> predefined actions associative array
|
||||
*
|
||||
* @param string $id Optionnal module ID
|
||||
* @return array Modules id
|
||||
*/
|
||||
public function getUserActions($id)
|
||||
{
|
||||
|
@ -264,21 +253,22 @@ class dcUninstaller
|
|||
return [];
|
||||
}
|
||||
$res = [];
|
||||
foreach(self::$priority_properties as $k => $v) {
|
||||
if (!isset($this->actions[$group][$id][$v])) {
|
||||
foreach($this->allowed_actions as $k => $v) {
|
||||
if (!isset($this->actions[$group][$id][$k])) {
|
||||
continue;
|
||||
}
|
||||
$res[$v] = $this->actions[$group][$id][$v];
|
||||
$res[$k] = $this->actions[$group][$id][$k];
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
Add a callable function for unsintall features.
|
||||
This action is set in _uninstall.php.
|
||||
|
||||
@param func <b>string</b> Callable function
|
||||
@param desc <b>string</b> Description of action
|
||||
* Add a callable function for unsintall features.
|
||||
*
|
||||
* This action is set in _uninstall.php.
|
||||
*
|
||||
* @param string $func Callable function
|
||||
* @param string $desc Description of action
|
||||
*/
|
||||
protected function addUserCallback($func, $desc= '')
|
||||
{
|
||||
|
@ -310,10 +300,11 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Returns modules <var>$id</var> callback actions associative array
|
||||
* Returns modules <var>$id</var> callback actions associative array
|
||||
|
||||
@param id <b>string</b> Optionnal module ID
|
||||
@return <b>array</b>
|
||||
* @param string $id Optionnal module ID
|
||||
*
|
||||
* @return array Modules id
|
||||
*/
|
||||
public function getUserCallbacks($id)
|
||||
{
|
||||
|
@ -336,22 +327,24 @@ class dcUninstaller
|
|||
}
|
||||
|
||||
/**
|
||||
Execute a predifined action. This function call dcAdvancedCleaner
|
||||
to do actions.
|
||||
|
||||
@param type <b>string</b> Type of action (from $allowed_properties)
|
||||
@param action <b>string</b> Action (from $allowed_properties)
|
||||
@param ns <b>string</b> Name of setting related to module.
|
||||
@return <b>array</b>
|
||||
* Execute a predifined action.
|
||||
*
|
||||
* This function call dcAdvancedCleaner to do actions.
|
||||
*
|
||||
* @param string $type Type of action (from $allowed_actions)
|
||||
* @param string $action Action (from $allowed_actions)
|
||||
* @param string $ns Name of setting related to module.
|
||||
*
|
||||
* @return boolean Success
|
||||
*/
|
||||
public function execute($type, $action, $ns)
|
||||
{
|
||||
$prop = $this->getAllowedProperties();
|
||||
|
||||
if (!isset($prop[$type][$action]) || empty($ns)) {
|
||||
return null;
|
||||
if (!isset($this->allowed_actions[$type][$action]) || empty($ns)) {
|
||||
return false;
|
||||
}
|
||||
dcAdvancedCleaner::execute($this->core, $type, $action, $ns);
|
||||
$this->ac->set($type, $action, $ns);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function sortModules($a, $b)
|
||||
|
|
515
inc/lib.advanced.cleaner.php
Normal file
515
inc/lib.advanced.cleaner.php
Normal file
|
@ -0,0 +1,515 @@
|
|||
<?php
|
||||
/**
|
||||
* @brief dcAdvancedCleaner, 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
|
||||
*/
|
||||
|
||||
class advancedCleanerSettings extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'settings',
|
||||
'name' => __('Settings'),
|
||||
'desc' => __('Namespaces registered in dcSettings')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete_global' => __('delete global settings'),
|
||||
'delete_local' => __('delete blog settings'),
|
||||
'delete_all' => __('delete all settings')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'delete_global') {
|
||||
return __('Failed to delete global settings');
|
||||
}
|
||||
if ($action == 'delete_local') {
|
||||
return __('Failed to delete local settings');
|
||||
}
|
||||
if ($action == 'delete_all') {
|
||||
return __('Failed to delete all settings');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return [
|
||||
'akismet',
|
||||
'antispam',
|
||||
'breadcrumb',
|
||||
'dcckeditor',
|
||||
'dclegacyeditor',
|
||||
'maintenance',
|
||||
'pages',
|
||||
'pings',
|
||||
'system',
|
||||
'themes',
|
||||
'widgets'
|
||||
];
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$res = $this->core->con->select(
|
||||
'SELECT setting_ns ' .
|
||||
'FROM ' . $this->core->prefix . 'setting ' .
|
||||
'WHERE blog_id IS NULL ' .
|
||||
"OR blog_id IS NOT NULL " .
|
||||
'GROUP BY setting_ns'
|
||||
);
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
while($res->fetch()) {
|
||||
$rs[$i]['key'] = $res->setting_ns;
|
||||
$rs[$i]['value'] = $this->core->con->select(
|
||||
'SELECT count(*) FROM ' . $this->core->prefix .'setting ' .
|
||||
"WHERE setting_ns = '" . $res->setting_ns ."' " .
|
||||
"AND (blog_id IS NULL OR blog_id IS NOT NULL) " .
|
||||
"GROUP BY setting_ns "
|
||||
)->f(0);
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $rs;
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'delete_global') {
|
||||
$this->core->con->execute(
|
||||
'DELETE FROM ' .$this->core->prefix . 'setting ' .
|
||||
'WHERE blog_id IS NULL ' .
|
||||
"AND setting_ns = '" . $this->core->con->escape($ns) . "' "
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete_local') {
|
||||
$this->core->con->execute(
|
||||
'DELETE FROM ' . $this->core->prefix . 'setting ' .
|
||||
"WHERE blog_id = '" . $this->core->con->escape($this->core->blog->id) . "' " .
|
||||
"AND setting_ns = '" . $this->core->con->escape($ns) . "' "
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete_all') {
|
||||
$this->core->con->execute(
|
||||
'DELETE FROM ' .$this->core->prefix . 'setting ' .
|
||||
"WHERE setting_ns = '" . $this->core->con->escape($ns) . "' " .
|
||||
"AND (blog_id IS NULL OR blog_id != '') "
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerTables extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'tables',
|
||||
'name' => __('Tables'),
|
||||
'desc' => __('All database tables of Dotclear')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete'),
|
||||
'empty' => __('empty')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
return __('Failed to empty table');
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete table');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return [
|
||||
'blog',
|
||||
'category',
|
||||
'comment',
|
||||
'link',
|
||||
'log',
|
||||
'media',
|
||||
'meta',
|
||||
'permissions',
|
||||
'ping',
|
||||
'post',
|
||||
'post_media',
|
||||
'pref',
|
||||
'session',
|
||||
'setting',
|
||||
'spamrule',
|
||||
'user',
|
||||
'version'
|
||||
];
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$object = dbSchema::init($this->core->con);
|
||||
$res = $object->getTables();
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
foreach($res as $k => $v) {
|
||||
if ('' != $this->core->prefix) {
|
||||
if (!preg_match('/^' . preg_quote($this->core->prefix) . '(.*?)$/', $v, $m)) {
|
||||
continue;
|
||||
}
|
||||
$v = $m[1];
|
||||
}
|
||||
$rs[$i]['key'] = $v;
|
||||
$rs[$i]['value'] = $this->core->con->select('SELECT count(*) FROM ' . $res[$k])->f(0);
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $rs;
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if (in_array($action, ['empty', 'delete'])) {
|
||||
$this->core->con->execute(
|
||||
'DELETE FROM ' . $this->core->con->escapeSystem($this->core->prefix . $ns)
|
||||
);
|
||||
}
|
||||
if ($action == 'empty') {
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
$this->core->con->execute(
|
||||
'DROP TABLE ' . $this->core->con->escapeSystem($this->core->prefix . $ns)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerVersions extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'versions',
|
||||
'name' => __('Versions'),
|
||||
'desc' => __('Versions registered in table "version" of Dotclear')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete version');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return [
|
||||
'antispam',
|
||||
'blogroll',
|
||||
'blowupConfig',
|
||||
'core',
|
||||
'dcCKEditor',
|
||||
'dcLegacyEditor',
|
||||
'pages',
|
||||
'pings',
|
||||
'simpleMenu',
|
||||
'tags',
|
||||
'widgets'
|
||||
];
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$res = $this->core->con->select('SELECT * FROM ' . $this->core->prefix . 'version');
|
||||
|
||||
$rs = [];
|
||||
$i = 0;
|
||||
while ($res->fetch()) {
|
||||
$rs[$i]['key'] = $res->module;
|
||||
$rs[$i]['value'] = $res->version;
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $rs;
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'delete') {
|
||||
$this->core->con->execute(
|
||||
'DELETE FROM '. $this->core->prefix . 'version ' .
|
||||
"WHERE module = '" . $this->core->con->escape($ns) . "' "
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerPlugins extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'plugins',
|
||||
'name' => __('Plugins'),
|
||||
'desc' => __('Folders from plugins directories')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete'),
|
||||
'empty' => __('empty')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
return __('Failed to empty plugin folder');
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete plugin folder');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return explode(',', DC_DISTRIB_PLUGINS);
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$res = self::getDirs(explode(PATH_SEPARATOR, DC_PLUGINS_ROOT));
|
||||
sort($res);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
$res = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
|
||||
self::delDir($res, $ns, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
$res = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
|
||||
self::delDir($res, $ns, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerThemes extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'themes',
|
||||
'name' => __('Themes'),
|
||||
'desc' => __('Folders from blog themes directory')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete'),
|
||||
'empty' => __('empty')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
return __('Failed to empty themes folder');
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete themes folder');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return explode(',', DC_DISTRIB_THEMES);
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$res = self::getDirs($this->core->blog->themes_path);
|
||||
sort($res);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
self::delDir($this->core->blog->themes_path, $ns, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
self::delDir($this->core->blog->themes_path, $ns, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerCaches extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'caches',
|
||||
'name' => __('Cache'),
|
||||
'desc' => __('Folders from cache directory')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete'),
|
||||
'empty' => __('empty')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
return __('Failed to empty cache folder');
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete cache folder');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return ['cbfeed', 'cbtpl', 'dcrepo', 'versions'];
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
return self::getDirs(DC_TPL_CACHE);
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'empty') {
|
||||
self::delDir(DC_TPL_CACHE, $ns, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($action == 'delete') {
|
||||
self::delDir(DC_TPL_CACHE, $ns, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class advancedCleanerVars extends advancedCleaner
|
||||
{
|
||||
protected function init(): bool
|
||||
{
|
||||
$this->setProperties([
|
||||
'id' => 'vars',
|
||||
'name' => __('Var'),
|
||||
'desc' => __('Folders from Dotclear VAR directory')
|
||||
]);
|
||||
|
||||
$this->setActions([
|
||||
'delete' => __('delete')
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function error($action): string
|
||||
{
|
||||
if ($action == 'delete') {
|
||||
return __('Failed to delete var folder');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function official(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
return self::getDirs(DC_VAR);
|
||||
}
|
||||
|
||||
public function set($action, $ns): bool
|
||||
{
|
||||
if ($action == 'delete') {
|
||||
self::delDir(DC_VAR, $ns, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -19,9 +19,7 @@ class dcAdvancedCleanerActivityReportBehaviors
|
|||
{
|
||||
public static function maintenance($type, $action, $ns)
|
||||
{
|
||||
$logs = array($type ,$action, $ns);
|
||||
|
||||
$GLOBALS['core']->activityReport->addLog('dcadvancedcleaner', 'maintenance', $logs);
|
||||
$GLOBALS['core']->activityReport->addLog('dcadvancedcleaner', 'maintenance', [$type ,$action, $ns]);
|
||||
}
|
||||
|
||||
public static function add($core)
|
||||
|
|
|
@ -17,6 +17,17 @@ if (!defined('DC_ADMIN_CONTEXT')) {
|
|||
|
||||
class behaviorsDcAdvancedCleaner
|
||||
{
|
||||
public static function adminDashboardFavorites(dcCore $core, $favs)
|
||||
{
|
||||
$favs->register('dcAdvancedCleaner', [
|
||||
'title' => __('Advanced cleaner'),
|
||||
'url' => $core->adminurl->get('admin.plugin.dcAdvancedCleaner'),
|
||||
'small-icon' => dcPage::getPF('dcAdvancedCleaner/icon.png'),
|
||||
'large-icon' => dcPage::getPF('dcAdvancedCleaner/icon-big.png'),
|
||||
'permissions' => $core->auth->isSuperAdmin()
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pluginsBeforeDelete($plugin)
|
||||
{
|
||||
self::moduleBeforeDelete($plugin, 'plugins.php?removed=1');
|
||||
|
@ -62,7 +73,7 @@ class behaviorsDcAdvancedCleaner
|
|||
|
||||
public static function pluginsToolsTabs($core)
|
||||
{
|
||||
self::modulesTabs($core, DC_PLUGINS_ROOT, $core->adminurl->get('admin.plugins', ['tab' => 'uninstaller']));
|
||||
self::modulesTabs($core, DC_PLUGINS_ROOT, $core->adminurl->get('admin.plugins') . '#uninstaller');
|
||||
}
|
||||
|
||||
public static function modulesTabs($core, $path, $redir, $title = '')
|
||||
|
@ -70,61 +81,15 @@ class behaviorsDcAdvancedCleaner
|
|||
if (!$core->blog->settings->dcAdvancedCleaner->dcAdvancedCleaner_behavior_active) {
|
||||
return null;
|
||||
}
|
||||
$err = '';
|
||||
$title = empty($title) ? __('Advanced uninstall') : $title;
|
||||
|
||||
$uninstaller = new dcUninstaller($core);
|
||||
$uninstaller->loadModules($path);
|
||||
$modules = $uninstaller->getModules();
|
||||
$props = $uninstaller->getAllowedProperties();
|
||||
|
||||
// Execute
|
||||
if (isset($_POST['action']) && $_POST['action'] == 'uninstall'
|
||||
&& (!empty($_POST['extras']) || !empty($_POST['actions']))
|
||||
) {
|
||||
try {
|
||||
// Extras
|
||||
if (!empty($_POST['extras'])) {
|
||||
foreach($_POST['extras'] as $module_id => $extras) {
|
||||
foreach($extras as $k => $sentence) {
|
||||
$extra = @unserialize(@base64_decode($sentence));
|
||||
|
||||
if (!$extra || !is_callable($extra)) {
|
||||
continue;
|
||||
}
|
||||
call_user_func($extra, $modul_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Actions
|
||||
if (!empty($_POST['actions'])) {
|
||||
foreach($_POST['actions'] as $module_id => $actions) {
|
||||
foreach($actions as $k => $sentence) {
|
||||
$action = @unserialize(@base64_decode($sentence));
|
||||
|
||||
if (!$action
|
||||
|| !isset($action['type'])
|
||||
|| !isset($action['action'])
|
||||
|| !isset($action['ns'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$uninstaller->execute($action['type'], $action['action'], $action['ns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
dcPage::addSuccessNotice(__('Action successfuly excecuted'));
|
||||
http::redirect($redir);
|
||||
} catch(Exception $e) {
|
||||
$err = $e->getMessage();
|
||||
}
|
||||
}
|
||||
$props = $uninstaller->getAllowedActions();
|
||||
|
||||
echo '<div class="multi-part" id="uninstaller" title="' . __($title) . '"><h3>' . __($title) . '</h3>';
|
||||
|
||||
if($err) {
|
||||
echo '<p class="error">' . $err . '</p>';
|
||||
}
|
||||
if(!count($modules)) {
|
||||
echo '<p>' . __('There is no module with uninstall features') . '</p></div>';
|
||||
return null;
|
||||
|
@ -204,6 +169,7 @@ class behaviorsDcAdvancedCleaner
|
|||
'</table>' .
|
||||
'<p>' .
|
||||
$core->formNonce() .
|
||||
form::hidden(['path'], $path) .
|
||||
form::hidden(['redir'], $redir) .
|
||||
form::hidden(['action'], 'uninstall') .
|
||||
'<input type="submit" name="submit" value="' . __('Perform selected actions') . '" /> ' .
|
||||
|
@ -212,4 +178,59 @@ class behaviorsDcAdvancedCleaner
|
|||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
public static function adminModulesListDoActions($list, $modules, $type)
|
||||
{
|
||||
if (!$list->core->blog->settings->dcAdvancedCleaner->dcAdvancedCleaner_behavior_active) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($_POST['action']) || $_POST['action'] != 'uninstall'
|
||||
|| (empty($_POST['extras']) && empty($_POST['actions']))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$uninstaller = new dcUninstaller($list->core);
|
||||
$uninstaller->loadModules($_POST['path']);
|
||||
$modules = $uninstaller->getModules();
|
||||
$props = $uninstaller->getAllowedActions();
|
||||
|
||||
try {
|
||||
// Extras
|
||||
if (!empty($_POST['extras'])) {
|
||||
foreach($_POST['extras'] as $module_id => $extras) {
|
||||
foreach($extras as $k => $sentence) {
|
||||
$extra = @unserialize(@base64_decode($sentence));
|
||||
|
||||
if (!$extra || !is_callable($extra)) {
|
||||
continue;
|
||||
}
|
||||
call_user_func($extra, $modul_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Actions
|
||||
if (!empty($_POST['actions'])) {
|
||||
foreach($_POST['actions'] as $module_id => $actions) {
|
||||
foreach($actions as $k => $sentence) {
|
||||
$action = @unserialize(@base64_decode($sentence));
|
||||
|
||||
if (!$action
|
||||
|| !isset($action['type'])
|
||||
|| !isset($action['action'])
|
||||
|| !isset($action['ns'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$uninstaller->execute($action['type'], $action['action'], $action['ns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
dcPage::addSuccessNotice(__('Action successfuly excecuted'));
|
||||
http::redirect($_POST['redir']);
|
||||
} catch(Exception $e) {
|
||||
$list->core->error->add($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
137
index.php
137
index.php
|
@ -17,106 +17,32 @@ if (!defined('DC_CONTEXT_ADMIN')) {
|
|||
|
||||
dcPage::checkSuper();
|
||||
|
||||
# Localized l10n
|
||||
__('Settings'); __('settings'); __('setting');
|
||||
__('Tables'); __('tables'); __('table');
|
||||
__('Plugins'); __('plugins'); __('plugin');
|
||||
__('Themes'); __('themes'); __('theme');
|
||||
__('Caches'); __('caches'); __('cache');
|
||||
__('Versions'); __('versions'); __('version');
|
||||
__('delete table');
|
||||
__('delete cache files');
|
||||
__('delete plugin files');
|
||||
__('delete theme files');
|
||||
__('delete the version number');
|
||||
__('Uninstall extensions');
|
||||
__('delete %s blog settings');
|
||||
__('delete %s global settings');
|
||||
__('delete all %s settings');
|
||||
__('delete %s table');
|
||||
__('delete %s version number');
|
||||
__('delete %s plugin files');
|
||||
__('delete %s theme file');
|
||||
__('delete %s cache files');
|
||||
$ac = new dcAdvancedCleaner($core);
|
||||
|
||||
# vars
|
||||
$part = $_REQUEST['part'] ?? 'caches';
|
||||
$entries = $_POST['entries'] ?? [];
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
# Combos
|
||||
$combo_title = [
|
||||
'settings' => __('Settings'),
|
||||
'tables' => __('Tables'),
|
||||
'plugins' => __('Extensions'),
|
||||
'themes' => __('Themes'),
|
||||
'caches' => __('Cache'),
|
||||
'versions' => __('Versions')
|
||||
];
|
||||
$combo_type = [
|
||||
'settings' => ['delete_global', 'delete_local', 'delete_all'],
|
||||
'tables' => ['empty', 'delete'],
|
||||
'plugins' => ['empty', 'delete'],
|
||||
'themes' => ['empty', 'delete'],
|
||||
'caches' => ['empty', 'delete'],
|
||||
'versions' => ['delete']
|
||||
];
|
||||
$combo_funcs = [
|
||||
'settings' => ['dcAdvancedCleaner', 'getSettings'],
|
||||
'tables' => ['dcAdvancedCleaner', 'getTables'],
|
||||
'plugins' => ['dcAdvancedCleaner', 'getPlugins'],
|
||||
'themes' => ['dcAdvancedCleaner', 'getThemes'],
|
||||
'caches' => ['dcAdvancedCleaner', 'getCaches'],
|
||||
'versions' => ['dcAdvancedCleaner', 'getVersions']
|
||||
];
|
||||
$combo_actions = [
|
||||
'settings' => [
|
||||
__('delete global settings') => 'delete_global',
|
||||
__('delete blog settings') => 'delete_local',
|
||||
__('delete all settings') =>'delete_all'
|
||||
],
|
||||
'tables' => [
|
||||
__('delete') => 'delete',
|
||||
__('empty') => 'empty'
|
||||
],
|
||||
'plugins' => [
|
||||
__('delete') => 'delete',
|
||||
__('empty') => 'empty'
|
||||
],
|
||||
'themes' => [
|
||||
__('delete') => 'delete',
|
||||
__('empty') => 'empty'
|
||||
],
|
||||
'caches' => [
|
||||
__('delete') => 'delete',
|
||||
__('empty') => 'empty'
|
||||
],
|
||||
'versions' => [
|
||||
__('delete') => 'delete'
|
||||
]
|
||||
];
|
||||
$combo_help = [
|
||||
'settings' => __('Namespaces registered in dcSettings'),
|
||||
'tables' => __('All database tables of Dotclear'),
|
||||
'plugins' => __('Folders from plugins directories'),
|
||||
'themes' => __('Folders from blog themes directory'),
|
||||
'caches' => __('Folders from cache directory'),
|
||||
'versions' => __('Versions registered in table "version" of Dotclear')
|
||||
];
|
||||
$cleaner = false;
|
||||
$select_menu = [];
|
||||
foreach($ac->get() as $k) {
|
||||
$select_menu[$k->name] = $k->id;
|
||||
if ($k->id == $_REQUEST['part']) {
|
||||
$cleaner = $k;
|
||||
}
|
||||
}
|
||||
if (!$cleaner) {
|
||||
if (!($cleaner = $ac->get('caches', true))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# Actions
|
||||
if (!empty($entries)
|
||||
&& isset($combo_type[$part])
|
||||
&& in_array($action, $combo_type[$part])
|
||||
) {
|
||||
if (!empty($_POST['entries']) && !empty($_POST['action'])) {
|
||||
try {
|
||||
foreach($entries as $v) {
|
||||
dcAdvancedCleaner::execute($core, $part, $action, $v);
|
||||
foreach($_POST['entries'] as $ns) {
|
||||
$ac->set($cleaner->id, $_POST['action'], $ns);
|
||||
}
|
||||
dcPage::addSuccessNotice(__('Action successfuly excecuted'));
|
||||
$core->adminurl->redirect(
|
||||
'admin.plugin.dcAdvancedCleaner',
|
||||
['part' => $part]
|
||||
['part' => $cleaner->id]
|
||||
);
|
||||
}
|
||||
catch(Exception $e) {
|
||||
|
@ -124,6 +50,7 @@ if (!empty($entries)
|
|||
}
|
||||
}
|
||||
|
||||
# Display
|
||||
echo '<html><head><title>' . __('Advanced cleaner') . '</title>' .
|
||||
dcPage::cssLoad(dcPage::getPF('dcAdvancedCleaner/style.css')) .
|
||||
dcPage::jsLoad(dcPage::getPF('dcAdvancedCleaner/js/index.js'));
|
||||
|
@ -136,35 +63,31 @@ dcPage::breadcrumb([
|
|||
__('Plugins') => '',
|
||||
__('Advanced cleaner') => ''
|
||||
]) .
|
||||
dcPage::notices();
|
||||
dcPage::notices() .
|
||||
|
||||
# select menu list
|
||||
echo
|
||||
'<form method="get" action="' . $core->adminurl->get('admin.plugin.dcAdvancedCleaner') . '" id="parts_menu">' .
|
||||
'<p class="anchor-nav"><label for="part" class="classic">' . __('Goto:') . ' </label>' .
|
||||
form::combo('part', array_flip($combo_title), $part) . ' ' .
|
||||
form::combo('part', $select_menu, $cleaner->id) . ' ' .
|
||||
'<input type="submit" value="' . __('Ok') . '" />' .
|
||||
form::hidden('p', 'dcAdvancedCleaner') . '</p>' .
|
||||
'</form>';
|
||||
'</form>' .
|
||||
|
||||
if (isset($combo_funcs[$part])) {
|
||||
echo '<h3>' . $combo_title[$part] . '</h3><p>' . $combo_help[$part] . '</p>';
|
||||
'<h3>' . $cleaner->name . '</h3><p>' . $cleaner->desc . '</p>';
|
||||
|
||||
$rs = call_user_func($combo_funcs[$part], $core);
|
||||
$rs = $cleaner->get();
|
||||
|
||||
if (empty($rs)) {
|
||||
echo '<p>' . sprintf(__('There is no %s'), __(substr($part, 0, -1))) . '</p>';
|
||||
echo '<p>' . __('There is nothing to display') . '</p>';
|
||||
} else {
|
||||
echo
|
||||
'<form method="post" action="' . $core->adminurl->get('admin.plugin.dcAdvancedCleaner') . '" id="form-funcs">' .
|
||||
'<div class="table-outer">' .
|
||||
'<table><caption>' . sprintf(__('There are %s %s'), count($rs), __($part)) . '</caption><thead><tr>' .
|
||||
'<table><caption>' . sprintf(__('There are %s %s'), count($rs), __($cleaner->id)) . '</caption><thead><tr>' .
|
||||
'<th colspan="2">' . __('Name') . '</th><th>' . __('Objects') . '</th>' .
|
||||
'</tr></thead><tbody>';
|
||||
|
||||
$official = dcAdvancedCleaner::getOfficial($part);
|
||||
foreach($rs as $k => $v) {
|
||||
$offline = in_array($v['key'], $official);
|
||||
$offline = in_array($v['key'], $cleaner->official());
|
||||
|
||||
if ($offline && $core->blog->settings->dcAdvancedCleaner->dcAdvancedCleaner_dcproperty_hide) {
|
||||
continue;
|
||||
|
@ -183,17 +106,17 @@ if (isset($combo_funcs[$part])) {
|
|||
echo
|
||||
'</tbody></table></div>' .
|
||||
'<p class="field">' . __('Action on selected rows:') . ' ' .
|
||||
form::combo(['action'], $combo_actions[$part]) .
|
||||
form::combo(['action'], array_flip($cleaner->getActions())) .
|
||||
'<input id="do-action" type="submit" value="' . __('ok') . '" />' .
|
||||
form::hidden(['p'], 'dcAdvancedCleaner') .
|
||||
form::hidden(['part'], $part) .
|
||||
form::hidden(['part'], $cleaner->id) .
|
||||
$core->formNonce() . '</p>' .
|
||||
'<p class="info">' .
|
||||
__('Beware: All actions done here are irreversible and are directly applied') .
|
||||
'</p>' .
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($core->blog->settings->dcAdvancedCleaner->dcAdvancedCleaner_dcproperty_hide) {
|
||||
echo '<p class="info">' .
|
||||
__('Default values of Dotclear are hidden. You can change this in settings') .
|
||||
|
|
Loading…
Reference in a new issue