Skip to content

Commit 076fb7d

Browse files
authored
Merge pull request #2 from soderlind/refactor
Refactor code
2 parents 7e8e11f + 141a247 commit 076fb7d

11 files changed

Lines changed: 1674 additions & 1224 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ All notable changes to the Multisite Exporter plugin will be documented in this
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.1.0] - 2025-05-07
8+
### Changed
9+
- Refactored plugin architecture to follow single responsibility principle
10+
- Moved admin UI functionality to dedicated `class-admin.php`
11+
- Separated export processing logic into `class-export.php`
12+
- Created core initialization class in `class-init.php`
13+
- Refactored JavaScript: Moved all inline JavaScript to separate files
14+
- Created `history-page.js` for export history page functionality
15+
- Added proper namespacing through IIFE pattern
16+
- Improved script loading using WordPress enqueuing API
17+
- Renamed `enqueue_admin_styles()` to `enqueue_admin_assets()` for better semantics
18+
- Implemented conditional asset loading based on current admin page
19+
- Moved plugin constants from class-multisite-exporter.php to main plugin file
20+
- Better initialization and earlier constant availability
21+
- Enhanced UI: Added disable/enable functionality for selection buttons when "select all across pages" is active
22+
723
## [1.0.1] - 2025-05-07
824
### Changed
925
- Updated documentation to clarify the location of the Action Scheduler library

