Plugin Directory

Changeset 3257611


Ignore:
Timestamp:
03/18/2025 08:51:26 AM (13 months ago)
Author:
watchful
Message:

v2.0.3

Location:
watchful
Files:
14 added
20 edited
1 copied

Legend:

Unmodified
Added
Removed
  • watchful/tags/v2.0.3/lib/Controller/Backups.php

    r3123686 r3257611  
    2020use Watchful\Helpers\Authentification;
    2121use Watchful\Helpers\BackupPluginHelper;
     22use Watchful\Helpers\BackupPlugins\WatchfulBackupPlugin;
    2223use Watchful\Helpers\BackupPlugins\XClonerBackupPlugin;
    2324use WP_REST_Request;
     
    193194            )
    194195        );
     196        register_rest_route(
     197            'watchful/v1',
     198            '/backup/watchful',
     199            array(
     200                array(
     201                    'methods' => WP_REST_Server::CREATABLE,
     202                    'callback' => array($this, 'execute_watchful_backup'),
     203                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     204                    'args' => Authentification::get_arguments(),
     205                ),
     206            )
     207        );
     208        register_rest_route(
     209            'watchful/v1',
     210            '/backup/watchful/step',
     211            array(
     212                array(
     213                    'methods' => WP_REST_Server::CREATABLE,
     214                    'callback' => array($this, 'step_watchful_backup'),
     215                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     216                    'args' => array_merge(
     217                        Authentification::get_arguments(),
     218                        array(
     219                            'archive' => array(
     220                                'default' => null,
     221                            ),
     222                            'priority' => array(
     223                                'default' => null,
     224                            ),
     225                        )
     226                    ),
     227                ),
     228            )
     229        );
     230        register_rest_route(
     231            'watchful/v1',
     232            '/backup/watchful/list',
     233            array(
     234                array(
     235                    'methods' => WP_REST_Server::READABLE,
     236                    'callback' => array($this, 'list_watchful_backup'),
     237                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     238                    'args' => array_merge(
     239                        Authentification::get_arguments(),
     240                        array(
     241                            'limit' => array(
     242                                'default' => null,
     243                            ),
     244                        )
     245                    ),
     246                ),
     247            )
     248        );
     249        register_rest_route(
     250            'watchful/v1',
     251            '/backup/watchful/data',
     252            array(
     253                array(
     254                    'methods' => WP_REST_Server::READABLE,
     255                    'callback' => array($this, 'data_watchful'),
     256                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     257                    'args' => array_merge(
     258                        Authentification::get_arguments(),
     259                        array(
     260                            'limit' => array(
     261                                'default' => null,
     262                            ),
     263                        )
     264                    ),
     265                ),
     266            )
     267        );
    195268    }
    196269
     
    338411        ];
    339412    }
     413
     414    /**
     415     * @throws Exception
     416     */
     417    public function execute_watchful_backup()
     418    {
     419        return rest_ensure_response(
     420            (new WatchfulBackupPlugin())->start_backup()
     421        );
     422    }
     423
     424    /**
     425     * Continues previously started backup process
     426     *
     427     * @param WP_REST_Request $request
     428     * @return array
     429     * @throws Exception
     430     */
     431    public function step_watchful_backup(WP_REST_Request $request)
     432    {
     433        $body = json_decode($request->get_body(), true);
     434        if (json_last_error() !== JSON_ERROR_NONE) {
     435            throw new Exception('Cannot decode request body');
     436        }
     437
     438        return (new WatchfulBackupPlugin())->step_backup($body);
     439    }
     440
     441    /**
     442     *
     443     * @param WP_REST_Request $request
     444     * @return array
     445     * @throws Exception
     446     */
     447    public function list_watchful_backup()
     448    {
     449        return $this->backupPluginHelper->get_backup_list('watchful');
     450    }
     451
     452
     453    /**
     454     * @param WP_REST_Request $request
     455     * @return array
     456     * @throws Exception
     457     */
     458    public function data_watchful()
     459    {
     460        return [];
     461    }
    340462}
  • watchful/tags/v2.0.3/lib/Controller/Logs.php

    r3243697 r3257611  
    44
    55use Watchful\Helpers\Logger;
     6use WP_REST_Request;
    67use WP_REST_Response;
    78use WP_REST_Server;
     
    3435                    'permission_callback' => array('Watchful\Routes', 'authentification'),
    3536                ),
     37                array(
     38                    'methods' => WP_REST_Server::DELETABLE,
     39                    'callback' => array($this, 'clear_logs'),
     40                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     41                ),
    3642            )
    3743        );
     
    4147     * @return WP_REST_Response
    4248     */
    43     public function get_logs()
     49    public function get_logs(WP_REST_Request $request)
    4450    {
    45         return new WP_REST_Response($this->logger->get_logs(), 200);
     51        $channel = $request->get_param('channel');
     52        $lines = $request->get_param('lines') ?? 0;
     53
     54        return new WP_REST_Response($this->logger->get_logs($lines, $channel), 200);
     55    }
     56
     57    /**
     58     * @return WP_REST_Response
     59     */
     60    public function clear_logs(WP_REST_Request $request)
     61    {
     62        $channel = $request->get_param('channel');
     63
     64        return new WP_REST_Response($this->logger->clear_logs($channel), 200);
    4665    }
    4766}
  • watchful/tags/v2.0.3/lib/Controller/Plugins.php

    r3123686 r3257611  
    166166            $params['zip'],
    167167            $params['enable_maintenance_mode'],
    168             $params['handle_shutdown']
     168            $params['handle_shutdown'],
     169            $params['use_lock']
    169170        );
    170171
     
    179180            'enable_maintenance_mode' => false,
    180181            'handle_shutdown' => false,
     182            'use_lock' => false,
    181183        );
    182184
     
    196198            if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
    197199                $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
     200            }
     201
     202            if (!empty($post_data) && !empty($post_data->use_lock)) {
     203                $params['use_lock'] = (bool)$post_data->use_lock;
    198204            }
    199205        }
     
    219225                $params['zip'],
    220226                $params['enable_maintenance_mode'],
    221                 $params['handle_shutdown']
     227                $params['handle_shutdown'],
     228                $params['use_lock']
    222229            )
    223230        );
  • watchful/tags/v2.0.3/lib/Controller/Themes.php

    r3238406 r3257611  
    9999    }
    100100
    101     private function parse_install_update_request_params(WP_REST_Request $request)
    102     {
    103         $params = array(
    104             'slug' => $request->get_param('slug'),
    105             'zip' => $request->get_param('zip'),
    106             'enable_maintenance_mode' => false,
    107             'handle_shutdown' => false,
    108             'new_version' => null
    109         );
    110 
    111         $body = $request->get_body();
    112 
    113         if (!empty($body)) {
    114             $post_data = json_decode($body);
    115 
    116             if (!empty($post_data) && !empty($post_data->package)) {
    117                 $params['zip'] = $post_data->package;
    118             }
    119 
    120             if (!empty($post_data) && !empty($post_data->maintenance_mode)) {
    121                 $params['enable_maintenance_mode'] = (bool)$post_data->maintenance_mode;
    122             }
    123 
    124             if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
    125                 $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
    126             }
    127 
    128             if (!empty($post_data) && !empty($post_data->new_version)) {
    129                 $params['new_version'] = $post_data->new_version;
    130             }
    131         }
    132 
    133         return $params;
    134     }
    135 
    136101    /**
    137102     * Update a theme from his slug.
     
    158123                $params['enable_maintenance_mode'],
    159124                $params['handle_shutdown'],
     125                $params['use_lock'],
    160126                $params['new_version']
    161127            )
    162128        );
     129    }
     130
     131    private function parse_install_update_request_params(WP_REST_Request $request)
     132    {
     133        $params = array(
     134            'slug' => $request->get_param('slug'),
     135            'zip' => $request->get_param('zip'),
     136            'enable_maintenance_mode' => false,
     137            'handle_shutdown' => false,
     138            'new_version' => null,
     139        );
     140
     141        $body = $request->get_body();
     142
     143        if (!empty($body)) {
     144            $post_data = json_decode($body);
     145
     146            if (!empty($post_data) && !empty($post_data->package)) {
     147                $params['zip'] = $post_data->package;
     148            }
     149
     150            if (!empty($post_data) && !empty($post_data->maintenance_mode)) {
     151                $params['enable_maintenance_mode'] = (bool)$post_data->maintenance_mode;
     152            }
     153
     154            if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
     155                $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
     156            }
     157
     158            if (!empty($post_data) && !empty($post_data->use_lock)) {
     159                $params['use_lock'] = (bool)$post_data->use_lock;
     160            }
     161
     162            if (!empty($post_data) && !empty($post_data->new_version)) {
     163                $params['new_version'] = $post_data->new_version;
     164            }
     165        }
     166
     167        return $params;
    163168    }
    164169
  • watchful/tags/v2.0.3/lib/Helpers/BackupPluginHelper.php

    r3123686 r3257611  
    55use Watchful\Helpers\BackupPlugins\Ai1wmBackupPlugin;
    66use Watchful\Helpers\BackupPlugins\AkeebaBackupPlugin;
     7use Watchful\Helpers\BackupPlugins\WatchfulBackupPlugin;
    78use Watchful\Helpers\BackupPlugins\XClonerBackupPlugin;
    89
     
    1617    private $xclonerBackupPluginHelper;
    1718
     19    /** @var WatchfulBackupPlugin|null */
     20    private $watchfulBackupPluginHelper;
     21
    1822    public function __construct()
    1923    {
     
    2125        $this->ai1wmBackupPluginHelper = self::has_active_backup_plugin('ai1wm') ? new Ai1wmBackupPlugin() : null;
    2226        $this->xclonerBackupPluginHelper = self::has_active_backup_plugin('xcloner') ? new XClonerBackupPlugin() : null;
     27        $this->watchfulBackupPluginHelper = new WatchfulBackupPlugin();
    2328    }
    2429
     
    8287        });
    8388
     89        $watchful_profiles = array_filter($site_backups_data, function ($profile) {
     90            return $profile->plugin === 'watchful';
     91        });
     92
    8493        if ($this->akeebaBackupPluginHelper !== null && !empty($akeeba_profiles)) {
    8594            foreach ($akeeba_profiles as $akeeba_profile) {
     
    96105        if ($this->xclonerBackupPluginHelper !== null && !empty($xcloner_profiles)) {
    97106            $last_backup_dates[] = $this->xclonerBackupPluginHelper->get_last_backup_date();
     107        }
     108
     109        if (!empty($watchful_profiles)) {
     110            $last_backup_dates[] = $this->watchfulBackupPluginHelper->get_last_backup_date();
    98111        }
    99112
  • watchful/tags/v2.0.3/lib/Helpers/Logger.php

    r3243697 r3257611  
    1010    const DEBUG = 100;
    1111    const INFO = 200;
    12     const NOTICE = 250;
    1312    /**
    1413     * Exceptional occurrences that are not errors
     
    2019    const ERROR = 400;
    2120
     21    /** @var string */
    2222    private $log_dir;
     23
     24    /** @var string */
    2325    private $log_file;
     26
     27    /** @var null | string */
     28    private $channel;
    2429    private $max_file_size = 5242880;
    2530    private $max_backup_files = 5;
     
    2732    private $filesystem;
    2833
    29     public function __construct()
     34    public function __construct(?string $channel = null)
    3035    {
    3136        $this->log_dir = WATCHFUL_PLUGIN_CONTENT_DIR;
     
    3641        global $wp_filesystem;
    3742        $this->filesystem = $wp_filesystem;
     43        $this->channel = $channel;
     44    }
     45
     46    public function get_logs(?int $lines = 0, ?string $channel = null): array
     47    {
     48        if (!$this->filesystem->exists($this->log_file)) {
     49            return [];
     50        }
     51
     52        $content = $this->filesystem->get_contents_array($this->log_file);
     53        if (!$content) {
     54            return [];
     55        }
     56
     57        $logs = array_slice($content, 2);
     58
     59        if ($lines > 0) {
     60            $logs = array_slice($logs, -$lines);
     61        }
     62
     63        $logs = array_values(
     64            array_filter($logs)
     65        );
     66
     67        if (!empty($channel)) {
     68            $logs = array_values(array_filter($logs, function ($log) use ($channel) {
     69                $log = json_decode($log, true);
     70
     71                return isset($log['channel']) && $log['channel'] === $channel;
     72            }));
     73        }
     74
     75        $map = array_map(function ($log) {
     76            $log = json_decode($log, true);
     77
     78            if (json_last_error() !== JSON_ERROR_NONE) {
     79                return null;
     80            }
     81
     82            if (!isset($log['message'], $log['context'], $log['level'], $log['ts'])) {
     83                return null;
     84            }
     85
     86            switch ($log['level']) {
     87                case self::DEBUG:
     88                    $log['level'] = 'DEBUG';
     89                    break;
     90                case self::INFO:
     91                    $log['level'] = 'INFO';
     92                    break;
     93                case self::WARNING:
     94                    $log['level'] = 'WARNING';
     95                    break;
     96                case self::ERROR:
     97                    $log['level'] = 'ERROR';
     98                    break;
     99                default:
     100                    $log['level'] = 'UNKNOWN';
     101                    break;
     102            }
     103
     104            $log['ts'] = gmdate('Y-m-d H:i:s', $log['ts'] ?: 0);
     105
     106            return $log;
     107        }, $logs);
     108
     109        return array_values(
     110            array_filter($map)
     111        );
     112    }
     113
     114    public function clear_logs(?string $channel = null): void
     115    {
     116        if (!$this->filesystem->exists($this->log_file)) {
     117            return;
     118        }
     119
     120        $content = $this->filesystem->get_contents_array($this->log_file);
     121        if (!$content) {
     122            return;
     123        }
     124
     125        $this->filesystem->put_contents($this->log_file, "<?php\nif (!defined('ABSPATH')) { exit; }\n", FS_CHMOD_FILE);
     126
     127        if (empty($channel)) {
     128            return;
     129        }
     130
     131        $logs = array_slice($content, 2);
     132        $logs = array_values(
     133            array_filter($logs)
     134        );
     135
     136        $logs = array_values(array_filter($logs, function ($log) use ($channel) {
     137            $log = json_decode($log, true);
     138
     139            return isset($log['channel']) && $log['channel'] === $channel;
     140        }));
     141
     142        foreach ($logs as $log) {
     143            $log = json_decode($log, true);
     144            $this->log($log['message'], $log['context'], $log['level']);
     145        }
    38146    }
    39147
     
    58166            ];
    59167
     168            if (!empty($this->channel)) {
     169                $log_entry['channel'] = $this->channel;
     170            }
     171
    60172            $encoded_entry = wp_json_encode($log_entry)."\n";
    61173
     
    72184    }
    73185
    74     public function check_log_size()
     186    private function check_log_size()
    75187    {
    76188        if (
     
    110222    }
    111223
    112     public function get_logs($lines = 0)
    113     {
    114         if (!$this->filesystem->exists($this->log_file)) {
    115             return [];
    116         }
    117 
    118         $content = $this->filesystem->get_contents_array($this->log_file);
    119         if (!$content) {
    120             return [];
    121         }
    122 
    123         $logs = array_slice($content, 2);
    124 
    125         if ($lines > 0) {
    126             $logs = array_slice($logs, -$lines);
    127         }
    128 
    129         $logs = array_values(
    130             array_filter($logs)
    131         );
    132 
    133         $map = array_map(function ($log) {
    134             $log = json_decode($log, true);
    135 
    136             if (json_last_error() !== JSON_ERROR_NONE) {
    137                 return null;
    138             }
    139 
    140             if (!isset($log['message'], $log['context'], $log['level'], $log['ts'])) {
    141                 return null;
    142             }
    143 
    144             switch ($log['level']) {
    145                 case self::DEBUG:
    146                     $log['level'] = 'DEBUG';
    147                     break;
    148                 case self::INFO:
    149                     $log['level'] = 'INFO';
    150                     break;
    151                 case self::NOTICE:
    152                     $log['level'] = 'NOTICE';
    153                     break;
    154                 case self::WARNING:
    155                     $log['level'] = 'WARNING';
    156                     break;
    157                 case self::ERROR:
    158                     $log['level'] = 'ERROR';
    159                     break;
    160                 default:
    161                     $log['level'] = 'UNKNOWN';
    162                     break;
    163             }
    164 
    165             $log['ts'] = gmdate('Y-m-d H:i:s', $log['ts'] ?: 0);
    166 
    167             return $log;
    168         }, $logs);
    169 
    170         return array_values(
    171             array_filter($map)
    172         );
     224    public function error($message, $context = [])
     225    {
     226        return $this->log($message, $context, self::ERROR);
     227    }
     228
     229    public function warning($message, $context = [])
     230    {
     231        return $this->log($message, $context, self::WARNING);
     232    }
     233
     234    public function info($message, $context = [])
     235    {
     236        return $this->log($message, $context);
     237    }
     238
     239    public function debug($message, $context = [])
     240    {
     241        return $this->log($message, $context, self::DEBUG);
    173242    }
    174243}
  • watchful/tags/v2.0.3/lib/Helpers/PluginManager.php

    r3243697 r3257611  
    1111class PluginManager
    1212{
     13    private const LOCK_NAME = 'install_update_plugin';
    1314    private $logger;
     15    private $lock_factory;
    1416
    1517    public function __construct()
    1618    {
    17         $this->logger = new Logger();
     19        $this->logger = new Logger('plugin_manager');
     20        $this->lock_factory = new LockFactory($this->logger);
    1821    }
    1922
     
    3134        $zip = null,
    3235        $enable_maintenance_mode = false,
    33         $handle_shutdown = false
     36        $handle_shutdown = false,
     37        $use_lock = false
    3438    ) {
    3539        include_once ABSPATH.'wp-admin/includes/admin.php';
     
    4751        ]);
    4852
     53        $lock = false;
     54
     55        if ($use_lock) {
     56            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     57        }
     58
     59        if ($use_lock && !$lock) {
     60            $this->logger->log('Plugin installation already in progress', [], Logger::WARNING);
     61            throw new Exception('Plugin installation already in progress', 409);
     62        }
     63
    4964        if (!$slug && !$zip) {
    5065            $this->logger->log('parameter is missing. slug or zip required', [
     
    5267                'zip' => $zip,
    5368            ],                 Logger::WARNING);
     69
     70            if ($use_lock) {
     71                $this->lock_factory->release(self::LOCK_NAME);
     72            }
     73
    5474            throw new Exception('parameter is missing. slug or zip required', 400);
    5575        }
     
    5777        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    5878            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     79
     80            if ($use_lock) {
     81                $this->lock_factory->release(self::LOCK_NAME);
     82            }
     83
    5984            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    6085        }
     86
    6187
    6288        if ($zip) {
     
    77103            ]);
    78104
    79             $this->update_plugin($slug, $zip, $enable_maintenance_mode, $handle_shutdown);
     105            if ($use_lock) {
     106                $this->lock_factory->release(self::LOCK_NAME);
     107            }
     108
     109            $this->update_plugin($slug, $zip, $enable_maintenance_mode, $handle_shutdown, $use_lock);
    80110
    81111            return;
     
    137167
    138168            throw new Exception($e->getMessage(), 500);
     169        } finally {
     170            if ($use_lock) {
     171                $this->lock_factory->release(self::LOCK_NAME);
     172            }
    139173        }
    140174
     
    279313        $zip = null,
    280314        $enable_maintenance_mode = false,
    281         $handle_shutdown = false
     315        $handle_shutdown = false,
     316        $use_lock = false
    282317    ) {
    283318        $this->logger->log('Received request to update plugin', [
     
    296331        }
    297332
     333        $lock = false;
     334
     335        if ($use_lock) {
     336            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     337        }
     338
     339        if ($use_lock && !$lock) {
     340            $this->logger->log('Plugin update already in progress', [], Logger::WARNING);
     341            $this->lock_factory->release(self::LOCK_NAME);
     342            throw new Exception('Plugin update already in progress', 409);
     343        }
     344
    298345        if (empty($plugin_path) && empty($zip)) {
    299346            $this->logger->log('parameter is missing. plugin_path or zip required', [], Logger::WARNING);
     347
     348            if ($use_lock) {
     349                $this->lock_factory->release(self::LOCK_NAME);
     350            }
     351
    300352            throw new Exception('parameter is missing. plugin_path or zip required', 400);
    301353        }
     
    303355        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    304356            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     357
     358            if ($use_lock) {
     359                $this->lock_factory->release(self::LOCK_NAME);
     360            }
     361
    305362            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    306363        }
     
    333390                'current_version' => phpversion(),
    334391            ],                 Logger::ERROR);
     392
     393            if ($use_lock) {
     394                $this->lock_factory->release(self::LOCK_NAME);
     395            }
     396
    335397            throw new Exception("The minimum required PHP version for this update is ".$min_php_version, 500);
    336398        }
     
    375437                $e->getMessage()
    376438            );
     439        } finally {
     440            if ($use_lock) {
     441                $this->lock_factory->release(self::LOCK_NAME);
     442            }
    377443        }
    378444
     
    396462                'plugin_path' => $plugin_path,
    397463                'error' => $skin->error->get_error_messages(),
    398                 'data' => $result->get_all_error_data(),
     464                'data' => $skin->error->get_all_error_data(),
    399465            ],                 Logger::ERROR);
    400466            $this->handle_update_error(
     
    482548        }
    483549
    484         throw new Exception($error_message, $error_code, [
     550        throw new Exception($error_message, (int)$error_code, [
    485551            'plugin' => $slug,
    486552            'is_installed' => $this->is_installed($slug),
  • watchful/tags/v2.0.3/lib/Helpers/ThemeUpdater.php

    r3243697 r3257611  
    1010class ThemeUpdater
    1111{
     12    private const LOCK_NAME = 'install_update_theme';
    1213    private $logger;
     14    private $lock_factory;
    1315
    1416    public function __construct()
    1517    {
    16         $this->logger = new Logger();
     18        $this->logger = new Logger('theme_updater');
     19        $this->lock_factory = new LockFactory($this->logger);
    1720    }
    1821
     
    3134        $enable_maintenance_mode = false,
    3235        $handle_shutdown = false,
     36        $use_lock = false,
    3337        $new_version = null
    3438    ) {
     
    4953        ]);
    5054
     55        $lock = false;
     56
     57        if ($use_lock) {
     58            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     59        }
     60
     61        if ($use_lock && !$lock) {
     62            $this->logger->log('Could not acquire lock', [
     63                'lock_name' => self::LOCK_NAME,
     64            ],                 Logger::WARNING);
     65            throw new Exception('Theme update is already in progress', 409);
     66        }
     67
    5168        if (empty($slug) && empty($zip)) {
    5269            $this->logger->log('parameter is missing. slug required or zip', [
     
    5471                'zip' => $zip,
    5572            ],                 Logger::WARNING);
     73
     74            if ($use_lock) {
     75                $this->lock_factory->release(self::LOCK_NAME);
     76            }
     77
    5678            throw new Exception('parameter is missing. slug required or zip', 400);
    5779        }
     
    5981        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    6082            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     83
     84            if ($use_lock) {
     85                $this->lock_factory->release(self::LOCK_NAME);
     86            }
     87
    6188            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    6289        }
     
    91118                'min_php_version' => $min_php_version,
    92119            ],                 Logger::WARNING);
     120
     121            if ($use_lock) {
     122                $this->lock_factory->release(self::LOCK_NAME);
     123            }
     124
    93125            throw new Exception("The minimum required PHP version for this update is ".$min_php_version, 500);
    94126        }
     
    128160                $e->getMessage()
    129161            );
     162        } finally {
     163            if ($use_lock) {
     164                $this->lock_factory->release(self::LOCK_NAME);
     165            }
    130166        }
    131167
  • watchful/tags/v2.0.3/readme.txt

    r3243697 r3257611  
    22Contributors: watchful
    33Tags: manage multiple sites, Wordpress Dashboard, backup, WordPress manager, WordPress management, site management, watchful, remote administration, multiple wordpress
    4 Requires at least: 4.4
     4Requires at least: 4.6
    55Tested up to: 6.7.2
    6 Requires PHP: 5.6
     6Requires PHP: 7.2
    77Stable tag: 1.5.0
    88License: GPLv2 or later
     
    8383
    8484== Changelog ==
     85= v2.0.3 =
     86* Add logs to backup processor class
     87* ignore backup folders from other vendors
     88
     89= v2.0.2 =
     90* Add a lock manager to prevent multiple update / install processes at the same time
     91
     92= v2.0.1 =
     93* Filter DB tables to backup on WP related tables
     94
     95= v2.0.0 =
     96* Change minimum PHP version to 7.2
     97* Implement site backups
     98
    8599= v1.8.2 =
    86100* Add logs to update process to improve debug
  • watchful/tags/v2.0.3/watchful.php

    r3243697 r3257611  
    44 * Plugin URI: https://app.watchful.net
    55 * Description: Remote Website Management Plugin by Watchful
    6  * Version: 1.8.2
     6 * Version: 2.0.3
    77 * Author: watchful
    88 * Author URI: https://watchful.net
  • watchful/trunk/lib/Controller/Backups.php

    r3123686 r3257611  
    2020use Watchful\Helpers\Authentification;
    2121use Watchful\Helpers\BackupPluginHelper;
     22use Watchful\Helpers\BackupPlugins\WatchfulBackupPlugin;
    2223use Watchful\Helpers\BackupPlugins\XClonerBackupPlugin;
    2324use WP_REST_Request;
     
    193194            )
    194195        );
     196        register_rest_route(
     197            'watchful/v1',
     198            '/backup/watchful',
     199            array(
     200                array(
     201                    'methods' => WP_REST_Server::CREATABLE,
     202                    'callback' => array($this, 'execute_watchful_backup'),
     203                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     204                    'args' => Authentification::get_arguments(),
     205                ),
     206            )
     207        );
     208        register_rest_route(
     209            'watchful/v1',
     210            '/backup/watchful/step',
     211            array(
     212                array(
     213                    'methods' => WP_REST_Server::CREATABLE,
     214                    'callback' => array($this, 'step_watchful_backup'),
     215                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     216                    'args' => array_merge(
     217                        Authentification::get_arguments(),
     218                        array(
     219                            'archive' => array(
     220                                'default' => null,
     221                            ),
     222                            'priority' => array(
     223                                'default' => null,
     224                            ),
     225                        )
     226                    ),
     227                ),
     228            )
     229        );
     230        register_rest_route(
     231            'watchful/v1',
     232            '/backup/watchful/list',
     233            array(
     234                array(
     235                    'methods' => WP_REST_Server::READABLE,
     236                    'callback' => array($this, 'list_watchful_backup'),
     237                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     238                    'args' => array_merge(
     239                        Authentification::get_arguments(),
     240                        array(
     241                            'limit' => array(
     242                                'default' => null,
     243                            ),
     244                        )
     245                    ),
     246                ),
     247            )
     248        );
     249        register_rest_route(
     250            'watchful/v1',
     251            '/backup/watchful/data',
     252            array(
     253                array(
     254                    'methods' => WP_REST_Server::READABLE,
     255                    'callback' => array($this, 'data_watchful'),
     256                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     257                    'args' => array_merge(
     258                        Authentification::get_arguments(),
     259                        array(
     260                            'limit' => array(
     261                                'default' => null,
     262                            ),
     263                        )
     264                    ),
     265                ),
     266            )
     267        );
    195268    }
    196269
     
    338411        ];
    339412    }
     413
     414    /**
     415     * @throws Exception
     416     */
     417    public function execute_watchful_backup()
     418    {
     419        return rest_ensure_response(
     420            (new WatchfulBackupPlugin())->start_backup()
     421        );
     422    }
     423
     424    /**
     425     * Continues previously started backup process
     426     *
     427     * @param WP_REST_Request $request
     428     * @return array
     429     * @throws Exception
     430     */
     431    public function step_watchful_backup(WP_REST_Request $request)
     432    {
     433        $body = json_decode($request->get_body(), true);
     434        if (json_last_error() !== JSON_ERROR_NONE) {
     435            throw new Exception('Cannot decode request body');
     436        }
     437
     438        return (new WatchfulBackupPlugin())->step_backup($body);
     439    }
     440
     441    /**
     442     *
     443     * @param WP_REST_Request $request
     444     * @return array
     445     * @throws Exception
     446     */
     447    public function list_watchful_backup()
     448    {
     449        return $this->backupPluginHelper->get_backup_list('watchful');
     450    }
     451
     452
     453    /**
     454     * @param WP_REST_Request $request
     455     * @return array
     456     * @throws Exception
     457     */
     458    public function data_watchful()
     459    {
     460        return [];
     461    }
    340462}
  • watchful/trunk/lib/Controller/Logs.php

    r3243697 r3257611  
    44
    55use Watchful\Helpers\Logger;
     6use WP_REST_Request;
    67use WP_REST_Response;
    78use WP_REST_Server;
     
    3435                    'permission_callback' => array('Watchful\Routes', 'authentification'),
    3536                ),
     37                array(
     38                    'methods' => WP_REST_Server::DELETABLE,
     39                    'callback' => array($this, 'clear_logs'),
     40                    'permission_callback' => array('Watchful\Routes', 'authentification'),
     41                ),
    3642            )
    3743        );
     
    4147     * @return WP_REST_Response
    4248     */
    43     public function get_logs()
     49    public function get_logs(WP_REST_Request $request)
    4450    {
    45         return new WP_REST_Response($this->logger->get_logs(), 200);
     51        $channel = $request->get_param('channel');
     52        $lines = $request->get_param('lines') ?? 0;
     53
     54        return new WP_REST_Response($this->logger->get_logs($lines, $channel), 200);
     55    }
     56
     57    /**
     58     * @return WP_REST_Response
     59     */
     60    public function clear_logs(WP_REST_Request $request)
     61    {
     62        $channel = $request->get_param('channel');
     63
     64        return new WP_REST_Response($this->logger->clear_logs($channel), 200);
    4665    }
    4766}
  • watchful/trunk/lib/Controller/Plugins.php

    r3123686 r3257611  
    166166            $params['zip'],
    167167            $params['enable_maintenance_mode'],
    168             $params['handle_shutdown']
     168            $params['handle_shutdown'],
     169            $params['use_lock']
    169170        );
    170171
     
    179180            'enable_maintenance_mode' => false,
    180181            'handle_shutdown' => false,
     182            'use_lock' => false,
    181183        );
    182184
     
    196198            if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
    197199                $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
     200            }
     201
     202            if (!empty($post_data) && !empty($post_data->use_lock)) {
     203                $params['use_lock'] = (bool)$post_data->use_lock;
    198204            }
    199205        }
     
    219225                $params['zip'],
    220226                $params['enable_maintenance_mode'],
    221                 $params['handle_shutdown']
     227                $params['handle_shutdown'],
     228                $params['use_lock']
    222229            )
    223230        );
  • watchful/trunk/lib/Controller/Themes.php

    r3238406 r3257611  
    9999    }
    100100
    101     private function parse_install_update_request_params(WP_REST_Request $request)
    102     {
    103         $params = array(
    104             'slug' => $request->get_param('slug'),
    105             'zip' => $request->get_param('zip'),
    106             'enable_maintenance_mode' => false,
    107             'handle_shutdown' => false,
    108             'new_version' => null
    109         );
    110 
    111         $body = $request->get_body();
    112 
    113         if (!empty($body)) {
    114             $post_data = json_decode($body);
    115 
    116             if (!empty($post_data) && !empty($post_data->package)) {
    117                 $params['zip'] = $post_data->package;
    118             }
    119 
    120             if (!empty($post_data) && !empty($post_data->maintenance_mode)) {
    121                 $params['enable_maintenance_mode'] = (bool)$post_data->maintenance_mode;
    122             }
    123 
    124             if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
    125                 $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
    126             }
    127 
    128             if (!empty($post_data) && !empty($post_data->new_version)) {
    129                 $params['new_version'] = $post_data->new_version;
    130             }
    131         }
    132 
    133         return $params;
    134     }
    135 
    136101    /**
    137102     * Update a theme from his slug.
     
    158123                $params['enable_maintenance_mode'],
    159124                $params['handle_shutdown'],
     125                $params['use_lock'],
    160126                $params['new_version']
    161127            )
    162128        );
     129    }
     130
     131    private function parse_install_update_request_params(WP_REST_Request $request)
     132    {
     133        $params = array(
     134            'slug' => $request->get_param('slug'),
     135            'zip' => $request->get_param('zip'),
     136            'enable_maintenance_mode' => false,
     137            'handle_shutdown' => false,
     138            'new_version' => null,
     139        );
     140
     141        $body = $request->get_body();
     142
     143        if (!empty($body)) {
     144            $post_data = json_decode($body);
     145
     146            if (!empty($post_data) && !empty($post_data->package)) {
     147                $params['zip'] = $post_data->package;
     148            }
     149
     150            if (!empty($post_data) && !empty($post_data->maintenance_mode)) {
     151                $params['enable_maintenance_mode'] = (bool)$post_data->maintenance_mode;
     152            }
     153
     154            if (!empty($post_data) && !empty($post_data->handle_shutdown)) {
     155                $params['handle_shutdown'] = (bool)$post_data->handle_shutdown;
     156            }
     157
     158            if (!empty($post_data) && !empty($post_data->use_lock)) {
     159                $params['use_lock'] = (bool)$post_data->use_lock;
     160            }
     161
     162            if (!empty($post_data) && !empty($post_data->new_version)) {
     163                $params['new_version'] = $post_data->new_version;
     164            }
     165        }
     166
     167        return $params;
    163168    }
    164169
  • watchful/trunk/lib/Helpers/BackupPluginHelper.php

    r3123686 r3257611  
    55use Watchful\Helpers\BackupPlugins\Ai1wmBackupPlugin;
    66use Watchful\Helpers\BackupPlugins\AkeebaBackupPlugin;
     7use Watchful\Helpers\BackupPlugins\WatchfulBackupPlugin;
    78use Watchful\Helpers\BackupPlugins\XClonerBackupPlugin;
    89
     
    1617    private $xclonerBackupPluginHelper;
    1718
     19    /** @var WatchfulBackupPlugin|null */
     20    private $watchfulBackupPluginHelper;
     21
    1822    public function __construct()
    1923    {
     
    2125        $this->ai1wmBackupPluginHelper = self::has_active_backup_plugin('ai1wm') ? new Ai1wmBackupPlugin() : null;
    2226        $this->xclonerBackupPluginHelper = self::has_active_backup_plugin('xcloner') ? new XClonerBackupPlugin() : null;
     27        $this->watchfulBackupPluginHelper = new WatchfulBackupPlugin();
    2328    }
    2429
     
    8287        });
    8388
     89        $watchful_profiles = array_filter($site_backups_data, function ($profile) {
     90            return $profile->plugin === 'watchful';
     91        });
     92
    8493        if ($this->akeebaBackupPluginHelper !== null && !empty($akeeba_profiles)) {
    8594            foreach ($akeeba_profiles as $akeeba_profile) {
     
    96105        if ($this->xclonerBackupPluginHelper !== null && !empty($xcloner_profiles)) {
    97106            $last_backup_dates[] = $this->xclonerBackupPluginHelper->get_last_backup_date();
     107        }
     108
     109        if (!empty($watchful_profiles)) {
     110            $last_backup_dates[] = $this->watchfulBackupPluginHelper->get_last_backup_date();
    98111        }
    99112
  • watchful/trunk/lib/Helpers/Logger.php

    r3243697 r3257611  
    1010    const DEBUG = 100;
    1111    const INFO = 200;
    12     const NOTICE = 250;
    1312    /**
    1413     * Exceptional occurrences that are not errors
     
    2019    const ERROR = 400;
    2120
     21    /** @var string */
    2222    private $log_dir;
     23
     24    /** @var string */
    2325    private $log_file;
     26
     27    /** @var null | string */
     28    private $channel;
    2429    private $max_file_size = 5242880;
    2530    private $max_backup_files = 5;
     
    2732    private $filesystem;
    2833
    29     public function __construct()
     34    public function __construct(?string $channel = null)
    3035    {
    3136        $this->log_dir = WATCHFUL_PLUGIN_CONTENT_DIR;
     
    3641        global $wp_filesystem;
    3742        $this->filesystem = $wp_filesystem;
     43        $this->channel = $channel;
     44    }
     45
     46    public function get_logs(?int $lines = 0, ?string $channel = null): array
     47    {
     48        if (!$this->filesystem->exists($this->log_file)) {
     49            return [];
     50        }
     51
     52        $content = $this->filesystem->get_contents_array($this->log_file);
     53        if (!$content) {
     54            return [];
     55        }
     56
     57        $logs = array_slice($content, 2);
     58
     59        if ($lines > 0) {
     60            $logs = array_slice($logs, -$lines);
     61        }
     62
     63        $logs = array_values(
     64            array_filter($logs)
     65        );
     66
     67        if (!empty($channel)) {
     68            $logs = array_values(array_filter($logs, function ($log) use ($channel) {
     69                $log = json_decode($log, true);
     70
     71                return isset($log['channel']) && $log['channel'] === $channel;
     72            }));
     73        }
     74
     75        $map = array_map(function ($log) {
     76            $log = json_decode($log, true);
     77
     78            if (json_last_error() !== JSON_ERROR_NONE) {
     79                return null;
     80            }
     81
     82            if (!isset($log['message'], $log['context'], $log['level'], $log['ts'])) {
     83                return null;
     84            }
     85
     86            switch ($log['level']) {
     87                case self::DEBUG:
     88                    $log['level'] = 'DEBUG';
     89                    break;
     90                case self::INFO:
     91                    $log['level'] = 'INFO';
     92                    break;
     93                case self::WARNING:
     94                    $log['level'] = 'WARNING';
     95                    break;
     96                case self::ERROR:
     97                    $log['level'] = 'ERROR';
     98                    break;
     99                default:
     100                    $log['level'] = 'UNKNOWN';
     101                    break;
     102            }
     103
     104            $log['ts'] = gmdate('Y-m-d H:i:s', $log['ts'] ?: 0);
     105
     106            return $log;
     107        }, $logs);
     108
     109        return array_values(
     110            array_filter($map)
     111        );
     112    }
     113
     114    public function clear_logs(?string $channel = null): void
     115    {
     116        if (!$this->filesystem->exists($this->log_file)) {
     117            return;
     118        }
     119
     120        $content = $this->filesystem->get_contents_array($this->log_file);
     121        if (!$content) {
     122            return;
     123        }
     124
     125        $this->filesystem->put_contents($this->log_file, "<?php\nif (!defined('ABSPATH')) { exit; }\n", FS_CHMOD_FILE);
     126
     127        if (empty($channel)) {
     128            return;
     129        }
     130
     131        $logs = array_slice($content, 2);
     132        $logs = array_values(
     133            array_filter($logs)
     134        );
     135
     136        $logs = array_values(array_filter($logs, function ($log) use ($channel) {
     137            $log = json_decode($log, true);
     138
     139            return isset($log['channel']) && $log['channel'] === $channel;
     140        }));
     141
     142        foreach ($logs as $log) {
     143            $log = json_decode($log, true);
     144            $this->log($log['message'], $log['context'], $log['level']);
     145        }
    38146    }
    39147
     
    58166            ];
    59167
     168            if (!empty($this->channel)) {
     169                $log_entry['channel'] = $this->channel;
     170            }
     171
    60172            $encoded_entry = wp_json_encode($log_entry)."\n";
    61173
     
    72184    }
    73185
    74     public function check_log_size()
     186    private function check_log_size()
    75187    {
    76188        if (
     
    110222    }
    111223
    112     public function get_logs($lines = 0)
    113     {
    114         if (!$this->filesystem->exists($this->log_file)) {
    115             return [];
    116         }
    117 
    118         $content = $this->filesystem->get_contents_array($this->log_file);
    119         if (!$content) {
    120             return [];
    121         }
    122 
    123         $logs = array_slice($content, 2);
    124 
    125         if ($lines > 0) {
    126             $logs = array_slice($logs, -$lines);
    127         }
    128 
    129         $logs = array_values(
    130             array_filter($logs)
    131         );
    132 
    133         $map = array_map(function ($log) {
    134             $log = json_decode($log, true);
    135 
    136             if (json_last_error() !== JSON_ERROR_NONE) {
    137                 return null;
    138             }
    139 
    140             if (!isset($log['message'], $log['context'], $log['level'], $log['ts'])) {
    141                 return null;
    142             }
    143 
    144             switch ($log['level']) {
    145                 case self::DEBUG:
    146                     $log['level'] = 'DEBUG';
    147                     break;
    148                 case self::INFO:
    149                     $log['level'] = 'INFO';
    150                     break;
    151                 case self::NOTICE:
    152                     $log['level'] = 'NOTICE';
    153                     break;
    154                 case self::WARNING:
    155                     $log['level'] = 'WARNING';
    156                     break;
    157                 case self::ERROR:
    158                     $log['level'] = 'ERROR';
    159                     break;
    160                 default:
    161                     $log['level'] = 'UNKNOWN';
    162                     break;
    163             }
    164 
    165             $log['ts'] = gmdate('Y-m-d H:i:s', $log['ts'] ?: 0);
    166 
    167             return $log;
    168         }, $logs);
    169 
    170         return array_values(
    171             array_filter($map)
    172         );
     224    public function error($message, $context = [])
     225    {
     226        return $this->log($message, $context, self::ERROR);
     227    }
     228
     229    public function warning($message, $context = [])
     230    {
     231        return $this->log($message, $context, self::WARNING);
     232    }
     233
     234    public function info($message, $context = [])
     235    {
     236        return $this->log($message, $context);
     237    }
     238
     239    public function debug($message, $context = [])
     240    {
     241        return $this->log($message, $context, self::DEBUG);
    173242    }
    174243}
  • watchful/trunk/lib/Helpers/PluginManager.php

    r3243697 r3257611  
    1111class PluginManager
    1212{
     13    private const LOCK_NAME = 'install_update_plugin';
    1314    private $logger;
     15    private $lock_factory;
    1416
    1517    public function __construct()
    1618    {
    17         $this->logger = new Logger();
     19        $this->logger = new Logger('plugin_manager');
     20        $this->lock_factory = new LockFactory($this->logger);
    1821    }
    1922
     
    3134        $zip = null,
    3235        $enable_maintenance_mode = false,
    33         $handle_shutdown = false
     36        $handle_shutdown = false,
     37        $use_lock = false
    3438    ) {
    3539        include_once ABSPATH.'wp-admin/includes/admin.php';
     
    4751        ]);
    4852
     53        $lock = false;
     54
     55        if ($use_lock) {
     56            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     57        }
     58
     59        if ($use_lock && !$lock) {
     60            $this->logger->log('Plugin installation already in progress', [], Logger::WARNING);
     61            throw new Exception('Plugin installation already in progress', 409);
     62        }
     63
    4964        if (!$slug && !$zip) {
    5065            $this->logger->log('parameter is missing. slug or zip required', [
     
    5267                'zip' => $zip,
    5368            ],                 Logger::WARNING);
     69
     70            if ($use_lock) {
     71                $this->lock_factory->release(self::LOCK_NAME);
     72            }
     73
    5474            throw new Exception('parameter is missing. slug or zip required', 400);
    5575        }
     
    5777        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    5878            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     79
     80            if ($use_lock) {
     81                $this->lock_factory->release(self::LOCK_NAME);
     82            }
     83
    5984            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    6085        }
     86
    6187
    6288        if ($zip) {
     
    77103            ]);
    78104
    79             $this->update_plugin($slug, $zip, $enable_maintenance_mode, $handle_shutdown);
     105            if ($use_lock) {
     106                $this->lock_factory->release(self::LOCK_NAME);
     107            }
     108
     109            $this->update_plugin($slug, $zip, $enable_maintenance_mode, $handle_shutdown, $use_lock);
    80110
    81111            return;
     
    137167
    138168            throw new Exception($e->getMessage(), 500);
     169        } finally {
     170            if ($use_lock) {
     171                $this->lock_factory->release(self::LOCK_NAME);
     172            }
    139173        }
    140174
     
    279313        $zip = null,
    280314        $enable_maintenance_mode = false,
    281         $handle_shutdown = false
     315        $handle_shutdown = false,
     316        $use_lock = false
    282317    ) {
    283318        $this->logger->log('Received request to update plugin', [
     
    296331        }
    297332
     333        $lock = false;
     334
     335        if ($use_lock) {
     336            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     337        }
     338
     339        if ($use_lock && !$lock) {
     340            $this->logger->log('Plugin update already in progress', [], Logger::WARNING);
     341            $this->lock_factory->release(self::LOCK_NAME);
     342            throw new Exception('Plugin update already in progress', 409);
     343        }
     344
    298345        if (empty($plugin_path) && empty($zip)) {
    299346            $this->logger->log('parameter is missing. plugin_path or zip required', [], Logger::WARNING);
     347
     348            if ($use_lock) {
     349                $this->lock_factory->release(self::LOCK_NAME);
     350            }
     351
    300352            throw new Exception('parameter is missing. plugin_path or zip required', 400);
    301353        }
     
    303355        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    304356            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     357
     358            if ($use_lock) {
     359                $this->lock_factory->release(self::LOCK_NAME);
     360            }
     361
    305362            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    306363        }
     
    333390                'current_version' => phpversion(),
    334391            ],                 Logger::ERROR);
     392
     393            if ($use_lock) {
     394                $this->lock_factory->release(self::LOCK_NAME);
     395            }
     396
    335397            throw new Exception("The minimum required PHP version for this update is ".$min_php_version, 500);
    336398        }
     
    375437                $e->getMessage()
    376438            );
     439        } finally {
     440            if ($use_lock) {
     441                $this->lock_factory->release(self::LOCK_NAME);
     442            }
    377443        }
    378444
     
    396462                'plugin_path' => $plugin_path,
    397463                'error' => $skin->error->get_error_messages(),
    398                 'data' => $result->get_all_error_data(),
     464                'data' => $skin->error->get_all_error_data(),
    399465            ],                 Logger::ERROR);
    400466            $this->handle_update_error(
     
    482548        }
    483549
    484         throw new Exception($error_message, $error_code, [
     550        throw new Exception($error_message, (int)$error_code, [
    485551            'plugin' => $slug,
    486552            'is_installed' => $this->is_installed($slug),
  • watchful/trunk/lib/Helpers/ThemeUpdater.php

    r3243697 r3257611  
    1010class ThemeUpdater
    1111{
     12    private const LOCK_NAME = 'install_update_theme';
    1213    private $logger;
     14    private $lock_factory;
    1315
    1416    public function __construct()
    1517    {
    16         $this->logger = new Logger();
     18        $this->logger = new Logger('theme_updater');
     19        $this->lock_factory = new LockFactory($this->logger);
    1720    }
    1821
     
    3134        $enable_maintenance_mode = false,
    3235        $handle_shutdown = false,
     36        $use_lock = false,
    3337        $new_version = null
    3438    ) {
     
    4953        ]);
    5054
     55        $lock = false;
     56
     57        if ($use_lock) {
     58            $lock = $this->lock_factory->acquire(self::LOCK_NAME);
     59        }
     60
     61        if ($use_lock && !$lock) {
     62            $this->logger->log('Could not acquire lock', [
     63                'lock_name' => self::LOCK_NAME,
     64            ],                 Logger::WARNING);
     65            throw new Exception('Theme update is already in progress', 409);
     66        }
     67
    5168        if (empty($slug) && empty($zip)) {
    5269            $this->logger->log('parameter is missing. slug required or zip', [
     
    5471                'zip' => $zip,
    5572            ],                 Logger::WARNING);
     73
     74            if ($use_lock) {
     75                $this->lock_factory->release(self::LOCK_NAME);
     76            }
     77
    5678            throw new Exception('parameter is missing. slug required or zip', 400);
    5779        }
     
    5981        if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
    6082            $this->logger->log('file modification is disabled (DISALLOW_FILE_MODS)', [], Logger::WARNING);
     83
     84            if ($use_lock) {
     85                $this->lock_factory->release(self::LOCK_NAME);
     86            }
     87
    6188            throw new Exception('file modification is disabled (DISALLOW_FILE_MODS)', 403);
    6289        }
     
    91118                'min_php_version' => $min_php_version,
    92119            ],                 Logger::WARNING);
     120
     121            if ($use_lock) {
     122                $this->lock_factory->release(self::LOCK_NAME);
     123            }
     124
    93125            throw new Exception("The minimum required PHP version for this update is ".$min_php_version, 500);
    94126        }
     
    128160                $e->getMessage()
    129161            );
     162        } finally {
     163            if ($use_lock) {
     164                $this->lock_factory->release(self::LOCK_NAME);
     165            }
    130166        }
    131167
  • watchful/trunk/readme.txt

    r3243697 r3257611  
    22Contributors: watchful
    33Tags: manage multiple sites, Wordpress Dashboard, backup, WordPress manager, WordPress management, site management, watchful, remote administration, multiple wordpress
    4 Requires at least: 4.4
     4Requires at least: 4.6
    55Tested up to: 6.7.2
    6 Requires PHP: 5.6
     6Requires PHP: 7.2
    77Stable tag: 1.5.0
    88License: GPLv2 or later
     
    8383
    8484== Changelog ==
     85= v2.0.3 =
     86* Add logs to backup processor class
     87* ignore backup folders from other vendors
     88
     89= v2.0.2 =
     90* Add a lock manager to prevent multiple update / install processes at the same time
     91
     92= v2.0.1 =
     93* Filter DB tables to backup on WP related tables
     94
     95= v2.0.0 =
     96* Change minimum PHP version to 7.2
     97* Implement site backups
     98
    8599= v1.8.2 =
    86100* Add logs to update process to improve debug
  • watchful/trunk/watchful.php

    r3243697 r3257611  
    44 * Plugin URI: https://app.watchful.net
    55 * Description: Remote Website Management Plugin by Watchful
    6  * Version: 1.8.2
     6 * Version: 2.0.3
    77 * Author: watchful
    88 * Author URI: https://watchful.net
Note: See TracChangeset for help on using the changeset viewer.