roles, true ) && user_can( $user, 'manage_options' ); } /** * URL of the Database Backups page. * * @return string */ public static function page_url() { return is_multisite() ? network_admin_url( 'settings.php?page=' . self::SLUG ) : admin_url( 'tools.php?page=' . self::SLUG ); } /** * Register hooks. */ public function boot() { add_filter( 'cron_schedules', array( Runner::class, 'cron_schedules' ) ); // phpcs:ignore WordPress.WP.CronInterval add_action( Runner::CRON_HOOK, array( Runner::class, 'cron' ) ); Ajax::register(); add_action( 'admin_post_dbmg_download', array( Downloader::class, 'handle' ) ); if ( is_multisite() ) { add_action( 'network_admin_menu', array( $this, 'menu' ) ); add_filter( 'network_admin_plugin_action_links_' . plugin_basename( DBMG_FILE ), array( $this, 'action_links' ) ); add_action( 'network_admin_notices', array( Notices::class, 'finished_notice' ) ); } else { add_action( 'admin_menu', array( $this, 'menu' ) ); add_filter( 'plugin_action_links_' . plugin_basename( DBMG_FILE ), array( $this, 'action_links' ) ); add_action( 'admin_notices', array( Notices::class, 'finished_notice' ) ); } add_action( 'admin_enqueue_scripts', array( $this, 'plugins_icon' ) ); add_action( 'admin_bar_menu', array( Notices::class, 'admin_bar' ), 90 ); add_action( 'admin_enqueue_scripts', array( Notices::class, 'enqueue_assist' ) ); add_filter( 'heartbeat_received', array( Notices::class, 'heartbeat' ), 10, 2 ); add_filter( 'site_status_tests', array( Site_Health::class, 'register' ) ); if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'dbmg', CLI::class ); } } /** * Tools → Database Backups, or Network Admin → Settings → Database Backups (§14.1). */ public function menu() { if ( ! self::user_can() ) { return; } $parent = is_multisite() ? 'settings.php' : 'tools.php'; $this->hook = add_submenu_page( $parent, __( 'Database Backups', 'database-export-genie' ), __( 'Database Backups', 'database-export-genie' ), self::capability(), self::SLUG, array( Admin_Page::class, 'render' ) ); if ( $this->hook ) { add_action( 'load-' . $this->hook, array( Admin_Page::class, 'load' ) ); } } /** * "Settings" link in the Plugins list (opens the backups page). * * @param array $links Links. * @return array */ public function action_links( $links ) { if ( ! self::user_can() ) { return $links; } array_unshift( $links, '' . esc_html__( 'Settings', 'database-export-genie' ) . '' ); return $links; } /** * The genie beside our row on the Plugins screen. WordPress only shows icons for wp.org plugins on update * screens, so the row gets it through a few lines of CSS scoped to our basename (no extra request). * Enqueued as inline CSS on a style handle without a file (wp.org review "wp_enqueue commands", D74). * * @param string $hook_suffix Current admin page. */ public function plugins_icon( $hook_suffix ) { if ( 'plugins.php' !== $hook_suffix ) { return; } $row = '.plugins tr[data-plugin="' . plugin_basename( DBMG_FILE ) . '"] .plugin-title'; $css = $row . '{background-image:url("' . esc_url( DBMG_URL . 'assets/genie.svg' ) . '");background-repeat:no-repeat;background-position:8px 10px;background-size:44px 44px;padding-inline-start:64px}' . '[dir="rtl"] ' . $row . '{background-position:right 8px top 10px}'; wp_register_style( 'dbmg-plugins-icon', false, array(), DBMG_VERSION ); wp_enqueue_style( 'dbmg-plugins-icon' ); wp_add_inline_style( 'dbmg-plugins-icon', $css ); } /** * Activation: requirements already checked; create the folder + protection files only (§18). */ public static function activate() { try { Storage::ensure(); } catch ( Backup_Exception $e ) { unset( $e ); // The page explains the folder problem; activation must not fail for it. } } /** * Deactivation: stop the job, clear hooks and locks, keep finished backups (§18). */ public static function deactivate() { Runner::unschedule_watchdog(); $job = Job::load(); if ( $job && $job->is_active() ) { $job->cancel( get_current_user_id() ); } Lock::force_clear(); Options::delete( Runner::KEY_OPTION ); } }