includes/admin/class-admin.php

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
<?php
2+
/**
3+
* Admin class.
4+
*
5+
* @since 1.0.1
6+
* @package Multisite_Exporter
7+
* @subpackage Multisite_Exporter/Admin
8+
*/
9+
10+
// If this file is called directly, abort.
11+
if ( ! defined( 'ABSPATH' ) ) {
12+
exit;
13+
}
14+
15+
/**
16+
* Admin class.
17+
*/
18+
class ME_Admin {
19+
20+
/**
21+
* The single instance of the class.
22+
*
23+
* @since 1.0.1
24+
* @var ME_Admin
25+
*/
26+
protected static $_instance = null;
27+
28+
/**
29+
* Main ME_Admin Instance.
30+
*
31+
* @return ME_Admin - Main instance.
32+
*/
33+
public static function instance() {
34+
if ( is_null( self::$_instance ) ) {
35+
self::$_instance = new self();
36+
}
37+
return self::$_instance;
38+
}
39+
40+
/**
41+
* Constructor.
42+
*/
43+
public function __construct() {
44+
$this->init_hooks();
45+
}
46+
47+
/**
48+
* Initialize hooks.
49+
*/
50+
private function init_hooks() {
51+
// Add menu
52+
add_action( 'network_admin_menu', array( $this, 'add_admin_menu' ) );
53+
54+
// Register plugin assets (styles and scripts)
55+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
56+
57+
// AJAX handlers
58+
add_action( 'wp_ajax_me_download_export', array( $this, 'handle_export_download' ) );
59+
add_action( 'wp_ajax_me_download_selected_exports', array( $this, 'download_selected_exports' ) );
60+
}
61+
62+
/**
63+
* Enqueue admin assets (styles and scripts).
64+
*
65+
* @param string $hook The current admin page hook.
66+
*/
67+
public function enqueue_admin_assets( $hook ) {
68+
// Only load assets on our plugin pages
69+
if ( strpos( $hook, 'multisite-exporter' ) === false ) {
70+
return;
71+
}
72+
73+
// Enqueue common styles
74+
wp_enqueue_style(
75+
'multisite-exporter-styles',
76+
MULTISITE_EXPORTER_PLUGIN_URL . 'css/multisite-exporter.css',
77+
array(),
78+
filemtime( MULTISITE_EXPORTER_PLUGIN_DIR . 'css/multisite-exporter.css' )
79+
);
80+
81+
// Enqueue common admin scripts
82+
wp_enqueue_script(
83+
'multisite-exporter-admin',
84+
MULTISITE_EXPORTER_PLUGIN_URL . 'js/multisite-exporter-admin.js',
85+
array( 'jquery' ),
86+
filemtime( MULTISITE_EXPORTER_PLUGIN_DIR . 'js/multisite-exporter-admin.js' ),
87+
true
88+
);
89+
90+
// Enqueue page-specific scripts
91+
if ( strpos( $hook, 'multisite-exporter-history' ) !== false ) {
92+
wp_enqueue_script(
93+
'multisite-exporter-history',
94+
MULTISITE_EXPORTER_PLUGIN_URL . 'js/history-page.js',
95+
array( 'jquery' ),
96+
filemtime( MULTISITE_EXPORTER_PLUGIN_DIR . 'js/history-page.js' ),
97+
true
98+
);
99+
}
100+
}
101+
102+
/**
103+
* Add top-level admin menu for Multisite Exporter.
104+
*/
105+
public function add_admin_menu() {
106+
add_menu_page(
107+
'Multisite Exporter', // Page title
108+
'MS Exporter', // Menu title (shorter for menu space)
109+
'manage_network', // Capability
110+
'multisite-exporter', // Menu slug
111+
array( $this, 'render_main_page' ), // Callback function
112+
'dashicons-download', // Icon (download icon is appropriate for export)
113+
30 // Position (after Comments which is 25)
114+
);
115+
116+
// Add submenu for export history
117+
add_submenu_page(
118+
'multisite-exporter', // Parent slug
119+
'Export History', // Page title
120+
'Export History', // Menu title
121+
'manage_network', // Capability
122+
'multisite-exporter-history', // Menu slug
123+
array( $this, 'render_history_page' ) // Callback function
124+
);
125+
126+
// Add submenu for Action Scheduler admin
127+
add_submenu_page(
128+
'multisite-exporter', // Parent slug
129+
esc_html__( 'Scheduled Actions', 'multisite-exporter' ), // Page title
130+
esc_html__( 'Scheduled Actions', 'multisite-exporter' ), // Menu title
131+
'manage_network', // Capability
132+
'me-scheduled-actions', // Menu slug - unique to avoid conflicts
133+
array( $this, 'render_action_scheduler_page' ) // Callback function
134+
);
135+
}
136+
137+
/**
138+
* Render the main admin page.
139+
*/
140+
public function render_main_page() {
141+
// Add nonce for security
142+
if ( isset( $_POST[ 'me_run' ] ) && check_admin_referer( 'multisite_exporter_action', 'me_nonce' ) ) {
143+
$export_args = [
144+
'content' => sanitize_text_field( $_POST[ 'content' ] ?? 'all' ),
145+
'post_type' => sanitize_text_field( $_POST[ 'post_type' ] ?? '' ),
146+
'start_date' => sanitize_text_field( $_POST[ 'start_date' ] ?? '' ),
147+
'end_date' => sanitize_text_field( $_POST[ 'end_date' ] ?? '' ),
148+
];
149+
150+
$init = ME_Init::instance();
151+
$init->schedule_exports( $export_args );
152+
153+
echo '<div class="updated"><p>' . esc_html__( 'Export has been scheduled for all subsites! View results in the', 'multisite-exporter' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=multisite-exporter-history' ) ) . '">' . esc_html__( 'Export History', 'multisite-exporter' ) . '</a> ' . esc_html__( 'page.', 'multisite-exporter' ) . '</p></div>';
154+
}
155+
156+
// Include the main page view
157+
include_once MULTISITE_EXPORTER_PLUGIN_DIR . 'includes/admin/views/view-main-page.php';
158+
}
159+
160+
/**
161+
* Render the export history page.
162+
*/
163+
public function render_history_page() {
164+
// Include the history page view
165+
include_once MULTISITE_EXPORTER_PLUGIN_DIR . 'includes/admin/views/view-history-page.php';
166+
}
167+
168+
/**
169+
* Display the Action Scheduler admin screen under our own menu
170+
*/
171+
public function render_action_scheduler_page() {
172+
// Include the action scheduler page view
173+
include_once MULTISITE_EXPORTER_PLUGIN_DIR . 'includes/admin/views/view-scheduled-actions-page.php';
174+
}
175+
176+
/**
177+
* Handle export file downloads
178+
*/
179+
public function handle_export_download() {
180+
// Check if user has permission
181+
if ( ! current_user_can( 'manage_network' ) ) {
182+
wp_die( 'You do not have permission to access this file.' );
183+
}
184+
185+
// Verify nonce
186+
$file = isset( $_GET[ 'file' ] ) ? sanitize_text_field( base64_decode( $_GET[ 'file' ] ) ) : '';
187+
if ( ! wp_verify_nonce( $_GET[ 'nonce' ], 'download_export_' . $file ) ) {
188+
wp_die( 'Security check failed.' );
189+
}
190+
191+
// Use the Export class to download the file
192+
$exporter = ME_Export::instance();
193+
$exporter->download_export_file( $file );
194+
}
195+
196+
/**
197+
* Handle the download of multiple selected export files as a zip archive
198+
*/
199+
public function download_selected_exports() {
200+
// Check nonce for security
201+
if ( ! isset( $_POST[ 'me_download_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'me_download_nonce' ], 'download_selected_exports' ) ) {
202+
wp_die( esc_html__( 'Security check failed.', 'multisite-exporter' ) );
203+
}
204+
205+
// Check if we're selecting all exports across all pages
206+
$select_all_pages = isset( $_POST[ 'select_all_pages' ] ) && $_POST[ 'select_all_pages' ] == '1';
207+
208+
// Get selected exports or all exports if select_all_pages is true
209+
$all_exports = get_site_transient( 'multisite_exports' ) ?: array();
210+
211+
if ( $select_all_pages ) {
212+
// Use all available exports
213+
$selected = array_column( $all_exports, 'file_name' );
214+
} else {
215+
// Use only explicitly selected exports
216+
$selected = isset( $_POST[ 'selected_exports' ] ) ? (array) $_POST[ 'selected_exports' ] : array();
217+
}
218+
219+
// Ensure we have selected exports
220+
if ( empty( $selected ) ) {
221+
wp_die( esc_html__( 'No exports selected.', 'multisite-exporter' ) );
222+
}
223+
224+
// If there's only one file selected, just download it directly
225+
if ( count( $selected ) === 1 ) {
226+
$file_name = $selected[ 0 ];
227+
$exporter = ME_Export::instance();
228+
$exporter->download_export_file( $file_name );
229+
exit;
230+
}
231+
232+
// Create a zip file containing all selected exports
233+
$zip = new ZipArchive();
234+
$temp_file = tempnam( sys_get_temp_dir(), 'me_exports_' );
235+
236+
if ( $zip->open( $temp_file, ZipArchive::CREATE ) !== true ) {
237+
wp_die( esc_html__( 'Could not create ZIP file.', 'multisite-exporter' ) );
238+
}
239+
240+
// Get the export directory
241+
$init = ME_Init::instance();
242+
$export_dir = $init->get_export_directory();
243+
244+
$found_files = array();
245+
246+
// Add files to the zip
247+
foreach ( $selected as $file_name ) {
248+
$file_path = trailingslashit( $export_dir ) . $file_name;
249+
250+
if ( file_exists( $file_path ) ) {
251+
$zip->addFile( $file_path, $file_name );
252+
$found_files[] = $file_name;
253+
}
254+
}
255+
256+
// Check if we found all files
257+
if ( count( $found_files ) !== count( $selected ) ) {
258+
$missing = array_diff( $selected, $found_files );
259+
$error_message = sprintf(
260+
/* translators: %s: Comma-separated list of missing files */
261+
esc_html__( 'Could not find some export files: %s', 'multisite-exporter' ),
262+
implode( ', ', $missing )
263+
);
264+
wp_die( $error_message );
265+
}
266+
267+
$zip->close();
268+
269+
// Output the zip file
270+
header( 'Content-Type: application/zip' );
271+
header( 'Content-Disposition: attachment; filename="multisite-exports-' . date( 'Y-m-d' ) . '.zip"' );
272+
header( 'Content-Length: ' . filesize( $temp_file ) );
273+
header( 'Pragma: no-cache' );
274+
header( 'Expires: 0' );
275+
276+
readfile( $temp_file );
277+
unlink( $temp_file );
278+
exit;
279+
}
280+
281+
/**
282+
* Force Action Scheduler query args to only show our hook
283+
*/
284+
public function force_hook_filter( $query_args ) {
285+
$query_args[ 'hook' ] = 'me_process_site_export';
286+
return $query_args;
287+
}
288+
289+
/**
290+
* Filter the SQL query directly if needed
291+
*/
292+
public function filter_as_sql_query( $sql ) {
293+
global $wpdb;
294+
295+
// Only modify the SQL if we're on our custom page
296+
if ( isset( $_GET[ 'page' ] ) && $_GET[ 'page' ] === 'me-scheduled-actions' ) {
297+
// Ensure the SQL contains a WHERE clause for our hook
298+
if ( ! strpos( $sql, "hook = 'me_process_site_export'" ) ) {
299+
// If there's a WHERE clause, add our condition
300+
if ( strpos( $sql, 'WHERE' ) !== false ) {
301+
$sql = str_replace(
302+
'WHERE',
303+
"WHERE (a.hook = 'me_process_site_export') AND ",
304+
$sql
305+
);
306+
} else {
307+
// If no WHERE clause exists, add one
308+
$sql .= " WHERE (a.hook = 'me_process_site_export')";
309+
}
310+
}
311+
}
312+
313+
return $sql;
314+
}
315+
}

0 commit comments

Comments
 (0)