From 1c1980bb22e5870546499661e62c24ddd7040c4b Mon Sep 17 00:00:00 2001 From: Marcus Quinn <6428977+marcusquinn@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:40:09 +0000 Subject: [PATCH] chore: improve workflow names and fix CSS indentation consistency (#18) * fix: resolve plugin class loading reliability issues * fix: address CodeRabbit XSS and accessibility findings from PR #18 - admin/js/admin-scripts.js: replace HTML string interpolation in showNotice with DOM API construction and .text() to prevent XSS; whitelist type values - admin/js/update-source-selector.js: replace .html(message) with .text(message) in showMessage to prevent XSS from AJAX response content - admin/templates/modal.php: add role=dialog, aria-modal=true, aria-labelledby for screen reader semantics; replace close control with
diff --git a/includes/Admin/class-admin.php b/includes/Admin/class-admin.php index 4a12d90..78e2fbe 100644 --- a/includes/Admin/class-admin.php +++ b/includes/Admin/class-admin.php @@ -69,10 +69,12 @@ class Admin { // Get the plugin version. $plugin_version = $this->core->get_plugin_version(); + $plugin_url = $this->get_plugin_base_url(); + // Enqueue styles. \wp_enqueue_style( 'wpst-admin-styles', - plugin_dir_url( dirname( __DIR__ ) ) . 'admin/css/admin-styles.css', + $plugin_url . 'admin/css/admin-styles.css', array(), // Dependencies. $plugin_version // Version. ); @@ -80,7 +82,7 @@ class Admin { // Enqueue admin scripts. \wp_enqueue_script( 'wpst-admin-script', - plugin_dir_url( dirname( __DIR__ ) ) . 'admin/js/admin-scripts.js', + $plugin_url . 'admin/js/admin-scripts.js', array( 'jquery' ), $plugin_version, // Version. true @@ -99,4 +101,21 @@ class Admin { $data ); } + + /** + * Get plugin base URL. + * + * @return string Plugin base URL with trailing slash. + */ + private function get_plugin_base_url(): string { + if ( defined( 'WP_PLUGIN_STARTER_TEMPLATE_URL' ) ) { + return WP_PLUGIN_STARTER_TEMPLATE_URL; + } + + if ( defined( 'WPST_PLUGIN_URL' ) ) { + return WPST_PLUGIN_URL; + } + + return \plugin_dir_url( dirname( __DIR__, 2 ) ); + } } diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index 2a10924..5c18ae0 100644 --- a/tests/phpunit/bootstrap.php +++ b/tests/phpunit/bootstrap.php @@ -46,7 +46,7 @@ if ( getenv( 'WP_PHPUNIT__DIR' ) ) { // Include plugin files needed for tests. require_once WPST_PLUGIN_DIR . 'includes/class-core.php'; require_once WPST_PLUGIN_DIR . 'includes/class-plugin.php'; - if ( file_exists( WPST_PLUGIN_DIR . 'admin/lib/admin.php' ) ) { - require_once WPST_PLUGIN_DIR . 'admin/lib/admin.php'; + if ( file_exists( WPST_PLUGIN_DIR . 'includes/Admin/class-admin.php' ) ) { + require_once WPST_PLUGIN_DIR . 'includes/Admin/class-admin.php'; } } diff --git a/tests/phpunit/test-admin.php b/tests/phpunit/test-admin.php index 1b3a342..a67e2d2 100644 --- a/tests/phpunit/test-admin.php +++ b/tests/phpunit/test-admin.php @@ -101,17 +101,12 @@ class AdminTest extends \WP_Mock\Tools\TestCase { 'return' => 'wp_plugin_starter_template_settings', ]); - // Mock WordPress functions used in the method - WP_Mock::userFunction('plugin_dir_url', [ - 'return' => 'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/', - ]); - // Mock wp_enqueue_style WP_Mock::userFunction('wp_enqueue_style', [ 'times' => 1, 'args' => [ 'wpst-admin-styles', - 'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/../../admin/css/admin-styles.css', + 'http://example.org/wp-content/plugins/wp-plugin-starter-template/admin/css/admin-styles.css', [], '1.0.0', ], @@ -122,7 +117,7 @@ class AdminTest extends \WP_Mock\Tools\TestCase { 'times' => 1, 'args' => [ 'wpst-admin-script', - 'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/../../admin/js/admin-scripts.js', + 'http://example.org/wp-content/plugins/wp-plugin-starter-template/admin/js/admin-scripts.js', ['jquery'], '1.0.0', true, diff --git a/wp-plugin-starter-template.php b/wp-plugin-starter-template.php index f4abd8d..280ab12 100644 --- a/wp-plugin-starter-template.php +++ b/wp-plugin-starter-template.php @@ -55,13 +55,19 @@ spl_autoload_register( // Get the relative class name. $relative_class = substr( $className, $len ); - // Convert namespace to path. - $file = WP_PLUGIN_STARTER_TEMPLATE_PATH . 'includes/' . str_replace( '\\', '/', $relative_class ) . '.php'; + // Build class file path using WordPress-style class file names. + $relative_path = str_replace( '\\', '/', $relative_class ); + $path_parts = explode( '/', $relative_path ); + $class_name = array_pop( $path_parts ); + $directory = ''; - // Convert class name format to file name format. - $file = str_replace( 'class-', '', $file ); - $file = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $file ); - $file = strtolower( $file ); + if ( ! empty( $path_parts ) ) { + $directory = implode( '/', $path_parts ) . '/'; + } + + $class_file = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $class_name ); + $class_file = 'class-' . strtolower( $class_file ) . '.php'; + $file = WP_PLUGIN_STARTER_TEMPLATE_PATH . 'includes/' . $directory . $class_file; // If the file exists, require it. if ( file_exists( $file ) ) {