'; return 'data:image/svg+xml;base64,' . base64_encode( $svg ); // phpcs:ignore } /** * Pages. * * @return array slug => [title, view] */ public static function pages() { $pages = array( self::SLUG => array( __( 'Dashboard', 'spinix-migration' ), 'dashboard', 'home' ), self::SLUG . '-backups' => array( __( 'Backups', 'spinix-migration' ), 'backups', 'archive' ), self::SLUG . '-import' => array( __( 'Import / Restore', 'spinix-migration' ), 'import', 'upload' ), self::SLUG . '-tools' => array( __( 'Downloads & Tools', 'spinix-migration' ), 'tools', 'tools' ), self::SLUG . '-settings' => array( __( 'Schedule & Settings', 'spinix-migration' ), 'settings', 'clock' ), self::SLUG . '-health' => array( __( 'System Health', 'spinix-migration' ), 'health', 'pulse' ), ); /** * Admin pages: slug => [ title, view, icon ]. Add-ons can add views (rendered by their own JS). */ return (array) apply_filters( 'spinix_admin_pages', $pages ); } /** * Branding (name, logo, colors). Filterable for white-label add-ons. * * @return array */ public static function branding() { return (array) apply_filters( 'spinix_branding', array( 'name' => 'Spinix Migration & Backup', 'menu' => 'Spinix', 'tagline' => __( 'Backup · Migrate · Restore', 'spinix-migration' ), 'logo' => '', 'menu_icon' => self::icon(), 'color' => '', 'color2' => '', 'show_footer' => true, 'show_version' => true, ) ); } /** * Register menu. */ public static function menu() { $cap = Spinix_Ajax::cap(); $brand = self::branding(); add_menu_page( $brand['name'], $brand['menu'], $cap, self::SLUG, array( __CLASS__, 'render' ), $brand['menu_icon'], 76 ); foreach ( self::pages() as $slug => $p ) { $menu_title = $p[0]; if ( 'backups' === $p[1] ) { $n = self::backup_count(); $menu_title = $p[0] . ' ' . (int) $n . ''; } add_submenu_page( self::SLUG, $p[0] . ' ‹ ' . $brand['menu'], $menu_title, $cap, $slug, array( __CLASS__, 'render' ) ); } } /** * Number of .spinix backups in the storage folder (cheap: no file is opened). * * @return int */ public static function backup_count() { $base = Spinix_Storage::base_dir(); if ( ! is_dir( $base ) ) { return 0; } $a = glob( $base . '/*.spinix' ); $b = glob( $base . '/*.SPINIX' ); return count( array_unique( array_merge( $a ? $a : array(), $b ? $b : array() ) ) ); } /** * Style for the backup count bubble in the WordPress admin menu. */ public static function menu_css() { if ( ! current_user_can( Spinix_Ajax::cap() ) ) { return; } wp_add_inline_style( 'admin-menu', '#adminmenu .spinix-count{display:inline-block;min-width:18px;height:18px;padding:0 6px;margin-left:6px;border-radius:9px;background:#5b4dff;color:#fff;font-size:11px;font-weight:600;line-height:18px;text-align:center;vertical-align:text-bottom;box-sizing:border-box}' ); } /** * Plugins list links. * * @param array $links Links. * @return array */ public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Open Spinix', 'spinix-migration' ) . '' ); return $links; } /** * Is this one of our pages? * * @return string|null View. */ private static function current_view() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore $pages = self::pages(); return isset( $pages[ $page ] ) ? $pages[ $page ][1] : null; } /** * Enqueue assets. */ public static function assets() { $view = self::current_view(); if ( ! $view ) { return; } $ver = SPINIX_VERSION . '.' . filemtime( SPINIX_DIR . 'assets/js/admin.js' ); wp_enqueue_style( 'spinix-admin', SPINIX_URL . 'assets/css/admin.css', array(), $ver ); wp_enqueue_script( 'spinix-admin', SPINIX_URL . 'assets/js/admin.js', array(), $ver, true ); $urls = array(); $nav = array(); foreach ( self::pages() as $slug => $p ) { $urls[ $p[1] ] = admin_url( 'admin.php?page=' . $slug ); $nav[] = array( $p[1], isset( $p[2] ) ? $p[2] : 'tools', $p[0] ); } $config = array( 'ajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( Spinix_Ajax::NONCE ), 'download' => wp_nonce_url( admin_url( 'admin-post.php?action=spinix_download' ), 'spinix_download' ), 'view' => $view, 'urls' => $urls, 'version' => SPINIX_VERSION, 'home' => home_url(), 'login' => wp_login_url(), 'chunk' => Spinix_Uploader::chunk_size(), 'folder' => Spinix_Storage::base_dir(), 'php' => PHP_VERSION, 'encryption' => (bool) Spinix_Crypto::best_cipher(), 'settings' => Spinix_Scheduler::settings(), 'backupCount' => self::backup_count(), 'i18n' => self::strings(), 'nav' => $nav, 'branding' => self::branding(), ); /** * Data passed to the admin app (add-ons can add their own keys). */ $config = apply_filters( 'spinix_admin_config', $config, $view ); wp_localize_script( 'spinix-admin', 'SpinixConfig', $config ); /** * Add-ons enqueue their admin scripts here (depend on 'spinix-admin'). */ do_action( 'spinix_admin_enqueue', $view ); } /** * Render the app shell. */ public static function render() { echo '

Spinix Migration & Backup

'; echo '
'; } /** * Admin notices outside our pages. */ public static function notices() { if ( self::current_view() || ! current_user_can( Spinix_Ajax::cap() ) ) { return; } try { $active = Spinix_Job::active(); } catch ( Throwable $e ) { return; } if ( $active && in_array( $active->data['type'], array( 'restore', 'rollback' ), true ) ) { echo '

Spinix: ' . esc_html__( 'A restore was interrupted before it finished. Open Spinix to complete it.', 'spinix-migration' ) . ' ' . esc_html__( 'Open Spinix', 'spinix-migration' ) . '

'; } } /** * UI strings (translatable). * * @return array */ private static function strings() { return array( 'dashboard' => __( 'Dashboard', 'spinix-migration' ), 'backups' => __( 'Backups', 'spinix-migration' ), 'import' => __( 'Import / Restore', 'spinix-migration' ), 'tools' => __( 'Downloads & Tools', 'spinix-migration' ), 'settings' => __( 'Schedule & Settings', 'spinix-migration' ), 'health' => __( 'System Health', 'spinix-migration' ), 'confirmDelete' => __( 'Delete this backup permanently?', 'spinix-migration' ), 'leaveWarning' => __( 'A restore is running. If you leave now, your website may be left broken. Stay on this page until it finishes.', 'spinix-migration' ), 'leaveBackup' => __( 'A backup is running. Leaving will pause it (you can resume later).', 'spinix-migration' ), ); } }