From b2be2b83233e1c8a4689842871ef72a007909b7d Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Tue, 1 Jul 2025 22:43:51 +0200 Subject: [PATCH 01/54] wip --- src/php/class-db.php | 10 +++ src/php/class-plugin.php | 4 + src/php/class-snippet-files.php | 125 ++++++++++++++++++++++++++++++++ src/php/class-snippet.php | 2 +- src/php/load.php | 3 +- src/php/snippet-ops.php | 58 ++++++++++++++- 6 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 src/php/class-snippet-files.php diff --git a/src/php/class-db.php b/src/php/class-db.php index 64ea5f47..a0b50782 100644 --- a/src/php/class-db.php +++ b/src/php/class-db.php @@ -283,4 +283,14 @@ function ( $snippet ) use ( $active_shared_ids ) { return $active_snippets; } + + public function get_active_tables(): array { + $active_tables = array( $this->table ); + + if ( is_multisite() ) { + $active_tables[] = $this->ms_table; + } + + return $active_tables; + } } diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index 92bebfa3..32a679db 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -119,6 +119,10 @@ public function load_plugin() { // Cloud List Table shared functions. require_once $includes_path . '/cloud/list-table-shared-ops.php'; + // Snippet files. + require_once $includes_path . '/class-snippet-files.php'; + ( new Snippet_Files() )->register_hooks(); + $this->active_snippets = new Active_Snippets(); $this->front_end = new Front_End(); $this->cloud_api = new Cloud_API(); diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php new file mode 100644 index 00000000..f319b5ee --- /dev/null +++ b/src/php/class-snippet-files.php @@ -0,0 +1,125 @@ +get_type() ) { + return; + } + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + + if ( ! is_dir( $base_dir ) ) { + wp_mkdir_p( $base_dir ); + } + + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + + if ( $snippet->active ) { + $content = "code; + + file_put_contents( $file_path, $content ); + } else { + @unlink( $file_path ); + } + + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + + if ( $snippet->active ) { + $active_snippets[ $snippet->id ] = $snippet->get_fields(); + } else { + unset( $active_snippets[ $snippet->id ] ); + } + + $index_content = "get_type() ) { + return; + } + + $table = code_snippets()->db->get_table_name( $network ); + + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + + @unlink( $file_path ); + + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + + unset( $active_snippets[ $snippet_id ] ); + + $index_content = "get_type() ) { + return; + } + + $table = code_snippets()->db->get_table_name( $network ); + + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + + $content = "code; + + file_put_contents( $file_path, $content ); + + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + + $active_snippets[ $snippet->id ] = $snippet->get_fields(); + + $index_content = "get_type() ) { + return; + } + + $table = code_snippets()->db->get_table_name( $network ); + + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + + @unlink( $file_path ); + + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + + unset( $active_snippets[ $snippet_id ] ); + + $index_content = "scope, -4 ) ) { return 'css'; } elseif ( '-js' === substr( $this->scope, -3 ) ) { diff --git a/src/php/load.php b/src/php/load.php index 2de59ce2..8700186a 100644 --- a/src/php/load.php +++ b/src/php/load.php @@ -65,4 +65,5 @@ function code_snippets(): Plugin { code_snippets()->load_plugin(); // Execute the snippets once the plugins are loaded. -add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets', 1 ); +// add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets', 1 ); +add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets_from_flat_files', 1 ); diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 2e3a9023..fd8c391e 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -314,7 +314,7 @@ function activate_snippet( int $id, ?bool $network = null ) { } update_shared_network_snippets( [ $snippet ] ); - do_action( 'code_snippets/activate_snippet', $snippet ); + do_action( 'code_snippets/activate_snippet', $snippet, $network ); clean_snippets_cache( $table_name ); return $snippet; } @@ -392,7 +392,7 @@ function deactivate_snippet( int $id, ?bool $network = null ): ?Snippet { $network = DB::validate_network_param( $network ); $table = code_snippets()->db->get_table_name( $network ); - // Set the snippet to active. + // Set the snippet to inactive. $result = $wpdb->update( $table, array( 'active' => '0' ), @@ -433,6 +433,8 @@ function delete_snippet( int $id, ?bool $network = null ): bool { $network = DB::validate_network_param( $network ); $table = code_snippets()->db->get_table_name( $network ); + $snippet = get_snippet( $id, $network ); + $result = $wpdb->delete( $table, array( 'id' => $id ), @@ -440,7 +442,7 @@ function delete_snippet( int $id, ?bool $network = null ): bool { ); if ( $result ) { - do_action( 'code_snippets/delete_snippet', $id, $network ); + do_action( 'code_snippets/delete_snippet', $snippet, $network ); clean_snippets_cache( $table ); code_snippets()->cloud_api->delete_snippet_from_transient_data( $id ); } @@ -670,6 +672,56 @@ function execute_active_snippets(): bool { return true; } +function execute_active_snippets_from_flat_files(): bool { + $tables = code_snippets()->db->get_active_tables(); + $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); + + foreach ( $tables as $table ) { + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + + if ( ! is_dir( $base_dir ) ) { + continue; + } + + $active_snippets_file_path = $base_dir . '/index.php'; + if ( ! is_file( $active_snippets_file_path ) ) { + continue; + } + + $active_snippets = require $active_snippets_file_path; + $sorted_snippets = sort_by_priority( $active_snippets ); + + foreach ( $sorted_snippets as $snippet_id => $snippet_data ) { + if ( ! in_array( $snippet_data['scope'], $scopes, true ) ) { + continue; + } + + $file = $base_dir . '/' . $snippet_id . '.php'; + execute_snippet_from_flat_file( $file, $snippet_id ); + } + } + + return true; +} + +function sort_by_priority( array $snippets ): array { + uasort( $snippets, function ( $a, $b ) { + return $a['priority'] <=> $b['priority']; + } ); + + return $snippets; +} + +function execute_snippet_from_flat_file( $file, int $id = 0, bool $force = false ) { + if ( ! is_file( $file ) || ( ! $force && defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) ) { + return false; + } + + require_once $file; + + do_action( 'code_snippets/after_execute_snippet_from_flat_file', $file, $id ); +} + /** * Retrieve a single snippets from the database using its cloud ID. * From 510e49e69da1ff6eb0c384469d97e09d46dcba84 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 09:15:12 +0200 Subject: [PATCH 02/54] wip --- src/php/class-snippet-files.php | 154 ++++++++++++++++---------------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index f319b5ee..00e2f375 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -4,122 +4,120 @@ class Snippet_Files { - public function register_hooks() { - add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); - add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); - add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); - } + public function register_hooks() { + add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); + add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); + add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + } - public function handle_snippet( $snippet, $table ) { - if ( 'php' !== $snippet->get_type() ) { - return; - } - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + public function handle_snippet( $snippet, $table ) { + if ( 'php' !== $snippet->get_type() ) { + return; + } + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - if ( ! is_dir( $base_dir ) ) { - wp_mkdir_p( $base_dir ); - } + if ( ! is_dir( $base_dir ) ) { + wp_mkdir_p( $base_dir ); + } - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; - if ( $snippet->active ) { - $content = "code; + if ( $snippet->active ) { + $content = "code; - file_put_contents( $file_path, $content ); - } else { - @unlink( $file_path ); - } + file_put_contents( $file_path, $content ); + } else { + @unlink( $file_path ); + } - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; - if ( $snippet->active ) { - $active_snippets[ $snippet->id ] = $snippet->get_fields(); - } else { - unset( $active_snippets[ $snippet->id ] ); - } - - $index_content = "active ) { + $active_snippets[ $snippet->id ] = $snippet->get_fields(); + } else { + unset( $active_snippets[ $snippet->id ] ); + } - public function delete_snippet( $snippet, $network ) { - if ( 'php' !== $snippet->get_type() ) { - return; - } + $index_content = "db->get_table_name( $network ); + file_put_contents( $index_file_path, $index_content ); + } - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + public function delete_snippet( $snippet, $network ) { + if ( 'php' !== $snippet->get_type() ) { + return; + } - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $table = code_snippets()->db->get_table_name( $network ); - @unlink( $file_path ); + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + @unlink( $file_path ); - unset( $active_snippets[ $snippet_id ] ); + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - $index_content = "get_type() ) { - return; - } + $index_content = "db->get_table_name( $network ); + file_put_contents( $index_file_path, $index_content ); + } - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + public function activate_snippet( $snippet, $network ) { + if ( 'php' !== $snippet->get_type() ) { + return; + } - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $table = code_snippets()->db->get_table_name( $network ); - $content = "code; + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - file_put_contents( $file_path, $content ); + $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + $content = "code; - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + file_put_contents( $file_path, $content ); - $active_snippets[ $snippet->id ] = $snippet->get_fields(); + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - $index_content = "id ] = $snippet->get_fields(); - public function deactivate_snippet( $snippet_id, $network ) { - $snippet = get_snippet( $snippet_id, $network ); + $index_content = "get_type() ) { - return; - } + file_put_contents( $index_file_path, $index_content ); + } - $table = code_snippets()->db->get_table_name( $network ); + public function deactivate_snippet( $snippet_id, $network ) { + $snippet = get_snippet( $snippet_id, $network ); - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + if ( 'php' !== $snippet->get_type() ) { + return; + } - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $network; - @unlink( $file_path ); + $file_path = trailingslashit( $base_dir ) . $snippet_id . '.php'; - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + @unlink( $file_path ); - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - unset( $active_snippets[ $snippet_id ] ); + $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; - $index_content = " Date: Wed, 2 Jul 2025 09:29:04 +0200 Subject: [PATCH 03/54] wip --- src/php/class-snippet-files.php | 166 +++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 58 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 00e2f375..d81572a9 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -4,6 +4,29 @@ class Snippet_Files { + /** + * Holds the WP_Filesystem instance. + * + * @var \WP_Filesystem_Base + */ + private $fs; + + public function __construct() { + $this->init_filesystem(); + } + + /** + * Initialize WP_Filesystem. + */ + private function init_filesystem() { + if ( ! function_exists( 'WP_Filesystem' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + WP_Filesystem(); + global $wp_filesystem; + $this->fs = $wp_filesystem; + } + public function register_hooks() { add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); @@ -16,35 +39,19 @@ public function handle_snippet( $snippet, $table ) { if ( 'php' !== $snippet->get_type() ) { return; } - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - if ( ! is_dir( $base_dir ) ) { - wp_mkdir_p( $base_dir ); - } + $base_dir = $this->get_base_dir( $table ); + $this->maybe_create_directory( $base_dir ); - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); if ( $snippet->active ) { - $content = "code; - - file_put_contents( $file_path, $content ); + $this->write_snippet_file( $file_path, $snippet->code ); } else { - @unlink( $file_path ); + $this->delete_file( $file_path ); } - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; - - if ( $snippet->active ) { - $active_snippets[ $snippet->id ] = $snippet->get_fields(); - } else { - unset( $active_snippets[ $snippet->id ] ); - } - - $index_content = "update_index_file( $base_dir, $snippet, $snippet->active ); } public function delete_snippet( $snippet, $network ) { @@ -53,22 +60,12 @@ public function delete_snippet( $snippet, $network ) { } $table = code_snippets()->db->get_table_name( $network ); + $base_dir = $this->get_base_dir( $table ); - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; - - @unlink( $file_path ); + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); + $this->delete_file( $file_path ); - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; - - unset( $active_snippets[ $snippet_id ] ); - - $index_content = "update_index_file( $base_dir, $snippet, false ); } public function activate_snippet( $snippet, $network ) { @@ -77,47 +74,100 @@ public function activate_snippet( $snippet, $network ) { } $table = code_snippets()->db->get_table_name( $network ); + $base_dir = $this->get_base_dir( $table ); - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; - - $file_path = trailingslashit( $base_dir ) . $snippet->id . '.php'; + $this->maybe_create_directory( $base_dir ); - $content = "code; + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); + $this->write_snippet_file( $file_path, $snippet->code ); - file_put_contents( $file_path, $content ); + $this->update_index_file( $base_dir, $snippet, true ); + } - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + public function deactivate_snippet( $snippet_id, $network ) { + $snippet = get_snippet( $snippet_id, $network ); - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + if ( ! $snippet || 'php' !== $snippet->get_type() ) { + return; + } - $active_snippets[ $snippet->id ] = $snippet->get_fields(); + $table = code_snippets()->db->get_table_name( $network ); + $base_dir = $this->get_base_dir( $table ); - $index_content = "get_snippet_file_path( $base_dir, $snippet_id ); + $this->delete_file( $file_path ); - file_put_contents( $index_file_path, $index_content ); + $this->update_index_file( $base_dir, $snippet, false ); } - public function deactivate_snippet( $snippet_id, $network ) { - $snippet = get_snippet( $snippet_id, $network ); + /** + * Returns the base directory path for a given table. + */ + private function get_base_dir( $table ) { + return WP_CONTENT_DIR . '/code-snippets/' . $table; + } - if ( 'php' !== $snippet->get_type() ) { - return; + /** + * Creates the directory if it does not exist. + */ + private function maybe_create_directory( $dir ) { + if ( ! $this->fs->is_dir( $dir ) ) { + $this->fs->mkdir( $dir, FS_CHMOD_DIR ); } + } - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $network; + /** + * Returns the path to the snippet PHP file. + */ + private function get_snippet_file_path( $base_dir, $snippet_id ) { + return trailingslashit( $base_dir ) . $snippet_id . '.php'; + } - $file_path = trailingslashit( $base_dir ) . $snippet_id . '.php'; + /** + * Writes the snippet code to a file, with the required header. + */ + private function write_snippet_file( $file_path, $code ) { + $content = "fs->put_contents( $file_path, $content, FS_CHMOD_FILE ); + } - @unlink( $file_path ); + /** + * Deletes a file if it exists. + */ + private function delete_file( $file_path ) { + if ( $this->fs->exists( $file_path ) ) { + $this->fs->delete( $file_path ); + } + } - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + /** + * Loads the index.php array by requiring it directly. + */ + private function load_index_file( $index_file_path ) { + return is_file( $index_file_path ) ? require $index_file_path : []; + } - $active_snippets = is_file( $index_file_path ) ? require $index_file_path : []; + /** + * Saves the index.php file via WP_Filesystem. + */ + private function save_index_file( $index_file_path, $active_snippets ) { + $index_content = "fs->put_contents( $index_file_path, $index_content, FS_CHMOD_FILE ); + } - unset( $active_snippets[ $snippet_id ] ); + /** + * Updates the index.php file by adding or removing a snippet. + */ + private function update_index_file( $base_dir, $snippet, $active ) { + $index_file_path = trailingslashit( $base_dir ) . 'index.php'; + $active_snippets = $this->load_index_file( $index_file_path ); - $index_content = "id ] = $snippet->get_fields(); + } else { + unset( $active_snippets[ $snippet->id ] ); + } - file_put_contents( $index_file_path, $index_content ); + $this->save_index_file( $index_file_path, $active_snippets ); } } From f65f5bc6f325392b0de9b75054d5afdb88a5ad57 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 09:43:56 +0200 Subject: [PATCH 04/54] wip --- src/php/class-plugin.php | 2 +- src/php/class-snippet-files.php | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index 32a679db..fda65d6f 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -121,7 +121,7 @@ public function load_plugin() { // Snippet files. require_once $includes_path . '/class-snippet-files.php'; - ( new Snippet_Files() )->register_hooks(); + ( new Snippet_Files() )->init(); $this->active_snippets = new Active_Snippets(); $this->front_end = new Front_End(); diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index d81572a9..4cb1b9b5 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -11,20 +11,9 @@ class Snippet_Files { */ private $fs; - public function __construct() { - $this->init_filesystem(); - } - - /** - * Initialize WP_Filesystem. - */ - private function init_filesystem() { - if ( ! function_exists( 'WP_Filesystem' ) ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - } - WP_Filesystem(); - global $wp_filesystem; - $this->fs = $wp_filesystem; + public function init() { + $this->ensure_filesystem(); + $this->register_hooks(); } public function register_hooks() { @@ -35,6 +24,17 @@ public function register_hooks() { add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); } + private function ensure_filesystem() { + if ( ! $this->fs ) { + if ( ! function_exists( 'WP_Filesystem' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + WP_Filesystem(); + global $wp_filesystem; + $this->fs = $wp_filesystem; + } + } + public function handle_snippet( $snippet, $table ) { if ( 'php' !== $snippet->get_type() ) { return; From c7f3d3cb49245f2fc1fd56e3b6b3204f8219f5d2 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 09:46:42 +0200 Subject: [PATCH 05/54] wip --- src/php/snippet-ops.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index fd8c391e..5927ff7b 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -689,7 +689,7 @@ function execute_active_snippets_from_flat_files(): bool { } $active_snippets = require $active_snippets_file_path; - $sorted_snippets = sort_by_priority( $active_snippets ); + $sorted_snippets = cs_sort_snippets_by_priority( $active_snippets ); foreach ( $sorted_snippets as $snippet_id => $snippet_data ) { if ( ! in_array( $snippet_data['scope'], $scopes, true ) ) { @@ -704,7 +704,7 @@ function execute_active_snippets_from_flat_files(): bool { return true; } -function sort_by_priority( array $snippets ): array { +function cs_sort_snippets_by_priority( array $snippets ): array { uasort( $snippets, function ( $a, $b ) { return $a['priority'] <=> $b['priority']; } ); From 29a540c1b29a1d4c1017de3923445d511a1b5cb0 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 09:58:00 +0200 Subject: [PATCH 06/54] wip --- src/php/class-snippet-files.php | 24 ++++++++++++++---------- src/php/snippet-ops.php | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 4cb1b9b5..bee41b59 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -36,11 +36,12 @@ private function ensure_filesystem() { } public function handle_snippet( $snippet, $table ) { - if ( 'php' !== $snippet->get_type() ) { + $snippet_type = $snippet->get_type(); + if ( 'php' !== $snippet_type ) { return; } - $base_dir = $this->get_base_dir( $table ); + $base_dir = $this->get_base_dir( $table, $snippet_type ); $this->maybe_create_directory( $base_dir ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); @@ -55,12 +56,13 @@ public function handle_snippet( $snippet, $table ) { } public function delete_snippet( $snippet, $network ) { - if ( 'php' !== $snippet->get_type() ) { + $snippet_type = $snippet->get_type(); + if ( 'php' !== $snippet_type ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table ); + $base_dir = $this->get_base_dir( $table, $snippet_type ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->delete_file( $file_path ); @@ -69,12 +71,13 @@ public function delete_snippet( $snippet, $network ) { } public function activate_snippet( $snippet, $network ) { - if ( 'php' !== $snippet->get_type() ) { + $snippet_type = $snippet->get_type(); + if ( 'php' !== $snippet_type ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table ); + $base_dir = $this->get_base_dir( $table, $snippet_type ); $this->maybe_create_directory( $base_dir ); @@ -86,13 +89,14 @@ public function activate_snippet( $snippet, $network ) { public function deactivate_snippet( $snippet_id, $network ) { $snippet = get_snippet( $snippet_id, $network ); + $snippet_type = $snippet->get_type(); - if ( ! $snippet || 'php' !== $snippet->get_type() ) { + if ( ! $snippet || 'php' !== $snippet_type ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table ); + $base_dir = $this->get_base_dir( $table, $snippet_type ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet_id ); $this->delete_file( $file_path ); @@ -103,8 +107,8 @@ public function deactivate_snippet( $snippet_id, $network ) { /** * Returns the base directory path for a given table. */ - private function get_base_dir( $table ) { - return WP_CONTENT_DIR . '/code-snippets/' . $table; + private function get_base_dir( $table, $snippet_type ) { + return WP_CONTENT_DIR . '/code-snippets/' . $table . '/' . $snippet_type; } /** diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 5927ff7b..7cf791bd 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -677,7 +677,7 @@ function execute_active_snippets_from_flat_files(): bool { $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); foreach ( $tables as $table ) { - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table; + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table . '/php'; if ( ! is_dir( $base_dir ) ) { continue; From c47c344cc004ddac7d961b7d59833040b7c81563 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 10:01:54 +0200 Subject: [PATCH 07/54] wip --- src/php/class-snippet-files.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index bee41b59..bd313cde 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -36,7 +36,7 @@ private function ensure_filesystem() { } public function handle_snippet( $snippet, $table ) { - $snippet_type = $snippet->get_type(); + $snippet_type = $snippet->get_type(); if ( 'php' !== $snippet_type ) { return; } @@ -56,7 +56,7 @@ public function handle_snippet( $snippet, $table ) { } public function delete_snippet( $snippet, $network ) { - $snippet_type = $snippet->get_type(); + $snippet_type = $snippet->get_type(); if ( 'php' !== $snippet_type ) { return; } @@ -71,7 +71,7 @@ public function delete_snippet( $snippet, $network ) { } public function activate_snippet( $snippet, $network ) { - $snippet_type = $snippet->get_type(); + $snippet_type = $snippet->get_type(); if ( 'php' !== $snippet_type ) { return; } @@ -89,7 +89,7 @@ public function activate_snippet( $snippet, $network ) { public function deactivate_snippet( $snippet_id, $network ) { $snippet = get_snippet( $snippet_id, $network ); - $snippet_type = $snippet->get_type(); + $snippet_type = $snippet->get_type(); if ( ! $snippet || 'php' !== $snippet_type ) { return; From b66db45dde19a4092bac879ebfc90460acc8fcd9 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 10:46:05 +0200 Subject: [PATCH 08/54] wip --- src/php/class-snippet-files.php | 23 +++++++++++++++-------- src/php/front-end/class-front-end.php | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index bd313cde..6fdadfcb 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -37,7 +37,7 @@ private function ensure_filesystem() { public function handle_snippet( $snippet, $table ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type ) { + if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { return; } @@ -47,7 +47,7 @@ public function handle_snippet( $snippet, $table ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); if ( $snippet->active ) { - $this->write_snippet_file( $file_path, $snippet->code ); + $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); } else { $this->delete_file( $file_path ); } @@ -57,7 +57,7 @@ public function handle_snippet( $snippet, $table ) { public function delete_snippet( $snippet, $network ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type ) { + if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { return; } @@ -72,7 +72,7 @@ public function delete_snippet( $snippet, $network ) { public function activate_snippet( $snippet, $network ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type ) { + if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { return; } @@ -82,7 +82,7 @@ public function activate_snippet( $snippet, $network ) { $this->maybe_create_directory( $base_dir ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); - $this->write_snippet_file( $file_path, $snippet->code ); + $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); $this->update_index_file( $base_dir, $snippet, true ); } @@ -91,7 +91,7 @@ public function deactivate_snippet( $snippet_id, $network ) { $snippet = get_snippet( $snippet_id, $network ); $snippet_type = $snippet->get_type(); - if ( ! $snippet || 'php' !== $snippet_type ) { + if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { return; } @@ -130,8 +130,15 @@ private function get_snippet_file_path( $base_dir, $snippet_id ) { /** * Writes the snippet code to a file, with the required header. */ - private function write_snippet_file( $file_path, $code ) { - $content = "\n\n"; + } + + $content .= $code; + $this->fs->put_contents( $file_path, $content, FS_CHMOD_FILE ); } diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 8d76ae8a..60429b35 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -245,6 +245,16 @@ protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): return $snippet->code; } + $network = DB::validate_network_param( $snippet->network ); + $table_name = code_snippets()->db->get_table_name( $network ); + $filepath = WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/html/' . $snippet->id . '.php'; + + return file_exists( $filepath ) + ? $this->evaluate_shortcode_from_flat_file( $filepath, $atts ) + : $this->evaluate_shortcode_from_db( $snippet, $atts ); + } + + private function evaluate_shortcode_from_db( Snippet $snippet, array $atts ): string { /** * Avoiding extract is typically recommended, however in this situation we want to make it easy for snippet * authors to use custom attributes. @@ -259,6 +269,23 @@ protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): return ob_get_clean(); } + private function evaluate_shortcode_from_flat_file( $filepath, array $atts ): string { + ob_start(); + + ( function( $atts ) use ( $filepath ) { + /** + * Avoiding extract is typically recommended, however in this situation we want to make it easy for snippet + * authors to use custom attributes. + * + * @phpcs:disable WordPress.PHP.DontExtract.extract_extract + */ + extract( $atts ); + require_once $filepath; + } )( $atts ); + + return ob_get_clean(); + } + /** * Render the value of a content shortcode * From 55561ca4d233bcfe94dd66f10de11adc057c984e Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 10:50:19 +0200 Subject: [PATCH 09/54] wip --- src/php/class-snippet-files.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 6fdadfcb..92d18ab4 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -52,7 +52,7 @@ public function handle_snippet( $snippet, $table ) { $this->delete_file( $file_path ); } - $this->update_index_file( $base_dir, $snippet, $snippet->active ); + $this->update_config_file( $base_dir, $snippet, $snippet->active ); } public function delete_snippet( $snippet, $network ) { @@ -67,7 +67,7 @@ public function delete_snippet( $snippet, $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->delete_file( $file_path ); - $this->update_index_file( $base_dir, $snippet, false ); + $this->update_config_file( $base_dir, $snippet, false ); } public function activate_snippet( $snippet, $network ) { @@ -84,7 +84,7 @@ public function activate_snippet( $snippet, $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); - $this->update_index_file( $base_dir, $snippet, true ); + $this->update_config_file( $base_dir, $snippet, true ); } public function deactivate_snippet( $snippet_id, $network ) { @@ -101,7 +101,7 @@ public function deactivate_snippet( $snippet_id, $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet_id ); $this->delete_file( $file_path ); - $this->update_index_file( $base_dir, $snippet, false ); + $this->update_config_file( $base_dir, $snippet, false ); } /** @@ -133,9 +133,9 @@ private function get_snippet_file_path( $base_dir, $snippet_id ) { private function write_snippet_file( $file_path, $code, $snippet_type ) { $content = "\n\n"; - } + if ( 'html' === $snippet_type ) { + $content .= "?>\n\n"; + } $content .= $code; @@ -154,24 +154,24 @@ private function delete_file( $file_path ) { /** * Loads the index.php array by requiring it directly. */ - private function load_index_file( $index_file_path ) { - return is_file( $index_file_path ) ? require $index_file_path : []; + private function load_config_file( $config_file_path ) { + return is_file( $config_file_path ) ? require $config_file_path : []; } /** * Saves the index.php file via WP_Filesystem. */ - private function save_index_file( $index_file_path, $active_snippets ) { + private function save_config_file( $config_file_path, $active_snippets ) { $index_content = "fs->put_contents( $index_file_path, $index_content, FS_CHMOD_FILE ); + $this->fs->put_contents( $config_file_path, $index_content, FS_CHMOD_FILE ); } /** * Updates the index.php file by adding or removing a snippet. */ - private function update_index_file( $base_dir, $snippet, $active ) { - $index_file_path = trailingslashit( $base_dir ) . 'index.php'; - $active_snippets = $this->load_index_file( $index_file_path ); + private function update_config_file( $base_dir, $snippet, $active ) { + $config_file_path = trailingslashit( $base_dir ) . 'index.php'; + $active_snippets = $this->load_config_file( $config_file_path ); if ( $active ) { $active_snippets[ $snippet->id ] = $snippet->get_fields(); @@ -179,6 +179,6 @@ private function update_index_file( $base_dir, $snippet, $active ) { unset( $active_snippets[ $snippet->id ] ); } - $this->save_index_file( $index_file_path, $active_snippets ); + $this->save_config_file( $config_file_path, $active_snippets ); } } From f2720ace011c9edd529f4e63cea633d699039910 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 12:39:17 +0200 Subject: [PATCH 10/54] wip --- src/php/class-snippet-files.php | 51 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 92d18ab4..1040df23 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -11,17 +11,19 @@ class Snippet_Files { */ private $fs; + const TYPES_TO_HANDLE = [ 'php', 'html' ]; + public function init() { $this->ensure_filesystem(); $this->register_hooks(); } public function register_hooks() { - add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); - add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); - add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + add_action( 'code_snippets/create_snippet', array( $this, 'handle_snippet' ), 10, 2 ); + add_action( 'code_snippets/update_snippet', array( $this, 'handle_snippet' ), 10, 2 ); + add_action( 'code_snippets/delete_snippet', array( $this, 'delete_snippet' ), 10, 2 ); + add_action( 'code_snippets/activate_snippet', array( $this, 'activate_snippet' ), 10, 2 ); + add_action( 'code_snippets/deactivate_snippet', array( $this, 'deactivate_snippet' ), 10, 2 ); } private function ensure_filesystem() { @@ -29,15 +31,22 @@ private function ensure_filesystem() { if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } + WP_Filesystem(); + global $wp_filesystem; + $this->fs = $wp_filesystem; } } - public function handle_snippet( $snippet, $table ) { + private function should_handle_snippet( string $snippet_type ) { + return in_array( $snippet_type, self::TYPES_TO_HANDLE, true ); + } + + public function handle_snippet( Snippet $snippet, string $table ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { + if ( ! $this->should_handle_snippet( $snippet_type ) ) { return; } @@ -55,9 +64,9 @@ public function handle_snippet( $snippet, $table ) { $this->update_config_file( $base_dir, $snippet, $snippet->active ); } - public function delete_snippet( $snippet, $network ) { + public function delete_snippet( Snippet $snippet, bool $network ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { + if ( ! $this->should_handle_snippet( $snippet_type ) ) { return; } @@ -70,9 +79,9 @@ public function delete_snippet( $snippet, $network ) { $this->update_config_file( $base_dir, $snippet, false ); } - public function activate_snippet( $snippet, $network ) { + public function activate_snippet( Snippet $snippet, bool $network ) { $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { + if ( ! $this->should_handle_snippet( $snippet_type ) ) { return; } @@ -87,11 +96,11 @@ public function activate_snippet( $snippet, $network ) { $this->update_config_file( $base_dir, $snippet, true ); } - public function deactivate_snippet( $snippet_id, $network ) { + public function deactivate_snippet( int $snippet_id, bool $network ) { $snippet = get_snippet( $snippet_id, $network ); $snippet_type = $snippet->get_type(); - if ( 'php' !== $snippet_type && 'html' !== $snippet_type ) { + if ( ! $this->should_handle_snippet( $snippet_type ) ) { return; } @@ -107,14 +116,14 @@ public function deactivate_snippet( $snippet_id, $network ) { /** * Returns the base directory path for a given table. */ - private function get_base_dir( $table, $snippet_type ) { + private function get_base_dir( string $table, string $snippet_type ) { return WP_CONTENT_DIR . '/code-snippets/' . $table . '/' . $snippet_type; } /** * Creates the directory if it does not exist. */ - private function maybe_create_directory( $dir ) { + private function maybe_create_directory( string $dir ) { if ( ! $this->fs->is_dir( $dir ) ) { $this->fs->mkdir( $dir, FS_CHMOD_DIR ); } @@ -123,14 +132,14 @@ private function maybe_create_directory( $dir ) { /** * Returns the path to the snippet PHP file. */ - private function get_snippet_file_path( $base_dir, $snippet_id ) { + private function get_snippet_file_path( string $base_dir, int $snippet_id ) { return trailingslashit( $base_dir ) . $snippet_id . '.php'; } /** * Writes the snippet code to a file, with the required header. */ - private function write_snippet_file( $file_path, $code, $snippet_type ) { + private function write_snippet_file( string $file_path, string $code, string $snippet_type ) { $content = "fs->exists( $file_path ) ) { $this->fs->delete( $file_path ); } @@ -154,14 +163,14 @@ private function delete_file( $file_path ) { /** * Loads the index.php array by requiring it directly. */ - private function load_config_file( $config_file_path ) { + private function load_config_file( string $config_file_path ) { return is_file( $config_file_path ) ? require $config_file_path : []; } /** * Saves the index.php file via WP_Filesystem. */ - private function save_config_file( $config_file_path, $active_snippets ) { + private function save_config_file( string $config_file_path, array $active_snippets ) { $index_content = "fs->put_contents( $config_file_path, $index_content, FS_CHMOD_FILE ); } @@ -169,7 +178,7 @@ private function save_config_file( $config_file_path, $active_snippets ) { /** * Updates the index.php file by adding or removing a snippet. */ - private function update_config_file( $base_dir, $snippet, $active ) { + private function update_config_file( string $base_dir, Snippet $snippet, bool $active ) { $config_file_path = trailingslashit( $base_dir ) . 'index.php'; $active_snippets = $this->load_config_file( $config_file_path ); From 6477f86889b33c2b2756b5570c6aa11ac283ce9e Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 13:01:40 +0200 Subject: [PATCH 11/54] wip --- src/php/snippet-ops.php | 68 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 7cf791bd..d6200091 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -659,6 +659,9 @@ function execute_active_snippets(): bool { array( '%d' ) ); clean_snippets_cache( $table_name ); + + $network = $table_name === $db->ms_table; + do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); } } @@ -672,12 +675,38 @@ function execute_active_snippets(): bool { return true; } +/** + * Execute the active snippets from the flat files. + * Read-write-execute operation. + * + * @return bool true on success, false on failure. + */ function execute_active_snippets_from_flat_files(): bool { - $tables = code_snippets()->db->get_active_tables(); + $db = code_snippets()->db; + $tables = $db->get_active_tables(); $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); - foreach ( $tables as $table ) { - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table . '/php'; + // Detect if a snippet is currently being edited, and if so, spare it from execution. + $edit_id = 0; + $edit_table = $db->table; + + if ( wp_is_json_request() && ! empty( $_SERVER['REQUEST_URI'] ) ) { + $url = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); + + if ( isset( $url['path'] ) && false !== strpos( $url['path'], Snippets_REST_Controller::get_prefixed_base_route() ) ) { + $path_parts = explode( '/', $url['path'] ); + $edit_id = intval( end( $path_parts ) ); + + if ( ! empty( $url['query'] ) ) { + wp_parse_str( $url['query'], $path_params ); + $edit_table = isset( $path_params['network'] ) && rest_sanitize_boolean( $path_params['network'] ) ? + $db->ms_table : $db->table; + } + } + } + + foreach ( $tables as $table_name ) { + $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/php'; if ( ! is_dir( $base_dir ) ) { continue; @@ -691,13 +720,40 @@ function execute_active_snippets_from_flat_files(): bool { $active_snippets = require $active_snippets_file_path; $sorted_snippets = cs_sort_snippets_by_priority( $active_snippets ); - foreach ( $sorted_snippets as $snippet_id => $snippet_data ) { - if ( ! in_array( $snippet_data['scope'], $scopes, true ) ) { + foreach ( $sorted_snippets as $snippet_id => $snippet ) { + if ( ! in_array( $snippet['scope'], $scopes, true ) ) { continue; } + // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. + if ( 'single-use' === $snippet['scope'] ) { + $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); + + if ( $table_name === $db->ms_table && is_array( $active_shared_ids ) && in_array( $snippet_id, $active_shared_ids, true ) ) { + unset( $active_shared_ids[ array_search( $snippet_id, $active_shared_ids, true ) ] ); + $active_shared_ids = array_values( $active_shared_ids ); + update_option( 'active_shared_network_snippets', $active_shared_ids ); + clean_active_snippets_cache( $table_name ); + } else { + $wpdb->update( + $table_name, + array( 'active' => '0' ), + array( 'id' => $snippet_id ), + array( '%d' ), + array( '%d' ) + ); + clean_snippets_cache( $table_name ); + + $network = $table_name === $db->ms_table; + do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); + } + } + $file = $base_dir . '/' . $snippet_id . '.php'; - execute_snippet_from_flat_file( $file, $snippet_id ); + if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && + ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { + execute_snippet_from_flat_file( $file, $snippet_id ); + } } } From 0eda7c915b2a0e4747954afd34df5ebc77fef6e0 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 13:09:46 +0200 Subject: [PATCH 12/54] wip --- src/php/class-snippet-files.php | 10 +++++----- src/php/snippet-ops.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 1040df23..cf3b7302 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -50,7 +50,7 @@ public function handle_snippet( Snippet $snippet, string $table ) { return; } - $base_dir = $this->get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $snippet_type ); $this->maybe_create_directory( $base_dir ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); @@ -71,7 +71,7 @@ public function delete_snippet( Snippet $snippet, bool $network ) { } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $snippet_type ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->delete_file( $file_path ); @@ -86,7 +86,7 @@ public function activate_snippet( Snippet $snippet, bool $network ) { } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $snippet_type ); $this->maybe_create_directory( $base_dir ); @@ -105,7 +105,7 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = $this->get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $snippet_type ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet_id ); $this->delete_file( $file_path ); @@ -116,7 +116,7 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { /** * Returns the base directory path for a given table. */ - private function get_base_dir( string $table, string $snippet_type ) { + public static function get_base_dir( string $table, string $snippet_type ) { return WP_CONTENT_DIR . '/code-snippets/' . $table . '/' . $snippet_type; } diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index d6200091..dc96a6df 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -706,7 +706,7 @@ function execute_active_snippets_from_flat_files(): bool { } foreach ( $tables as $table_name ) { - $base_dir = WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/php'; + $base_dir = Snippet_Files::get_base_dir( $table_name, 'php' ); if ( ! is_dir( $base_dir ) ) { continue; From 759aabdedf2460adad77b95053e3a9cf334c4a1a Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 13:35:15 +0200 Subject: [PATCH 13/54] wip --- src/php/snippet-ops.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index dc96a6df..91326aec 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -682,6 +682,12 @@ function execute_active_snippets(): bool { * @return bool true on success, false on failure. */ function execute_active_snippets_from_flat_files(): bool { + // Bail early if safe mode is active. + if ( ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) || + ! apply_filters( 'code_snippets/execute_snippets', true ) ) { + return false; + } + $db = code_snippets()->db; $tables = $db->get_active_tables(); $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); From 18bcfa399954a92cbe8cda4df9999183944c0707 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 14:40:19 +0200 Subject: [PATCH 14/54] wip --- src/php/snippet-ops.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 91326aec..34d2c787 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -682,6 +682,8 @@ function execute_active_snippets(): bool { * @return bool true on success, false on failure. */ function execute_active_snippets_from_flat_files(): bool { + global $wpdb; + // Bail early if safe mode is active. if ( ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) || ! apply_filters( 'code_snippets/execute_snippets', true ) ) { From dc91357dc608e501df27926fe35e385a1c37bc76 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 21:46:20 +0200 Subject: [PATCH 15/54] wip --- src/php/class-snippet-files.php | 37 +++++++++++++-------------------- src/php/snippet-ops.php | 4 ++++ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index cf3b7302..3aa97f7a 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -19,11 +19,11 @@ public function init() { } public function register_hooks() { - add_action( 'code_snippets/create_snippet', array( $this, 'handle_snippet' ), 10, 2 ); - add_action( 'code_snippets/update_snippet', array( $this, 'handle_snippet' ), 10, 2 ); - add_action( 'code_snippets/delete_snippet', array( $this, 'delete_snippet' ), 10, 2 ); - add_action( 'code_snippets/activate_snippet', array( $this, 'activate_snippet' ), 10, 2 ); - add_action( 'code_snippets/deactivate_snippet', array( $this, 'deactivate_snippet' ), 10, 2 ); + add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); + add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); + add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); } private function ensure_filesystem() { @@ -55,13 +55,9 @@ public function handle_snippet( Snippet $snippet, string $table ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); - if ( $snippet->active ) { - $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); - } else { - $this->delete_file( $file_path ); - } + $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); - $this->update_config_file( $base_dir, $snippet, $snippet->active ); + $this->update_config_file( $base_dir, $snippet ); } public function delete_snippet( Snippet $snippet, bool $network ) { @@ -76,7 +72,7 @@ public function delete_snippet( Snippet $snippet, bool $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->delete_file( $file_path ); - $this->update_config_file( $base_dir, $snippet, false ); + $this->update_config_file( $base_dir, $snippet, true ); } public function activate_snippet( Snippet $snippet, bool $network ) { @@ -93,7 +89,7 @@ public function activate_snippet( Snippet $snippet, bool $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); - $this->update_config_file( $base_dir, $snippet, true ); + $this->update_config_file( $base_dir, $snippet ); } public function deactivate_snippet( int $snippet_id, bool $network ) { @@ -107,10 +103,7 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { $table = code_snippets()->db->get_table_name( $network ); $base_dir = self::get_base_dir( $table, $snippet_type ); - $file_path = $this->get_snippet_file_path( $base_dir, $snippet_id ); - $this->delete_file( $file_path ); - - $this->update_config_file( $base_dir, $snippet, false ); + $this->update_config_file( $base_dir, $snippet ); } /** @@ -176,16 +169,16 @@ private function save_config_file( string $config_file_path, array $active_snipp } /** - * Updates the index.php file by adding or removing a snippet. + * Updates the index.php file with snippet config. */ - private function update_config_file( string $base_dir, Snippet $snippet, bool $active ) { + private function update_config_file( string $base_dir, Snippet $snippet, bool $remove = false ) { $config_file_path = trailingslashit( $base_dir ) . 'index.php'; $active_snippets = $this->load_config_file( $config_file_path ); - if ( $active ) { - $active_snippets[ $snippet->id ] = $snippet->get_fields(); - } else { + if ( $remove ) { unset( $active_snippets[ $snippet->id ] ); + } else { + $active_snippets[ $snippet->id ] = $snippet->get_fields(); } $this->save_config_file( $config_file_path, $active_snippets ); diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 34d2c787..39565c59 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -733,6 +733,10 @@ function execute_active_snippets_from_flat_files(): bool { continue; } + if ( ! $snippet['active'] ) { + continue; + } + // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); From 1d99420c70a43658f380f89c832540086f997161 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 22:28:45 +0200 Subject: [PATCH 16/54] wip --- src/php/class-snippet-files.php | 37 ++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 3aa97f7a..3219540e 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -24,6 +24,7 @@ public function register_hooks() { add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); } private function ensure_filesystem() { @@ -107,10 +108,20 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { } /** - * Returns the base directory path for a given table. + * Returns the base directory path for a given context. */ - public static function get_base_dir( string $table, string $snippet_type ) { - return WP_CONTENT_DIR . '/code-snippets/' . $table . '/' . $snippet_type; + public static function get_base_dir( string $table = '', string $snippet_type = '' ) { + $base_dir = WP_CONTENT_DIR . '/code-snippets'; + + if ( ! empty( $table ) ) { + $base_dir .= '/' . $table; + } + + if ( ! empty( $snippet_type ) ) { + $base_dir .= '/' . $snippet_type; + } + + return $base_dir; } /** @@ -164,8 +175,8 @@ private function load_config_file( string $config_file_path ) { * Saves the index.php file via WP_Filesystem. */ private function save_config_file( string $config_file_path, array $active_snippets ) { - $index_content = "fs->put_contents( $config_file_path, $index_content, FS_CHMOD_FILE ); + $file_content = "fs->put_contents( $config_file_path, $file_content, FS_CHMOD_FILE ); } /** @@ -183,4 +194,20 @@ private function update_config_file( string $base_dir, Snippet $snippet, bool $r $this->save_config_file( $config_file_path, $active_snippets ); } + + public function sync_active_shared_network_snippets( $option, $old_value, $value ) { + if ( 'active_shared_network_snippets' !== $option ) { + return; + } + + $table = code_snippets()->db->get_table_name(); + $base_dir = self::get_base_dir( $table ); + + $this->maybe_create_directory( $base_dir ); + + $file_path = trailingslashit( $base_dir ) . 'active-shared-network-snippets.php'; + $file_content = "fs->put_contents( $file_path, $file_content, FS_CHMOD_FILE ); + } } From 918e7697737b95c49be29342762cbc517917c455 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 23:25:38 +0200 Subject: [PATCH 17/54] wip --- src/php/class-snippet-files.php | 52 ++++++++++++++++++++++++++++++--- src/php/snippet-ops.php | 37 +++++------------------ 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 3219540e..4fc0bab0 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -77,6 +77,8 @@ public function delete_snippet( Snippet $snippet, bool $network ) { } public function activate_snippet( Snippet $snippet, bool $network ) { + $snippet = get_snippet( $snippet->id, $network ); + $snippet_type = $snippet->get_type(); if ( ! $this->should_handle_snippet( $snippet_type ) ) { return; @@ -182,14 +184,14 @@ private function save_config_file( string $config_file_path, array $active_snipp /** * Updates the index.php file with snippet config. */ - private function update_config_file( string $base_dir, Snippet $snippet, bool $remove = false ) { + private function update_config_file( string $base_dir, Snippet $snippet, ?bool $remove = false ) { $config_file_path = trailingslashit( $base_dir ) . 'index.php'; $active_snippets = $this->load_config_file( $config_file_path ); - if ( $remove ) { - unset( $active_snippets[ $snippet->id ] ); - } else { + if ( ! $remove ) { $active_snippets[ $snippet->id ] = $snippet->get_fields(); + } else { + unset( $active_snippets[ $snippet->id ] ); } $this->save_config_file( $config_file_path, $active_snippets ); @@ -210,4 +212,46 @@ public function sync_active_shared_network_snippets( $option, $old_value, $value $this->fs->put_contents( $file_path, $file_content, FS_CHMOD_FILE ); } + + public static function get_active_snippets_from_flat_files() { + $snippets = []; + + $table = code_snippets()->db->get_table_name(); + $base_dir = self::get_base_dir( $table, 'php' ); + $snippets_file_path = $base_dir . '/index.php'; + + if ( is_file( $snippets_file_path ) ) { + $site_snippets = is_file( $snippets_file_path ) ? require $snippets_file_path : []; + + $snippets[ $table ] = array_filter( + $site_snippets, + function ( $snippet ) { + return $snippet['active']; + } + ); + } + + if ( is_multisite() ) { + $root_base_dir = self::get_base_dir( $table ); + $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; + + $active_shared_ids = is_file( $active_shared_ids_file_path ) ? require $active_shared_ids_file_path : []; + $ms_table = code_snippets()->db->get_table_name( true ); + $ms_base_dir = self::get_base_dir( $ms_table, 'php' ); + $ms_snippets_file_path = $ms_base_dir . '/index.php'; + + if ( is_file( $ms_snippets_file_path ) ) { + $ms_snippets = is_file( $ms_snippets_file_path ) ? require $ms_snippets_file_path : []; + + $snippets[ $ms_table ] = array_filter( + $ms_snippets, + function ( $snippet ) use ( $active_shared_ids ) { + return $snippet['active'] || in_array( intval( $snippet['id'] ), $active_shared_ids, true ); + } + ); + } + } + + return $snippets; + } } diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 39565c59..2eab3c94 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -675,12 +675,6 @@ function execute_active_snippets(): bool { return true; } -/** - * Execute the active snippets from the flat files. - * Read-write-execute operation. - * - * @return bool true on success, false on failure. - */ function execute_active_snippets_from_flat_files(): bool { global $wpdb; @@ -691,8 +685,8 @@ function execute_active_snippets_from_flat_files(): bool { } $db = code_snippets()->db; - $tables = $db->get_active_tables(); $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); + $data = Snippet_Files::get_active_snippets_from_flat_files(); // Detect if a snippet is currently being edited, and if so, spare it from execution. $edit_id = 0; @@ -713,29 +707,14 @@ function execute_active_snippets_from_flat_files(): bool { } } - foreach ( $tables as $table_name ) { + foreach ( $data as $table_name => $active_snippets ) { $base_dir = Snippet_Files::get_base_dir( $table_name, 'php' ); + $active_snippets = cs_sort_snippets_by_priority( $active_snippets ); - if ( ! is_dir( $base_dir ) ) { - continue; - } - - $active_snippets_file_path = $base_dir . '/index.php'; - if ( ! is_file( $active_snippets_file_path ) ) { - continue; - } - - $active_snippets = require $active_snippets_file_path; - $sorted_snippets = cs_sort_snippets_by_priority( $active_snippets ); - - foreach ( $sorted_snippets as $snippet_id => $snippet ) { - if ( ! in_array( $snippet['scope'], $scopes, true ) ) { - continue; - } - - if ( ! $snippet['active'] ) { - continue; - } + // Loop through the returned snippets and execute the PHP code. + foreach ( $active_snippets as $snippet ) { + $snippet_id = intval( $snippet['id'] ); + $code = $snippet['code']; // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { @@ -761,9 +740,9 @@ function execute_active_snippets_from_flat_files(): bool { } } - $file = $base_dir . '/' . $snippet_id . '.php'; if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { + $file = $base_dir . '/' . $snippet_id . '.php'; execute_snippet_from_flat_file( $file, $snippet_id ); } } From 737b3cfe7d6230b2c25417bb006057094ad6166c Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 23:37:42 +0200 Subject: [PATCH 18/54] wip --- src/php/class-snippet-files.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/php/class-snippet-files.php b/src/php/class-snippet-files.php index 4fc0bab0..9bb7bdd6 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/class-snippet-files.php @@ -232,10 +232,6 @@ function ( $snippet ) { } if ( is_multisite() ) { - $root_base_dir = self::get_base_dir( $table ); - $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; - - $active_shared_ids = is_file( $active_shared_ids_file_path ) ? require $active_shared_ids_file_path : []; $ms_table = code_snippets()->db->get_table_name( true ); $ms_base_dir = self::get_base_dir( $ms_table, 'php' ); $ms_snippets_file_path = $ms_base_dir . '/index.php'; @@ -243,6 +239,10 @@ function ( $snippet ) { if ( is_file( $ms_snippets_file_path ) ) { $ms_snippets = is_file( $ms_snippets_file_path ) ? require $ms_snippets_file_path : []; + $root_base_dir = self::get_base_dir( $table ); + $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; + $active_shared_ids = is_file( $active_shared_ids_file_path ) ? require $active_shared_ids_file_path : []; + $snippets[ $ms_table ] = array_filter( $ms_snippets, function ( $snippet ) use ( $active_shared_ids ) { From 20ec1896523dd192ab305bba28ec62896685e442 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 23:41:08 +0200 Subject: [PATCH 19/54] wip --- src/php/class-db.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/php/class-db.php b/src/php/class-db.php index a0b50782..64ea5f47 100644 --- a/src/php/class-db.php +++ b/src/php/class-db.php @@ -283,14 +283,4 @@ function ( $snippet ) use ( $active_shared_ids ) { return $active_snippets; } - - public function get_active_tables(): array { - $active_tables = array( $this->table ); - - if ( is_multisite() ) { - $active_tables[] = $this->ms_table; - } - - return $active_tables; - } } From 04e706d756eeb215921d38c830a56663efe22003 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Wed, 2 Jul 2025 23:46:21 +0200 Subject: [PATCH 20/54] wip --- src/php/snippet-ops.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 2eab3c94..8fc90ff2 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -685,7 +685,7 @@ function execute_active_snippets_from_flat_files(): bool { } $db = code_snippets()->db; - $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); + $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; $data = Snippet_Files::get_active_snippets_from_flat_files(); // Detect if a snippet is currently being edited, and if so, spare it from execution. @@ -713,8 +713,11 @@ function execute_active_snippets_from_flat_files(): bool { // Loop through the returned snippets and execute the PHP code. foreach ( $active_snippets as $snippet ) { + if ( ! in_array( $snippet['scope'], $scopes, true ) ) { + continue; + } + $snippet_id = intval( $snippet['id'] ); - $code = $snippet['code']; // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { From 890481ae74afc246e4b6debe10c3a784d02f089d Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sat, 5 Jul 2025 23:40:40 +0200 Subject: [PATCH 21/54] wip --- src/php/class-plugin.php | 9 +- .../classes/class-file-system-adapter.php | 35 ++++++++ .../classes}/class-snippet-files.php | 86 +++++++------------ .../files/handlers/html-snippet-handler.php | 17 ++++ .../files/handlers/php-snippet-handler.php | 17 ++++ .../interfaces/interface-file-system.php | 11 +++ .../interfaces/interface-snippet-handler.php | 9 ++ src/php/files/load.php | 9 ++ src/php/files/registry.php | 47 ++++++++++ 9 files changed, 183 insertions(+), 57 deletions(-) create mode 100644 src/php/files/classes/class-file-system-adapter.php rename src/php/{ => files/classes}/class-snippet-files.php (78%) create mode 100644 src/php/files/handlers/html-snippet-handler.php create mode 100644 src/php/files/handlers/php-snippet-handler.php create mode 100644 src/php/files/interfaces/interface-file-system.php create mode 100644 src/php/files/interfaces/interface-snippet-handler.php create mode 100644 src/php/files/load.php create mode 100644 src/php/files/registry.php diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index fda65d6f..c09edbb4 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -120,8 +120,13 @@ public function load_plugin() { require_once $includes_path . '/cloud/list-table-shared-ops.php'; // Snippet files. - require_once $includes_path . '/class-snippet-files.php'; - ( new Snippet_Files() )->init(); + require_once $includes_path . '/files/load.php'; + $registry = new Snippet_Handler_Registry( [ + 'php' => new Php_Snippet_Handler(), + 'html' => new Html_Snippet_Handler(), + ] ); + $fs = new WordPress_Filesystem_Adapter(); + ( new Snippet_Files( $registry, $fs ) )->register_hooks(); $this->active_snippets = new Active_Snippets(); $this->front_end = new Front_End(); diff --git a/src/php/files/classes/class-file-system-adapter.php b/src/php/files/classes/class-file-system-adapter.php new file mode 100644 index 00000000..b22308e3 --- /dev/null +++ b/src/php/files/classes/class-file-system-adapter.php @@ -0,0 +1,35 @@ +fs = $wp_filesystem; + } + + public function put_contents( string $path, string $contents, $chmod ) { + return $this->fs->put_contents( $path, $contents, $chmod ); + } + + public function exists( string $path ): bool { + return $this->fs->exists( $path ); + } + + public function delete( string $path ): bool { + return $this->fs->delete( $path ); + } + + public function is_dir( string $path ): bool { + return $this->fs->is_dir( $path ); + } + + public function mkdir( string $path, $chmod ) { + return $this->fs->mkdir( $path, $chmod ); + } +} diff --git a/src/php/class-snippet-files.php b/src/php/files/classes/class-snippet-files.php similarity index 78% rename from src/php/class-snippet-files.php rename to src/php/files/classes/class-snippet-files.php index 9bb7bdd6..a7a0df61 100644 --- a/src/php/class-snippet-files.php +++ b/src/php/files/classes/class-snippet-files.php @@ -4,19 +4,17 @@ class Snippet_Files { - /** - * Holds the WP_Filesystem instance. - * - * @var \WP_Filesystem_Base - */ - private $fs; + private Snippet_Handler_Registry $handler_registry; - const TYPES_TO_HANDLE = [ 'php', 'html' ]; + private File_System_Interface $fs; - public function init() { - $this->ensure_filesystem(); - $this->register_hooks(); - } + public function __construct( + Snippet_Handler_Registry $handler_registry, + File_System_Interface $fs + ) { + $this->handler_registry = $handler_registry; + $this->fs = $fs; + } public function register_hooks() { add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); @@ -27,48 +25,36 @@ public function register_hooks() { add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); } - private function ensure_filesystem() { - if ( ! $this->fs ) { - if ( ! function_exists( 'WP_Filesystem' ) ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - } - - WP_Filesystem(); - - global $wp_filesystem; - - $this->fs = $wp_filesystem; - } - } - - private function should_handle_snippet( string $snippet_type ) { - return in_array( $snippet_type, self::TYPES_TO_HANDLE, true ); - } - public function handle_snippet( Snippet $snippet, string $table ) { $snippet_type = $snippet->get_type(); - if ( ! $this->should_handle_snippet( $snippet_type ) ) { + $handler = $this->handler_registry->get_handler( $snippet_type ); + + if ( ! $handler ) { return; } - $base_dir = self::get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); - $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); + $contents = $handler->wrap_code( $snippet->code ); + + $this->fs->put_contents( $file_path, $contents, FS_CHMOD_FILE ); $this->update_config_file( $base_dir, $snippet ); } public function delete_snippet( Snippet $snippet, bool $network ) { $snippet_type = $snippet->get_type(); - if ( ! $this->should_handle_snippet( $snippet_type ) ) { + $handler = $this->handler_registry->get_handler( $snippet_type ); + + if ( ! $handler ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = self::get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); $this->delete_file( $file_path ); @@ -78,19 +64,23 @@ public function delete_snippet( Snippet $snippet, bool $network ) { public function activate_snippet( Snippet $snippet, bool $network ) { $snippet = get_snippet( $snippet->id, $network ); - $snippet_type = $snippet->get_type(); - if ( ! $this->should_handle_snippet( $snippet_type ) ) { + $handler = $this->handler_registry->get_handler( $snippet_type ); + + if ( ! $handler ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = self::get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); - $this->write_snippet_file( $file_path, $snippet->code, $snippet_type ); + + $contents = $handler->wrap_code( $snippet->code ); + + $this->fs->put_contents( $file_path, $contents, FS_CHMOD_FILE ); $this->update_config_file( $base_dir, $snippet ); } @@ -98,13 +88,14 @@ public function activate_snippet( Snippet $snippet, bool $network ) { public function deactivate_snippet( int $snippet_id, bool $network ) { $snippet = get_snippet( $snippet_id, $network ); $snippet_type = $snippet->get_type(); + $handler = $this->handler_registry->get_handler( $snippet_type ); - if ( ! $this->should_handle_snippet( $snippet_type ) ) { + if ( ! $handler ) { return; } $table = code_snippets()->db->get_table_name( $network ); - $base_dir = self::get_base_dir( $table, $snippet_type ); + $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->update_config_file( $base_dir, $snippet ); } @@ -142,21 +133,6 @@ private function get_snippet_file_path( string $base_dir, int $snippet_id ) { return trailingslashit( $base_dir ) . $snippet_id . '.php'; } - /** - * Writes the snippet code to a file, with the required header. - */ - private function write_snippet_file( string $file_path, string $code, string $snippet_type ) { - $content = "\n\n"; - } - - $content .= $code; - - $this->fs->put_contents( $file_path, $content, FS_CHMOD_FILE ); - } - /** * Deletes a file if it exists. */ diff --git a/src/php/files/handlers/html-snippet-handler.php b/src/php/files/handlers/html-snippet-handler.php new file mode 100644 index 00000000..7916297d --- /dev/null +++ b/src/php/files/handlers/html-snippet-handler.php @@ -0,0 +1,17 @@ +\n\n" . $code; + } +} diff --git a/src/php/files/handlers/php-snippet-handler.php b/src/php/files/handlers/php-snippet-handler.php new file mode 100644 index 00000000..666617b0 --- /dev/null +++ b/src/php/files/handlers/php-snippet-handler.php @@ -0,0 +1,17 @@ + $handler ) { + $this->register_handler( $type, $handler ); + } + } + + /** + * Registers a handler for a snippet type. + * + * @param string $type + * @param Snippet_Type_Handler_Interface $handler + * @return void + */ + public function register_handler( string $type, Snippet_Type_Handler_Interface $handler ): void { + $this->handlers[ $type ] = $handler; + } + + /** + * Gets the handler for a snippet type. + * + * @param string $type + * + * @return Snippet_Type_Handler_Interface|null + */ + public function get_handler( string $type ): ?Snippet_Type_Handler_Interface { + if ( ! isset( $this->handlers[ $type ] ) ) { + return null; + } + + return $this->handlers[ $type ]; + } +} From 3f5eea3fb3ddeebceb6a9a4d51939ccdd02930af Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sat, 5 Jul 2025 23:44:39 +0200 Subject: [PATCH 22/54] wip --- src/php/class-plugin.php | 2 +- .../{files => flat-files}/classes/class-file-system-adapter.php | 0 src/php/{files => flat-files}/classes/class-snippet-files.php | 0 src/php/{files => flat-files}/handlers/html-snippet-handler.php | 0 src/php/{files => flat-files}/handlers/php-snippet-handler.php | 0 .../{files => flat-files}/interfaces/interface-file-system.php | 0 .../interfaces/interface-snippet-handler.php | 0 src/php/{files => flat-files}/load.php | 0 src/php/{files => flat-files}/registry.php | 0 9 files changed, 1 insertion(+), 1 deletion(-) rename src/php/{files => flat-files}/classes/class-file-system-adapter.php (100%) rename src/php/{files => flat-files}/classes/class-snippet-files.php (100%) rename src/php/{files => flat-files}/handlers/html-snippet-handler.php (100%) rename src/php/{files => flat-files}/handlers/php-snippet-handler.php (100%) rename src/php/{files => flat-files}/interfaces/interface-file-system.php (100%) rename src/php/{files => flat-files}/interfaces/interface-snippet-handler.php (100%) rename src/php/{files => flat-files}/load.php (100%) rename src/php/{files => flat-files}/registry.php (100%) diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index c09edbb4..ed07aaec 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -120,7 +120,7 @@ public function load_plugin() { require_once $includes_path . '/cloud/list-table-shared-ops.php'; // Snippet files. - require_once $includes_path . '/files/load.php'; + require_once $includes_path . '/flat-files/load.php'; $registry = new Snippet_Handler_Registry( [ 'php' => new Php_Snippet_Handler(), 'html' => new Html_Snippet_Handler(), diff --git a/src/php/files/classes/class-file-system-adapter.php b/src/php/flat-files/classes/class-file-system-adapter.php similarity index 100% rename from src/php/files/classes/class-file-system-adapter.php rename to src/php/flat-files/classes/class-file-system-adapter.php diff --git a/src/php/files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php similarity index 100% rename from src/php/files/classes/class-snippet-files.php rename to src/php/flat-files/classes/class-snippet-files.php diff --git a/src/php/files/handlers/html-snippet-handler.php b/src/php/flat-files/handlers/html-snippet-handler.php similarity index 100% rename from src/php/files/handlers/html-snippet-handler.php rename to src/php/flat-files/handlers/html-snippet-handler.php diff --git a/src/php/files/handlers/php-snippet-handler.php b/src/php/flat-files/handlers/php-snippet-handler.php similarity index 100% rename from src/php/files/handlers/php-snippet-handler.php rename to src/php/flat-files/handlers/php-snippet-handler.php diff --git a/src/php/files/interfaces/interface-file-system.php b/src/php/flat-files/interfaces/interface-file-system.php similarity index 100% rename from src/php/files/interfaces/interface-file-system.php rename to src/php/flat-files/interfaces/interface-file-system.php diff --git a/src/php/files/interfaces/interface-snippet-handler.php b/src/php/flat-files/interfaces/interface-snippet-handler.php similarity index 100% rename from src/php/files/interfaces/interface-snippet-handler.php rename to src/php/flat-files/interfaces/interface-snippet-handler.php diff --git a/src/php/files/load.php b/src/php/flat-files/load.php similarity index 100% rename from src/php/files/load.php rename to src/php/flat-files/load.php diff --git a/src/php/files/registry.php b/src/php/flat-files/registry.php similarity index 100% rename from src/php/files/registry.php rename to src/php/flat-files/registry.php From 69449a01470195e85102078a1dcfe55a3b1e9ebb Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sat, 5 Jul 2025 23:48:38 +0200 Subject: [PATCH 23/54] wip --- .../classes/class-file-system-adapter.php | 48 ++++++------ .../handlers/html-snippet-handler.php | 18 ++--- .../interfaces/interface-file-system.php | 10 +-- .../interfaces/interface-snippet-handler.php | 6 +- src/php/flat-files/registry.php | 74 +++++++++---------- 5 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/php/flat-files/classes/class-file-system-adapter.php b/src/php/flat-files/classes/class-file-system-adapter.php index b22308e3..c8085e5c 100644 --- a/src/php/flat-files/classes/class-file-system-adapter.php +++ b/src/php/flat-files/classes/class-file-system-adapter.php @@ -2,34 +2,34 @@ namespace Code_Snippets; class WordPress_Filesystem_Adapter implements File_System_Interface { - private $fs; + private $fs; - public function __construct() { - if ( ! function_exists( 'WP_Filesystem' ) ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - } - WP_Filesystem(); - global $wp_filesystem; - $this->fs = $wp_filesystem; - } + public function __construct() { + if ( ! function_exists( 'WP_Filesystem' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + WP_Filesystem(); + global $wp_filesystem; + $this->fs = $wp_filesystem; + } - public function put_contents( string $path, string $contents, $chmod ) { - return $this->fs->put_contents( $path, $contents, $chmod ); - } + public function put_contents( string $path, string $contents, $chmod ) { + return $this->fs->put_contents( $path, $contents, $chmod ); + } - public function exists( string $path ): bool { - return $this->fs->exists( $path ); - } + public function exists( string $path ): bool { + return $this->fs->exists( $path ); + } - public function delete( string $path ): bool { - return $this->fs->delete( $path ); - } + public function delete( string $path ): bool { + return $this->fs->delete( $path ); + } - public function is_dir( string $path ): bool { - return $this->fs->is_dir( $path ); - } + public function is_dir( string $path ): bool { + return $this->fs->is_dir( $path ); + } - public function mkdir( string $path, $chmod ) { - return $this->fs->mkdir( $path, $chmod ); - } + public function mkdir( string $path, $chmod ) { + return $this->fs->mkdir( $path, $chmod ); + } } diff --git a/src/php/flat-files/handlers/html-snippet-handler.php b/src/php/flat-files/handlers/html-snippet-handler.php index 7916297d..d7a4446a 100644 --- a/src/php/flat-files/handlers/html-snippet-handler.php +++ b/src/php/flat-files/handlers/html-snippet-handler.php @@ -3,15 +3,15 @@ namespace Code_Snippets; class Html_Snippet_Handler implements Snippet_Type_Handler_Interface { - public function get_file_extension(): string { - return 'php'; - } + public function get_file_extension(): string { + return 'php'; + } - public function get_dir_name(): string { - return 'html'; - } + public function get_dir_name(): string { + return 'html'; + } - public function wrap_code( string $code ): string { - return "\n\n" . $code; - } + public function wrap_code( string $code ): string { + return "\n\n" . $code; + } } diff --git a/src/php/flat-files/interfaces/interface-file-system.php b/src/php/flat-files/interfaces/interface-file-system.php index 3a8cc7a3..38ab5bd0 100644 --- a/src/php/flat-files/interfaces/interface-file-system.php +++ b/src/php/flat-files/interfaces/interface-file-system.php @@ -3,9 +3,9 @@ namespace Code_Snippets; interface File_System_Interface { - public function put_contents( string $path, string $contents, $chmod ); - public function exists( string $path ): bool; - public function delete( string $path ): bool; - public function is_dir( string $path ): bool; - public function mkdir( string $path, $chmod ); + public function put_contents( string $path, string $contents, $chmod ); + public function exists( string $path ): bool; + public function delete( string $path ): bool; + public function is_dir( string $path ): bool; + public function mkdir( string $path, $chmod ); } diff --git a/src/php/flat-files/interfaces/interface-snippet-handler.php b/src/php/flat-files/interfaces/interface-snippet-handler.php index fdcbb6b8..4ff024cb 100644 --- a/src/php/flat-files/interfaces/interface-snippet-handler.php +++ b/src/php/flat-files/interfaces/interface-snippet-handler.php @@ -3,7 +3,7 @@ namespace Code_Snippets; interface Snippet_Type_Handler_Interface { - public function get_file_extension(): string; - public function get_dir_name(): string; - public function wrap_code( string $code ): string; + public function get_file_extension(): string; + public function get_dir_name(): string; + public function wrap_code( string $code ): string; } diff --git a/src/php/flat-files/registry.php b/src/php/flat-files/registry.php index a02b589b..4fce503d 100644 --- a/src/php/flat-files/registry.php +++ b/src/php/flat-files/registry.php @@ -3,45 +3,45 @@ namespace Code_Snippets; class Snippet_Handler_Registry { - /** - * @var Snippet_Type_Handler_Interface[] - */ - private array $handlers = []; + /** + * @var Snippet_Type_Handler_Interface[] + */ + private array $handlers = []; - /** - * Constructor - * - * @param Snippet_Type_Handler_Interface[] $handlers - */ - public function __construct( array $handlers ) { - foreach ( $handlers as $type => $handler ) { - $this->register_handler( $type, $handler ); - } - } + /** + * Constructor + * + * @param Snippet_Type_Handler_Interface[] $handlers + */ + public function __construct( array $handlers ) { + foreach ( $handlers as $type => $handler ) { + $this->register_handler( $type, $handler ); + } + } - /** - * Registers a handler for a snippet type. - * - * @param string $type - * @param Snippet_Type_Handler_Interface $handler - * @return void - */ - public function register_handler( string $type, Snippet_Type_Handler_Interface $handler ): void { - $this->handlers[ $type ] = $handler; - } + /** + * Registers a handler for a snippet type. + * + * @param string $type + * @param Snippet_Type_Handler_Interface $handler + * @return void + */ + public function register_handler( string $type, Snippet_Type_Handler_Interface $handler ): void { + $this->handlers[ $type ] = $handler; + } - /** - * Gets the handler for a snippet type. - * - * @param string $type - * - * @return Snippet_Type_Handler_Interface|null - */ - public function get_handler( string $type ): ?Snippet_Type_Handler_Interface { - if ( ! isset( $this->handlers[ $type ] ) ) { - return null; - } + /** + * Gets the handler for a snippet type. + * + * @param string $type + * + * @return Snippet_Type_Handler_Interface|null + */ + public function get_handler( string $type ): ?Snippet_Type_Handler_Interface { + if ( ! isset( $this->handlers[ $type ] ) ) { + return null; + } - return $this->handlers[ $type ]; - } + return $this->handlers[ $type ]; + } } From 91b754696d144c0c119b89482aa8dfcecb6bd342 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sat, 5 Jul 2025 23:49:37 +0200 Subject: [PATCH 24/54] wip --- src/php/flat-files/classes/class-snippet-files.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index a7a0df61..9750d2a2 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -8,13 +8,13 @@ class Snippet_Files { private File_System_Interface $fs; - public function __construct( - Snippet_Handler_Registry $handler_registry, + public function __construct( + Snippet_Handler_Registry $handler_registry, File_System_Interface $fs - ) { - $this->handler_registry = $handler_registry; - $this->fs = $fs; - } + ) { + $this->handler_registry = $handler_registry; + $this->fs = $fs; + } public function register_hooks() { add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); From 0029e0f0dde5f386acaf1b1695f4dc0a8baa0f89 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sat, 5 Jul 2025 23:55:22 +0200 Subject: [PATCH 25/54] wip --- src/php/flat-files/classes/class-snippet-files.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 9750d2a2..add308ad 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -36,7 +36,7 @@ public function handle_snippet( Snippet $snippet, string $table ) { $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); - $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id, $handler->get_file_extension() ); $contents = $handler->wrap_code( $snippet->code ); @@ -56,7 +56,7 @@ public function delete_snippet( Snippet $snippet, bool $network ) { $table = code_snippets()->db->get_table_name( $network ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); - $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id, $handler->get_file_extension() ); $this->delete_file( $file_path ); $this->update_config_file( $base_dir, $snippet, true ); @@ -76,7 +76,7 @@ public function activate_snippet( Snippet $snippet, bool $network ) { $this->maybe_create_directory( $base_dir ); - $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id ); + $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id, $handler->get_file_extension() ); $contents = $handler->wrap_code( $snippet->code ); @@ -129,8 +129,8 @@ private function maybe_create_directory( string $dir ) { /** * Returns the path to the snippet PHP file. */ - private function get_snippet_file_path( string $base_dir, int $snippet_id ) { - return trailingslashit( $base_dir ) . $snippet_id . '.php'; + private function get_snippet_file_path( string $base_dir, int $snippet_id, string $ext ) { + return trailingslashit( $base_dir ) . $snippet_id . '.' . $ext; } /** From 1a7789e9792923dba658880188f7b00d961c10a6 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 00:07:32 +0200 Subject: [PATCH 26/54] wip --- src/php/class-plugin.php | 7 ++- .../classes/class-config-repository.php | 41 +++++++++++++ .../classes/class-snippet-files.php | 57 +++---------------- .../interface-config-repository.php | 9 +++ src/php/flat-files/load.php | 1 + 5 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 src/php/flat-files/classes/class-config-repository.php create mode 100644 src/php/flat-files/interfaces/interface-config-repository.php diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index ed07aaec..9366a5f8 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -121,12 +121,17 @@ public function load_plugin() { // Snippet files. require_once $includes_path . '/flat-files/load.php'; + $registry = new Snippet_Handler_Registry( [ 'php' => new Php_Snippet_Handler(), 'html' => new Html_Snippet_Handler(), ] ); + $fs = new WordPress_Filesystem_Adapter(); - ( new Snippet_Files( $registry, $fs ) )->register_hooks(); + + $config_repo = new Snippet_Config_Repository( $fs ); + + ( new Snippet_Files( $registry, $fs, $config_repo ) )->register_hooks(); $this->active_snippets = new Active_Snippets(); $this->front_end = new Front_End(); diff --git a/src/php/flat-files/classes/class-config-repository.php b/src/php/flat-files/classes/class-config-repository.php new file mode 100644 index 00000000..bcf48535 --- /dev/null +++ b/src/php/flat-files/classes/class-config-repository.php @@ -0,0 +1,41 @@ +fs = $fs; + } + + public function load( string $base_dir ): array { + $config_file_path = trailingslashit( $base_dir ) . 'index.php'; + + return is_file( $config_file_path ) + ? require $config_file_path + : []; + } + + public function save( string $base_dir, array $active_snippets ): void { + $config_file_path = trailingslashit( $base_dir ) . 'index.php'; + + $file_content = "fs->put_contents( $config_file_path, $file_content, FS_CHMOD_FILE ); + } + + public function update( string $base_dir, Snippet $snippet, ?bool $remove = false ): void { + $active_snippets = $this->load( $base_dir ); + + if ( ! $remove ) { + $active_snippets[ $snippet->id ] = $snippet->get_fields(); + } else { + unset( $active_snippets[ $snippet->id ] ); + } + + $this->save( $base_dir, $active_snippets ); + } +} diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index add308ad..35ccaa03 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -8,12 +8,16 @@ class Snippet_Files { private File_System_Interface $fs; + private Snippet_Config_Repository_Interface $config_repo; + public function __construct( Snippet_Handler_Registry $handler_registry, - File_System_Interface $fs + File_System_Interface $fs, + Snippet_Config_Repository_Interface $config_repo ) { $this->handler_registry = $handler_registry; $this->fs = $fs; + $this->config_repo = $config_repo; } public function register_hooks() { @@ -42,7 +46,7 @@ public function handle_snippet( Snippet $snippet, string $table ) { $this->fs->put_contents( $file_path, $contents, FS_CHMOD_FILE ); - $this->update_config_file( $base_dir, $snippet ); + $this->config_repo->update( $base_dir, $snippet ); } public function delete_snippet( Snippet $snippet, bool $network ) { @@ -59,7 +63,7 @@ public function delete_snippet( Snippet $snippet, bool $network ) { $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id, $handler->get_file_extension() ); $this->delete_file( $file_path ); - $this->update_config_file( $base_dir, $snippet, true ); + $this->config_repo->update( $base_dir, $snippet, true ); } public function activate_snippet( Snippet $snippet, bool $network ) { @@ -82,7 +86,7 @@ public function activate_snippet( Snippet $snippet, bool $network ) { $this->fs->put_contents( $file_path, $contents, FS_CHMOD_FILE ); - $this->update_config_file( $base_dir, $snippet ); + $this->config_repo->update( $base_dir, $snippet ); } public function deactivate_snippet( int $snippet_id, bool $network ) { @@ -97,12 +101,9 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { $table = code_snippets()->db->get_table_name( $network ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); - $this->update_config_file( $base_dir, $snippet ); + $this->config_repo->update( $base_dir, $snippet ); } - /** - * Returns the base directory path for a given context. - */ public static function get_base_dir( string $table = '', string $snippet_type = '' ) { $base_dir = WP_CONTENT_DIR . '/code-snippets'; @@ -117,62 +118,22 @@ public static function get_base_dir( string $table = '', string $snippet_type = return $base_dir; } - /** - * Creates the directory if it does not exist. - */ private function maybe_create_directory( string $dir ) { if ( ! $this->fs->is_dir( $dir ) ) { $this->fs->mkdir( $dir, FS_CHMOD_DIR ); } } - /** - * Returns the path to the snippet PHP file. - */ private function get_snippet_file_path( string $base_dir, int $snippet_id, string $ext ) { return trailingslashit( $base_dir ) . $snippet_id . '.' . $ext; } - /** - * Deletes a file if it exists. - */ private function delete_file( string $file_path ) { if ( $this->fs->exists( $file_path ) ) { $this->fs->delete( $file_path ); } } - /** - * Loads the index.php array by requiring it directly. - */ - private function load_config_file( string $config_file_path ) { - return is_file( $config_file_path ) ? require $config_file_path : []; - } - - /** - * Saves the index.php file via WP_Filesystem. - */ - private function save_config_file( string $config_file_path, array $active_snippets ) { - $file_content = "fs->put_contents( $config_file_path, $file_content, FS_CHMOD_FILE ); - } - - /** - * Updates the index.php file with snippet config. - */ - private function update_config_file( string $base_dir, Snippet $snippet, ?bool $remove = false ) { - $config_file_path = trailingslashit( $base_dir ) . 'index.php'; - $active_snippets = $this->load_config_file( $config_file_path ); - - if ( ! $remove ) { - $active_snippets[ $snippet->id ] = $snippet->get_fields(); - } else { - unset( $active_snippets[ $snippet->id ] ); - } - - $this->save_config_file( $config_file_path, $active_snippets ); - } - public function sync_active_shared_network_snippets( $option, $old_value, $value ) { if ( 'active_shared_network_snippets' !== $option ) { return; diff --git a/src/php/flat-files/interfaces/interface-config-repository.php b/src/php/flat-files/interfaces/interface-config-repository.php new file mode 100644 index 00000000..e8c3a819 --- /dev/null +++ b/src/php/flat-files/interfaces/interface-config-repository.php @@ -0,0 +1,9 @@ + Date: Sun, 6 Jul 2025 00:08:36 +0200 Subject: [PATCH 27/54] wip --- src/php/flat-files/load.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/php/flat-files/load.php b/src/php/flat-files/load.php index ea42c050..476a5113 100644 --- a/src/php/flat-files/load.php +++ b/src/php/flat-files/load.php @@ -8,3 +8,4 @@ require_once 'handlers/html-snippet-handler.php'; require_once 'classes/class-file-system-adapter.php'; require_once 'classes/class-snippet-files.php'; +require_once 'classes/class-config-repository.php'; From 87b9b2dca9ae96755e4c0951489b8579ce4958ee Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 00:27:08 +0200 Subject: [PATCH 28/54] wip --- src/php/flat-files/load.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/php/flat-files/load.php b/src/php/flat-files/load.php index 476a5113..dd87d808 100644 --- a/src/php/flat-files/load.php +++ b/src/php/flat-files/load.php @@ -1,11 +1,17 @@ Date: Sun, 6 Jul 2025 21:51:13 +0200 Subject: [PATCH 29/54] wip --- src/php/flat-files/classes/class-snippet-files.php | 13 +++++++++++++ src/php/load.php | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 35ccaa03..093e33fd 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -26,7 +26,10 @@ public function register_hooks() { add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); + + add_filter( 'code_snippets_settings_fields', [ $this, 'add_settings_fields' ], 10, 1 ); } public function handle_snippet( Snippet $snippet, string $table ) { @@ -191,4 +194,14 @@ function ( $snippet ) use ( $active_shared_ids ) { return $snippets; } + + public function add_settings_fields( array $fields ) { + $fields['general']['enable_flat_files'] = [ + 'name' => __( 'Enable Flat Files', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'Snippets will be executed from flat files instead of the database.', 'code-snippets' ), + ]; + + return $fields; + } } diff --git a/src/php/load.php b/src/php/load.php index 8700186a..220d95a5 100644 --- a/src/php/load.php +++ b/src/php/load.php @@ -65,5 +65,8 @@ function code_snippets(): Plugin { code_snippets()->load_plugin(); // Execute the snippets once the plugins are loaded. -// add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets', 1 ); -add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets_from_flat_files', 1 ); +$snippet_execution_fn = Settings\get_setting( 'general', 'enable_flat_files' ) + ? '\execute_active_snippets_from_flat_files' + : '\execute_active_snippets'; + +add_action( 'plugins_loaded', __NAMESPACE__ . $snippet_execution_fn, 1 ); From 531ee3515a14ff6f0e5723337194e9524fa6b59b Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 21:57:51 +0200 Subject: [PATCH 30/54] wip --- src/php/front-end/class-front-end.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 60429b35..258b27ec 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -245,6 +245,12 @@ protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): return $snippet->code; } + $should_execute_from_flat_files = Settings\get_setting( 'general', 'enable_flat_files' ); + + if ( ! $should_execute_from_flat_files ) { + return $this->evaluate_shortcode_from_db( $snippet, $atts ); + } + $network = DB::validate_network_param( $snippet->network ); $table_name = code_snippets()->db->get_table_name( $network ); $filepath = WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/html/' . $snippet->id . '.php'; From 0b19d6ef3ef82408ff3b47cd1eec24bcae4cc850 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 22:07:48 +0200 Subject: [PATCH 31/54] wip --- src/php/class-plugin.php | 2 +- src/php/front-end/class-front-end.php | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index 9366a5f8..bb21473a 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -134,7 +134,7 @@ public function load_plugin() { ( new Snippet_Files( $registry, $fs, $config_repo ) )->register_hooks(); $this->active_snippets = new Active_Snippets(); - $this->front_end = new Front_End(); + $this->front_end = new Front_End( $registry ); $this->cloud_api = new Cloud_API(); $upgrade = new Upgrade( $this->version, $this->db ); diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 258b27ec..f0ddf8e1 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -33,10 +33,14 @@ class Front_End { */ const MAX_SHORTCODE_DEPTH = 5; + private Snippet_Handler_Registry $handler_registry; + /** * Class constructor */ - public function __construct() { + public function __construct( Snippet_Handler_Registry $handler_registry ) { + $this->handler_registry = $handler_registry; + add_action( 'the_posts', [ $this, 'enqueue_highlighting' ] ); add_action( 'init', [ $this, 'setup_mce_plugin' ] ); @@ -232,6 +236,20 @@ protected function convert_boolean_attribute_flags( array $atts, array $boolean_ return $atts; } + /** + * Build the file path for a snippet's flat file. + * + * @param string $table_name Table name for the snippet. + * @param Snippet $snippet Snippet object. + * + * @return string Full file path for the snippet. + */ + private function build_snippet_filepath( string $table_name, Snippet $snippet ): string { + $handler = $this->handler_registry->get_handler( $snippet->get_type() ); + + return WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/' . $handler->get_dir_name() . '/' . $snippet->id . '.' . $handler->get_file_extension(); + } + /** * Evaluate the code from a content shortcode. * @@ -253,7 +271,7 @@ protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): $network = DB::validate_network_param( $snippet->network ); $table_name = code_snippets()->db->get_table_name( $network ); - $filepath = WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/html/' . $snippet->id . '.php'; + $filepath = $this->build_snippet_filepath( $table_name, $snippet ); return file_exists( $filepath ) ? $this->evaluate_shortcode_from_flat_file( $filepath, $atts ) From ff80d08a00934d88ab6ea02b2c6561dc355a278e Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 22:10:29 +0200 Subject: [PATCH 32/54] wip --- src/php/front-end/class-front-end.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index f0ddf8e1..66d00e05 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -244,7 +244,7 @@ protected function convert_boolean_attribute_flags( array $atts, array $boolean_ * * @return string Full file path for the snippet. */ - private function build_snippet_filepath( string $table_name, Snippet $snippet ): string { + private function build_snippet_flat_file_path( string $table_name, Snippet $snippet ): string { $handler = $this->handler_registry->get_handler( $snippet->get_type() ); return WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/' . $handler->get_dir_name() . '/' . $snippet->id . '.' . $handler->get_file_extension(); @@ -271,7 +271,7 @@ protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): $network = DB::validate_network_param( $snippet->network ); $table_name = code_snippets()->db->get_table_name( $network ); - $filepath = $this->build_snippet_filepath( $table_name, $snippet ); + $filepath = $this->build_snippet_flat_file_path( $table_name, $snippet ); return file_exists( $filepath ) ? $this->evaluate_shortcode_from_flat_file( $filepath, $atts ) From b533854ab4f5ae9501298e858fcff31117b100f2 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 22:13:32 +0200 Subject: [PATCH 33/54] wip --- .../flat-files/classes/class-snippet-files.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 093e33fd..8765780f 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -21,13 +21,15 @@ public function __construct( } public function register_hooks() { - add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); - add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); - add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); - add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); - - add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); + if ( Settings\get_setting( 'general', 'enable_flat_files' ) ) { + add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); + add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); + add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); + add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + + add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); + } add_filter( 'code_snippets_settings_fields', [ $this, 'add_settings_fields' ], 10, 1 ); } From b983aa79f74cba15ecc33221815ea3155bee22c7 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 22:29:23 +0200 Subject: [PATCH 34/54] wip --- .../classes/class-file-system-adapter.php | 4 ++++ .../interfaces/interface-file-system.php | 1 + src/php/uninstall.php | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/php/flat-files/classes/class-file-system-adapter.php b/src/php/flat-files/classes/class-file-system-adapter.php index c8085e5c..6c230f13 100644 --- a/src/php/flat-files/classes/class-file-system-adapter.php +++ b/src/php/flat-files/classes/class-file-system-adapter.php @@ -32,4 +32,8 @@ public function is_dir( string $path ): bool { public function mkdir( string $path, $chmod ) { return $this->fs->mkdir( $path, $chmod ); } + + public function rmdir( string $path, bool $recursive = false ): bool { + return $this->fs->rmdir( $path, $recursive ); + } } diff --git a/src/php/flat-files/interfaces/interface-file-system.php b/src/php/flat-files/interfaces/interface-file-system.php index 38ab5bd0..7560d7bb 100644 --- a/src/php/flat-files/interfaces/interface-file-system.php +++ b/src/php/flat-files/interfaces/interface-file-system.php @@ -8,4 +8,5 @@ public function exists( string $path ): bool; public function delete( string $path ): bool; public function is_dir( string $path ): bool; public function mkdir( string $path, $chmod ); + public function rmdir( string $path, bool $recursive = false ): bool; } diff --git a/src/php/uninstall.php b/src/php/uninstall.php index 4da58cfe..028aca5c 100644 --- a/src/php/uninstall.php +++ b/src/php/uninstall.php @@ -18,7 +18,7 @@ function complete_uninstall_enabled(): bool { $unified = false; if ( is_multisite() ) { - $menu_perms = get_site_option( 'menu_items', array() ); + $menu_perms = get_site_option( 'menu_items', [] ); $unified = empty( $menu_perms['snippets_settings'] ); } @@ -72,6 +72,17 @@ function uninstall_multisite() { delete_site_option( 'recently_activated_snippets' ); } +function delete_flat_files_directory() { + $flat_files_dir = WP_CONTENT_DIR . '/code-snippets'; + + if ( ! is_dir( $flat_files_dir ) ) { + return; + } + + $fs = new \Code_Snippets\WordPress_Filesystem_Adapter(); + $fs->rmdir( $flat_files_dir, true ); +} + /** * Uninstall the Code Snippets plugin. * @@ -85,5 +96,7 @@ function uninstall_plugin() { } else { uninstall_current_site(); } + + delete_flat_files_directory(); } } From e3a96ab1a469657e6fc392e264a7db351dbb4851 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Sun, 6 Jul 2025 22:31:19 +0200 Subject: [PATCH 35/54] wip --- src/php/flat-files/classes/class-config-repository.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/php/flat-files/classes/class-config-repository.php b/src/php/flat-files/classes/class-config-repository.php index bcf48535..2087f572 100644 --- a/src/php/flat-files/classes/class-config-repository.php +++ b/src/php/flat-files/classes/class-config-repository.php @@ -3,6 +3,8 @@ class Snippet_Config_Repository implements Snippet_Config_Repository_Interface { + const CONFIG_FILE_NAME = 'index.php'; + private File_System_Interface $fs; public function __construct( File_System_Interface $fs ) { @@ -10,7 +12,7 @@ public function __construct( File_System_Interface $fs ) { } public function load( string $base_dir ): array { - $config_file_path = trailingslashit( $base_dir ) . 'index.php'; + $config_file_path = trailingslashit( $base_dir ) . static::CONFIG_FILE_NAME; return is_file( $config_file_path ) ? require $config_file_path @@ -18,7 +20,7 @@ public function load( string $base_dir ): array { } public function save( string $base_dir, array $active_snippets ): void { - $config_file_path = trailingslashit( $base_dir ) . 'index.php'; + $config_file_path = trailingslashit( $base_dir ) . static::CONFIG_FILE_NAME; $file_content = " Date: Sun, 6 Jul 2025 23:16:43 +0200 Subject: [PATCH 36/54] wip --- .../classes/class-snippet-files.php | 25 +++++++++++++++++++ src/php/settings/settings.php | 2 ++ 2 files changed, 27 insertions(+) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 8765780f..18a68176 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -32,6 +32,7 @@ public function register_hooks() { } add_filter( 'code_snippets_settings_fields', [ $this, 'add_settings_fields' ], 10, 1 ); + add_action( 'code_snippets/settings_updated', [ $this, 'create_all_flat_files' ], 10, 2 ); } public function handle_snippet( Snippet $snippet, string $table ) { @@ -206,4 +207,28 @@ public function add_settings_fields( array $fields ) { return $fields; } + + public function create_all_flat_files( array $settings, array $input ) { + if ( ! isset( $settings['general']['enable_flat_files'] ) ) { + return; + } + + if ( ! $settings['general']['enable_flat_files'] ) { + return; + } + + $db = code_snippets()->db; + $data = $db->fetch_active_snippets( Snippet::get_all_scopes() ); + + if ( empty( $data ) ) { + return; + } + + foreach ( $data as $table_name => $active_snippets ) { + foreach ( $active_snippets as $snippet ) { + $snippet_obj = get_snippet( $snippet['id'], $table_name === $db->ms_table ); + $this->handle_snippet( $snippet_obj, $table_name ); + } + } + } } diff --git a/src/php/settings/settings.php b/src/php/settings/settings.php index cf94f8b9..e3565b46 100644 --- a/src/php/settings/settings.php +++ b/src/php/settings/settings.php @@ -330,6 +330,8 @@ function sanitize_settings( array $input ): array { __( 'Settings saved.', 'code-snippets' ), 'updated' ); + + do_action( 'code_snippets/settings_updated', $settings, $input ); } return $settings; From 07f542bee02eda5b59fe8c4d2a71b1c3aa57bc1c Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Mon, 7 Jul 2025 22:04:13 +0200 Subject: [PATCH 37/54] wip --- .../classes/class-file-system-adapter.php | 8 ++++ .../classes/class-snippet-files.php | 44 +++++++++++-------- .../interfaces/interface-file-system.php | 2 + src/php/snippet-ops.php | 5 +-- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/php/flat-files/classes/class-file-system-adapter.php b/src/php/flat-files/classes/class-file-system-adapter.php index 6c230f13..c17cef28 100644 --- a/src/php/flat-files/classes/class-file-system-adapter.php +++ b/src/php/flat-files/classes/class-file-system-adapter.php @@ -36,4 +36,12 @@ public function mkdir( string $path, $chmod ) { public function rmdir( string $path, bool $recursive = false ): bool { return $this->fs->rmdir( $path, $recursive ); } + + public function chmod( string $path, $chmod ): bool { + return $this->fs->chmod( $path, $chmod ); + } + + public function is_writable( string $path ): bool { + return $this->fs->is_writable( $path ); + } } diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 18a68176..41f7b280 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -20,7 +20,11 @@ public function __construct( $this->config_repo = $config_repo; } - public function register_hooks() { + public function register_hooks(): void { + if ( ! $this->fs->is_writable( WP_CONTENT_DIR ) ) { + return; + } + if ( Settings\get_setting( 'general', 'enable_flat_files' ) ) { add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); @@ -35,7 +39,7 @@ public function register_hooks() { add_action( 'code_snippets/settings_updated', [ $this, 'create_all_flat_files' ], 10, 2 ); } - public function handle_snippet( Snippet $snippet, string $table ) { + public function handle_snippet( Snippet $snippet, string $table ): void { $snippet_type = $snippet->get_type(); $handler = $this->handler_registry->get_handler( $snippet_type ); @@ -55,7 +59,7 @@ public function handle_snippet( Snippet $snippet, string $table ) { $this->config_repo->update( $base_dir, $snippet ); } - public function delete_snippet( Snippet $snippet, bool $network ) { + public function delete_snippet( Snippet $snippet, bool $network ): void { $snippet_type = $snippet->get_type(); $handler = $this->handler_registry->get_handler( $snippet_type ); @@ -72,7 +76,7 @@ public function delete_snippet( Snippet $snippet, bool $network ) { $this->config_repo->update( $base_dir, $snippet, true ); } - public function activate_snippet( Snippet $snippet, bool $network ) { + public function activate_snippet( Snippet $snippet, bool $network ): void { $snippet = get_snippet( $snippet->id, $network ); $snippet_type = $snippet->get_type(); $handler = $this->handler_registry->get_handler( $snippet_type ); @@ -95,7 +99,7 @@ public function activate_snippet( Snippet $snippet, bool $network ) { $this->config_repo->update( $base_dir, $snippet ); } - public function deactivate_snippet( int $snippet_id, bool $network ) { + public function deactivate_snippet( int $snippet_id, bool $network ): void { $snippet = get_snippet( $snippet_id, $network ); $snippet_type = $snippet->get_type(); $handler = $this->handler_registry->get_handler( $snippet_type ); @@ -110,7 +114,7 @@ public function deactivate_snippet( int $snippet_id, bool $network ) { $this->config_repo->update( $base_dir, $snippet ); } - public static function get_base_dir( string $table = '', string $snippet_type = '' ) { + public static function get_base_dir( string $table = '', string $snippet_type = '' ): string { $base_dir = WP_CONTENT_DIR . '/code-snippets'; if ( ! empty( $table ) ) { @@ -124,23 +128,27 @@ public static function get_base_dir( string $table = '', string $snippet_type = return $base_dir; } - private function maybe_create_directory( string $dir ) { + private function maybe_create_directory( string $dir ): void { if ( ! $this->fs->is_dir( $dir ) ) { - $this->fs->mkdir( $dir, FS_CHMOD_DIR ); + $result = wp_mkdir_p( $dir ); + + if ( $result ) { + $this->fs->chmod( $dir, FS_CHMOD_DIR ); + } } } - private function get_snippet_file_path( string $base_dir, int $snippet_id, string $ext ) { + private function get_snippet_file_path( string $base_dir, int $snippet_id, string $ext ): string { return trailingslashit( $base_dir ) . $snippet_id . '.' . $ext; } - private function delete_file( string $file_path ) { + private function delete_file( string $file_path ): void { if ( $this->fs->exists( $file_path ) ) { $this->fs->delete( $file_path ); } } - public function sync_active_shared_network_snippets( $option, $old_value, $value ) { + public function sync_active_shared_network_snippets( $option, $old_value, $value ): void { if ( 'active_shared_network_snippets' !== $option ) { return; } @@ -156,7 +164,7 @@ public function sync_active_shared_network_snippets( $option, $old_value, $value $this->fs->put_contents( $file_path, $file_content, FS_CHMOD_FILE ); } - public static function get_active_snippets_from_flat_files() { + public static function get_active_snippets_from_flat_files( array $scopes = [] ): array { $snippets = []; $table = code_snippets()->db->get_table_name(); @@ -168,8 +176,8 @@ public static function get_active_snippets_from_flat_files() { $snippets[ $table ] = array_filter( $site_snippets, - function ( $snippet ) { - return $snippet['active']; + function ( $snippet ) use ( $scopes ) { + return $snippet['active'] && in_array( $snippet['scope'], $scopes, true ); } ); } @@ -188,8 +196,8 @@ function ( $snippet ) { $snippets[ $ms_table ] = array_filter( $ms_snippets, - function ( $snippet ) use ( $active_shared_ids ) { - return $snippet['active'] || in_array( intval( $snippet['id'] ), $active_shared_ids, true ); + function ( $snippet ) use ( $active_shared_ids, $scopes ) { + return ( $snippet['active'] || in_array( intval( $snippet['id'] ), $active_shared_ids, true ) ) && in_array( $snippet['scope'], $scopes, true ); } ); } @@ -198,7 +206,7 @@ function ( $snippet ) use ( $active_shared_ids ) { return $snippets; } - public function add_settings_fields( array $fields ) { + public function add_settings_fields( array $fields ): array { $fields['general']['enable_flat_files'] = [ 'name' => __( 'Enable Flat Files', 'code-snippets' ), 'type' => 'checkbox', @@ -208,7 +216,7 @@ public function add_settings_fields( array $fields ) { return $fields; } - public function create_all_flat_files( array $settings, array $input ) { + public function create_all_flat_files( array $settings, array $input ): void { if ( ! isset( $settings['general']['enable_flat_files'] ) ) { return; } diff --git a/src/php/flat-files/interfaces/interface-file-system.php b/src/php/flat-files/interfaces/interface-file-system.php index 7560d7bb..71b0efe2 100644 --- a/src/php/flat-files/interfaces/interface-file-system.php +++ b/src/php/flat-files/interfaces/interface-file-system.php @@ -9,4 +9,6 @@ public function delete( string $path ): bool; public function is_dir( string $path ): bool; public function mkdir( string $path, $chmod ); public function rmdir( string $path, bool $recursive = false ): bool; + public function chmod( string $path, $chmod ): bool; + public function is_writable( string $path ): bool; } diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 8fc90ff2..9cf86b91 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -686,7 +686,7 @@ function execute_active_snippets_from_flat_files(): bool { $db = code_snippets()->db; $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; - $data = Snippet_Files::get_active_snippets_from_flat_files(); + $data = Snippet_Files::get_active_snippets_from_flat_files( $scopes ); // Detect if a snippet is currently being edited, and if so, spare it from execution. $edit_id = 0; @@ -713,9 +713,6 @@ function execute_active_snippets_from_flat_files(): bool { // Loop through the returned snippets and execute the PHP code. foreach ( $active_snippets as $snippet ) { - if ( ! in_array( $snippet['scope'], $scopes, true ) ) { - continue; - } $snippet_id = intval( $snippet['id'] ); From 19708267fe80d473b9bed8a51e860bc55c1dc534 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Mon, 7 Jul 2025 22:15:22 +0200 Subject: [PATCH 38/54] wip --- .../flat-files/classes/class-snippet-files.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 41f7b280..0f98fa57 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -153,6 +153,10 @@ public function sync_active_shared_network_snippets( $option, $old_value, $value return; } + $this->create_active_shared_network_snippets_file( $value ); + } + + private function create_active_shared_network_snippets_file( $value ): void { $table = code_snippets()->db->get_table_name(); $base_dir = self::get_base_dir( $table ); @@ -225,6 +229,11 @@ public function create_all_flat_files( array $settings, array $input ): void { return; } + $this->create_snippet_flat_files(); + $this->create_active_shared_network_snippets_config_file(); + } + + private function create_snippet_flat_files(): void { $db = code_snippets()->db; $data = $db->fetch_active_snippets( Snippet::get_all_scopes() ); @@ -239,4 +248,11 @@ public function create_all_flat_files( array $settings, array $input ): void { } } } + + private function create_active_shared_network_snippets_config_file(): void { + $active_shared_network_snippets = get_option( 'active_shared_network_snippets' ); + if ( false !== $active_shared_network_snippets ) { + $this->create_active_shared_network_snippets_file( $active_shared_network_snippets ); + } + } } From 4cd5ee80783841433c9dc2c1b2a942b546c821ef Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Mon, 7 Jul 2025 22:24:41 +0200 Subject: [PATCH 39/54] wip --- src/php/snippet-ops.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 9cf86b91..9bdb7741 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -713,8 +713,8 @@ function execute_active_snippets_from_flat_files(): bool { // Loop through the returned snippets and execute the PHP code. foreach ( $active_snippets as $snippet ) { - $snippet_id = intval( $snippet['id'] ); + $code = $snippet['code']; // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { @@ -743,7 +743,7 @@ function execute_active_snippets_from_flat_files(): bool { if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { $file = $base_dir . '/' . $snippet_id . '.php'; - execute_snippet_from_flat_file( $file, $snippet_id ); + execute_snippet_from_flat_file( $code, $file, $snippet_id ); } } } @@ -759,8 +759,12 @@ function cs_sort_snippets_by_priority( array $snippets ): array { return $snippets; } -function execute_snippet_from_flat_file( $file, int $id = 0, bool $force = false ) { - if ( ! is_file( $file ) || ( ! $force && defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) ) { +function execute_snippet_from_flat_file( $code, $file, int $id = 0, bool $force = false ) { + if ( ! is_file( $file ) ) { + execute_snippet( $code, $id, $force ); + } + + if ( ! $force && defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) { return false; } From 4d84a24d1dad3ab9d40b62f71b3073ebf9a6be00 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Thu, 24 Jul 2025 22:11:39 +0200 Subject: [PATCH 40/54] sync all changes from pro --- src/php/class-active-snippets.php | 35 ++- src/php/class-plugin.php | 13 +- .../classes/class-config-repository.php | 12 +- .../classes/class-file-system-adapter.php | 4 +- .../classes/class-snippet-files.php | 212 ++++++++++++++---- .../handlers/php-snippet-handler.php | 18 +- src/php/flat-files/load.php | 2 +- src/php/front-end/class-front-end.php | 10 +- src/php/snippet-ops.php | 15 +- src/php/uninstall.php | 12 +- 10 files changed, 255 insertions(+), 78 deletions(-) diff --git a/src/php/class-active-snippets.php b/src/php/class-active-snippets.php index 7c1de751..b04f04b3 100644 --- a/src/php/class-active-snippets.php +++ b/src/php/class-active-snippets.php @@ -26,8 +26,15 @@ public function __construct() { * Initialise class functions. */ public function init() { - add_action( 'wp_head', [ $this, 'load_head_content' ] ); - add_action( 'wp_footer', [ $this, 'load_footer_content' ] ); + $should_use_flat_files = Settings\get_setting( 'general', 'enable_flat_files' ); + + if ( ! $should_use_flat_files ) { + add_action( 'wp_head', [ $this, 'load_head_content' ] ); + add_action( 'wp_footer', [ $this, 'load_footer_content' ] ); + } else { + add_action( 'wp_head', [ $this, 'load_head_content_from_flat_files' ] ); + add_action( 'wp_footer', [ $this, 'load_footer_content_from_flat_files' ] ); + } } /** @@ -78,4 +85,28 @@ public function load_head_content() { public function load_footer_content() { $this->print_content_snippets( 'footer-content' ); } + + public function load_head_content_from_flat_files() { + $this->load_content_snippets_from_flat_files( 'head-content' ); + } + + public function load_footer_content_from_flat_files() { + $this->load_content_snippets_from_flat_files( 'footer-content' ); + } + + private function load_content_snippets_from_flat_files( string $scope ) { + $handler = code_snippets()->snippet_handler_registry->get_handler( 'html' ); + $dir_name = $handler->get_dir_name(); + $ext = $handler->get_file_extension(); + $snippets = Snippet_Files::get_active_snippets_from_flat_files( [ $scope ], $dir_name ); + + foreach ( $snippets as $table_name => $active_snippets ) { + $active_snippets = cs_sort_snippets_by_priority( $active_snippets ); + $base_path = Snippet_Files::get_base_dir( $table_name, $dir_name ); + + foreach ( $active_snippets as $snippet ) { + require_once $base_path . '/' . $snippet['id'] . '.' . $ext; + } + } + } } diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index bb21473a..f6f2d8f8 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -68,6 +68,13 @@ class Plugin { */ public Licensing $licensing; + /** + * Handles snippet handler registration. + * + * @var Snippet_Handler_Registry + */ + public Snippet_Handler_Registry $snippet_handler_registry; + /** * Class constructor * @@ -122,7 +129,7 @@ public function load_plugin() { // Snippet files. require_once $includes_path . '/flat-files/load.php'; - $registry = new Snippet_Handler_Registry( [ + $this->snippet_handler_registry = new Snippet_Handler_Registry( [ 'php' => new Php_Snippet_Handler(), 'html' => new Html_Snippet_Handler(), ] ); @@ -131,10 +138,10 @@ public function load_plugin() { $config_repo = new Snippet_Config_Repository( $fs ); - ( new Snippet_Files( $registry, $fs, $config_repo ) )->register_hooks(); + ( new Snippet_Files( $this->snippet_handler_registry, $fs, $config_repo ) )->register_hooks(); $this->active_snippets = new Active_Snippets(); - $this->front_end = new Front_End( $registry ); + $this->front_end = new Front_End(); $this->cloud_api = new Cloud_API(); $upgrade = new Upgrade( $this->version, $this->db ); diff --git a/src/php/flat-files/classes/class-config-repository.php b/src/php/flat-files/classes/class-config-repository.php index 2087f572..74c0388f 100644 --- a/src/php/flat-files/classes/class-config-repository.php +++ b/src/php/flat-files/classes/class-config-repository.php @@ -14,14 +14,20 @@ public function __construct( File_System_Interface $fs ) { public function load( string $base_dir ): array { $config_file_path = trailingslashit( $base_dir ) . static::CONFIG_FILE_NAME; - return is_file( $config_file_path ) - ? require $config_file_path - : []; + if ( is_file( $config_file_path ) ) { + if ( function_exists( 'opcache_invalidate' ) ) { + opcache_invalidate( $config_file_path, true ); + } + return require $config_file_path; + } + return []; } public function save( string $base_dir, array $active_snippets ): void { $config_file_path = trailingslashit( $base_dir ) . static::CONFIG_FILE_NAME; + ksort( $active_snippets ); + $file_content = "fs->exists( $path ); } - public function delete( string $path ): bool { - return $this->fs->delete( $path ); + public function delete( $file, $recursive = false, $type = false ): bool { + return $this->fs->delete( $file, $recursive, $type ); } public function is_dir( string $path ): bool { diff --git a/src/php/flat-files/classes/class-snippet-files.php b/src/php/flat-files/classes/class-snippet-files.php index 0f98fa57..284c1677 100644 --- a/src/php/flat-files/classes/class-snippet-files.php +++ b/src/php/flat-files/classes/class-snippet-files.php @@ -31,15 +31,28 @@ public function register_hooks(): void { add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); + add_action( 'code_snippets/activate_snippets', [ $this, 'activate_snippets' ], 10, 2 ); add_action( 'updated_option', [ $this, 'sync_active_shared_network_snippets' ], 10, 3 ); + add_action( 'add_option', [ $this, 'sync_active_shared_network_snippets_add' ], 10, 2 ); } add_filter( 'code_snippets_settings_fields', [ $this, 'add_settings_fields' ], 10, 1 ); add_action( 'code_snippets/settings_updated', [ $this, 'create_all_flat_files' ], 10, 2 ); } + public function activate_snippets( $valid_snippets, $table ): void { + foreach ( $valid_snippets as $snippet ) { + $snippet->active = true; + $this->handle_snippet( $snippet, $table ); + } + } + public function handle_snippet( Snippet $snippet, string $table ): void { + if ( 0 === $snippet->id ) { + return; + } + $snippet_type = $snippet->get_type(); $handler = $this->handler_registry->get_handler( $snippet_type ); @@ -47,6 +60,7 @@ public function handle_snippet( Snippet $snippet, string $table ): void { return; } + $table = self::get_hashed_table_name( $table ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); @@ -67,7 +81,7 @@ public function delete_snippet( Snippet $snippet, bool $network ): void { return; } - $table = code_snippets()->db->get_table_name( $network ); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $file_path = $this->get_snippet_file_path( $base_dir, $snippet->id, $handler->get_file_extension() ); @@ -85,7 +99,7 @@ public function activate_snippet( Snippet $snippet, bool $network ): void { return; } - $table = code_snippets()->db->get_table_name( $network ); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); @@ -108,7 +122,7 @@ public function deactivate_snippet( int $snippet_id, bool $network ): void { return; } - $table = code_snippets()->db->get_table_name( $network ); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->config_repo->update( $base_dir, $snippet ); @@ -128,6 +142,20 @@ public static function get_base_dir( string $table = '', string $snippet_type = return $base_dir; } + public static function get_base_url( string $table = '', string $snippet_type = '' ): string { + $base_url = WP_CONTENT_URL . '/code-snippets'; + + if ( ! empty( $table ) ) { + $base_url .= '/' . $table; + } + + if ( ! empty( $snippet_type ) ) { + $base_url .= '/' . $snippet_type; + } + + return $base_url; + } + private function maybe_create_directory( string $dir ): void { if ( ! $this->fs->is_dir( $dir ) ) { $result = wp_mkdir_p( $dir ); @@ -156,8 +184,16 @@ public function sync_active_shared_network_snippets( $option, $old_value, $value $this->create_active_shared_network_snippets_file( $value ); } + public function sync_active_shared_network_snippets_add( $option, $value ): void { + if ( 'active_shared_network_snippets' !== $option ) { + return; + } + + $this->create_active_shared_network_snippets_file( $value ); + } + private function create_active_shared_network_snippets_file( $value ): void { - $table = code_snippets()->db->get_table_name(); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( false ) ); $base_dir = self::get_base_dir( $table ); $this->maybe_create_directory( $base_dir ); @@ -168,53 +204,104 @@ private function create_active_shared_network_snippets_file( $value ): void { $this->fs->put_contents( $file_path, $file_content, FS_CHMOD_FILE ); } - public static function get_active_snippets_from_flat_files( array $scopes = [] ): array { + public static function get_hashed_table_name( string $table ): string { + return wp_hash( $table ); + } + + public static function get_active_snippets_from_flat_files( + array $scopes = [], + $snippet_type = 'php' + ): array { $snippets = []; - $table = code_snippets()->db->get_table_name(); - $base_dir = self::get_base_dir( $table, 'php' ); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name() ); + $snippets[ $table ] = self::load_active_snippets_from_file( + $table, + $snippet_type, + $scopes + ); + + if ( is_multisite() ) { + $ms_table = self::get_hashed_table_name( code_snippets()->db->get_table_name( true ) ); + + $root_base_dir = self::get_base_dir( $table ); + $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; + $active_shared_ids = is_file( $active_shared_ids_file_path ) + ? require $active_shared_ids_file_path + : []; + + $snippets[ $ms_table ] = self::load_active_snippets_from_file( + $ms_table, + $snippet_type, + $scopes, + $active_shared_ids + ); + } + + return $snippets; + } + + private static function load_active_snippets_from_file( + string $table, + string $snippet_type, + array $scopes, + ?array $active_shared_ids = null + ): array { + $snippets = []; + $db = code_snippets()->db; + + $base_dir = self::get_base_dir( $table, $snippet_type ); $snippets_file_path = $base_dir . '/index.php'; - if ( is_file( $snippets_file_path ) ) { - $site_snippets = is_file( $snippets_file_path ) ? require $snippets_file_path : []; + if ( ! is_file( $snippets_file_path ) ) { + return $snippets; + } - $snippets[ $table ] = array_filter( - $site_snippets, - function ( $snippet ) use ( $scopes ) { - return $snippet['active'] && in_array( $snippet['scope'], $scopes, true ); - } - ); + $cache_key = sprintf( + 'active_snippets_%s_%s', + sanitize_key( join( '_', $scopes ) ), + self::get_hashed_table_name( $db->table ) === $table ? $db->table : $db->ms_table + ); + + $cached_snippets = wp_cache_get( $cache_key, CACHE_GROUP ); + + if ( is_array( $cached_snippets ) ) { + return $cached_snippets; } - if ( is_multisite() ) { - $ms_table = code_snippets()->db->get_table_name( true ); - $ms_base_dir = self::get_base_dir( $ms_table, 'php' ); - $ms_snippets_file_path = $ms_base_dir . '/index.php'; + $file_snippets = require $snippets_file_path; - if ( is_file( $ms_snippets_file_path ) ) { - $ms_snippets = is_file( $ms_snippets_file_path ) ? require $ms_snippets_file_path : []; + $filtered_snippets = array_filter( + $file_snippets, + function ( $snippet ) use ( $scopes, $active_shared_ids ) { + $is_active = $snippet['active']; - $root_base_dir = self::get_base_dir( $table ); - $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; - $active_shared_ids = is_file( $active_shared_ids_file_path ) ? require $active_shared_ids_file_path : []; + if ( null !== $active_shared_ids ) { + $is_active = $is_active || in_array( + intval( $snippet['id'] ), + $active_shared_ids, + true + ); + } - $snippets[ $ms_table ] = array_filter( - $ms_snippets, - function ( $snippet ) use ( $active_shared_ids, $scopes ) { - return ( $snippet['active'] || in_array( intval( $snippet['id'] ), $active_shared_ids, true ) ) && in_array( $snippet['scope'], $scopes, true ); - } - ); + return $is_active && in_array( $snippet['scope'], $scopes, true ); } - } + ); - return $snippets; + wp_cache_set( $cache_key, $filtered_snippets, CACHE_GROUP ); + + return $filtered_snippets; } public function add_settings_fields( array $fields ): array { $fields['general']['enable_flat_files'] = [ - 'name' => __( 'Enable Flat Files', 'code-snippets' ), + 'name' => __( 'Enable file-based execution', 'code-snippets' ), 'type' => 'checkbox', - 'label' => __( 'Snippets will be executed from flat files instead of the database.', 'code-snippets' ), + 'label' => __( 'Snippets will be executed directly from files instead of the database.', 'code-snippets' ) . ' ' . sprintf( + '%s', + esc_url( 'https://codesnippets.pro/doc/file-based-execution/' ), + __( 'Learn more.', 'code-snippets' ) + ), ]; return $fields; @@ -235,24 +322,65 @@ public function create_all_flat_files( array $settings, array $input ): void { private function create_snippet_flat_files(): void { $db = code_snippets()->db; - $data = $db->fetch_active_snippets( Snippet::get_all_scopes() ); - if ( empty( $data ) ) { - return; - } + $scopes = Snippet::get_all_scopes(); + + $data = $db->fetch_active_snippets( $scopes ); foreach ( $data as $table_name => $active_snippets ) { foreach ( $active_snippets as $snippet ) { - $snippet_obj = get_snippet( $snippet['id'], $table_name === $db->ms_table ); + $snippet_obj = get_snippet( $snippet['id'], $db->ms_table === $table_name ); $this->handle_snippet( $snippet_obj, $table_name ); } } + + if ( is_multisite() ) { + $current_blog_id = get_current_blog_id(); + + $sites = get_sites( [ 'fields' => 'ids' ] ); + foreach ( $sites as $site_id ) { + switch_to_blog( $site_id ); + $db->set_table_vars(); + + $site_data = $db->fetch_active_snippets( $scopes ); + foreach ( $site_data as $table_name => $active_snippets ) { + foreach ( $active_snippets as $snippet ) { + $snippet_obj = get_snippet( $snippet['id'], false ); + $this->handle_snippet( $snippet_obj, $table_name ); + } + } + + restore_current_blog(); + } + + $db->set_table_vars(); + } } private function create_active_shared_network_snippets_config_file(): void { - $active_shared_network_snippets = get_option( 'active_shared_network_snippets' ); - if ( false !== $active_shared_network_snippets ) { - $this->create_active_shared_network_snippets_file( $active_shared_network_snippets ); + if ( is_multisite() ) { + $current_blog_id = get_current_blog_id(); + $sites = get_sites( [ 'fields' => 'ids' ] ); + $db = code_snippets()->db; + + foreach ( $sites as $site_id ) { + switch_to_blog( $site_id ); + $db->set_table_vars(); + + $active_shared_network_snippets = get_option( 'active_shared_network_snippets' ); + if ( false !== $active_shared_network_snippets ) { + $this->create_active_shared_network_snippets_file( $active_shared_network_snippets ); + } + + restore_current_blog(); + } + + $db->set_table_vars(); + } else { + $active_shared_network_snippets = get_option( 'active_shared_network_snippets' ); + if ( false !== $active_shared_network_snippets ) { + $this->create_active_shared_network_snippets_file( $active_shared_network_snippets ); + } } } } diff --git a/src/php/flat-files/handlers/php-snippet-handler.php b/src/php/flat-files/handlers/php-snippet-handler.php index 666617b0..5d306392 100644 --- a/src/php/flat-files/handlers/php-snippet-handler.php +++ b/src/php/flat-files/handlers/php-snippet-handler.php @@ -3,15 +3,15 @@ namespace Code_Snippets; class Php_Snippet_Handler implements Snippet_Type_Handler_Interface { - public function get_file_extension(): string { - return 'php'; - } + public function get_file_extension(): string { + return 'php'; + } - public function get_dir_name(): string { - return 'php'; - } + public function get_dir_name(): string { + return 'php'; + } - public function wrap_code( string $code ): string { - return "handler_registry = $handler_registry; - + public function __construct() { add_action( 'the_posts', [ $this, 'enqueue_highlighting' ] ); add_action( 'init', [ $this, 'setup_mce_plugin' ] ); @@ -245,9 +241,9 @@ protected function convert_boolean_attribute_flags( array $atts, array $boolean_ * @return string Full file path for the snippet. */ private function build_snippet_flat_file_path( string $table_name, Snippet $snippet ): string { - $handler = $this->handler_registry->get_handler( $snippet->get_type() ); + $handler = code_snippets()->snippet_handler_registry->get_handler( $snippet->get_type() ); - return WP_CONTENT_DIR . '/code-snippets/' . $table_name . '/' . $handler->get_dir_name() . '/' . $snippet->id . '.' . $handler->get_file_extension(); + return Snippet_Files::get_base_dir( $table_name, $handler->get_dir_name() ) . '/' . $snippet->id . '.' . $handler->get_file_extension(); } /** diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 9bdb7741..f3789915 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -690,7 +690,7 @@ function execute_active_snippets_from_flat_files(): bool { // Detect if a snippet is currently being edited, and if so, spare it from execution. $edit_id = 0; - $edit_table = $db->table; + $edit_table = Snippet_Files::get_hashed_table_name( $db->table ); if ( wp_is_json_request() && ! empty( $_SERVER['REQUEST_URI'] ) ) { $url = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); @@ -702,7 +702,7 @@ function execute_active_snippets_from_flat_files(): bool { if ( ! empty( $url['query'] ) ) { wp_parse_str( $url['query'], $path_params ); $edit_table = isset( $path_params['network'] ) && rest_sanitize_boolean( $path_params['network'] ) ? - $db->ms_table : $db->table; + Snippet_Files::get_hashed_table_name( $db->ms_table ) : Snippet_Files::get_hashed_table_name( $db->table ); } } } @@ -718,24 +718,25 @@ function execute_active_snippets_from_flat_files(): bool { // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { + $table_to_update = Snippet_Files::get_hashed_table_name( $db->table ) === $table_name ? $db->table : $db->ms_table; $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); - if ( $table_name === $db->ms_table && is_array( $active_shared_ids ) && in_array( $snippet_id, $active_shared_ids, true ) ) { + if ( Snippet_Files::get_hashed_table_name( $db->ms_table ) === $table_name && is_array( $active_shared_ids ) && in_array( $snippet_id, $active_shared_ids, true ) ) { unset( $active_shared_ids[ array_search( $snippet_id, $active_shared_ids, true ) ] ); $active_shared_ids = array_values( $active_shared_ids ); update_option( 'active_shared_network_snippets', $active_shared_ids ); - clean_active_snippets_cache( $table_name ); + clean_active_snippets_cache( $table_to_update ); } else { $wpdb->update( - $table_name, + $table_to_update, array( 'active' => '0' ), array( 'id' => $snippet_id ), array( '%d' ), array( '%d' ) ); - clean_snippets_cache( $table_name ); + clean_snippets_cache( $table_to_update ); - $network = $table_name === $db->ms_table; + $network = Snippet_Files::get_hashed_table_name( $db->ms_table ) === $table_name; do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); } } diff --git a/src/php/uninstall.php b/src/php/uninstall.php index 028aca5c..5afaefb6 100644 --- a/src/php/uninstall.php +++ b/src/php/uninstall.php @@ -79,8 +79,16 @@ function delete_flat_files_directory() { return; } - $fs = new \Code_Snippets\WordPress_Filesystem_Adapter(); - $fs->rmdir( $flat_files_dir, true ); + if ( ! function_exists( 'request_filesystem_credentials' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + + global $wp_filesystem; + WP_Filesystem(); + + if ( $wp_filesystem && $wp_filesystem->is_dir( $flat_files_dir ) ) { + $wp_filesystem->delete( $flat_files_dir, true ); + } } /** From 4e16d4552b8bdfee7e134740be7fe68fa821de7c Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 29 Aug 2025 17:44:06 +1000 Subject: [PATCH 41/54] Update version number and changelog for v3.7.0. --- CHANGELOG.md | 3 ++- package-lock.json | 4 ++-- package.json | 2 +- src/code-snippets.php | 6 +++--- src/readme.txt | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a78539a6..8f1dcd7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0] (beta release) +## [3.7.0] (2025-08-29) ### Added * New 'conditions' feature: control where and when snippets execute with a powerful logic builder. (PRO) @@ -1210,6 +1210,7 @@ [brandonjp]: https://github.com/brandonjp [unreleased]: https://github.com/codesnippetspro/code-snippets/tree/core +[3.7.0]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.7.0 [3.6.7]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.6.7 [3.6.6.1]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.6.6.1 [3.6.6]: https://github.com/codesnippetspro/code-snippets/releases/tag/v3.6.6 diff --git a/package-lock.json b/package-lock.json index 2f804753..98d143df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-snippets", - "version": "3.7.0-beta.7", + "version": "3.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-snippets", - "version": "3.7.0-beta.7", + "version": "3.7.0", "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", diff --git a/package.json b/package.json index 71f583ad..2c5dbb32 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-snippets", "description": "Manage code snippets running on a WordPress-powered site through a graphical interface.", "homepage": "https://codesnippets.pro", - "version": "3.7.0-beta.7", + "version": "3.7.0", "main": "src/dist/edit.js", "directories": { "test": "tests" diff --git a/src/code-snippets.php b/src/code-snippets.php index b2b9b8d4..d5a7ce77 100644 --- a/src/code-snippets.php +++ b/src/code-snippets.php @@ -8,11 +8,11 @@ * License: GPL-2.0-or-later * License URI: license.txt * Text Domain: code-snippets - * Version: 3.7.0-beta.7 + * Version: 3.7.0 * Requires PHP: 7.4 * Requires at least: 5.0 * - * @version 3.7.0-beta.7 + * @version 3.7.0 * @package Code_Snippets * @author Shea Bunge * @copyright 2012-2024 Code Snippets Pro @@ -37,7 +37,7 @@ * * @const string */ - define( 'CODE_SNIPPETS_VERSION', '3.7.0-beta.7' ); + define( 'CODE_SNIPPETS_VERSION', '3.7.0' ); /** * The full path to the main file of this plugin. diff --git a/src/readme.txt b/src/readme.txt index 8f0410f5..5798c93e 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -4,7 +4,7 @@ Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later License URI: license.txt -Stable tag: 3.7.0-beta.7 +Stable tag: 3.7.0 Tested up to: 6.8.2 An easy, clean and simple way to enhance your site with code snippets. @@ -104,7 +104,7 @@ You can report security bugs found in the source code of this plugin through the == Changelog == -= 3.7.0 (beta release) = += 3.7.0 (2025-08-29) = __Added__ From 68d0d3af6cd54fc5a331d09b3be7de5225ae76db Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Tue, 7 Oct 2025 23:03:18 +0200 Subject: [PATCH 42/54] sync changes from pro --- src/php/class-active-snippets.php | 112 ---------- src/php/class-plugin.php | 8 +- src/php/class-snippet.php | 13 -- src/php/evaluation/class-evaluate-content.php | 54 ++++- .../evaluation/class-evaluate-functions.php | 51 +++++ .../classes/class-config-repository.php | 6 +- .../classes/class-file-system-adapter.php | 2 +- .../classes/class-snippet-files.php | 150 ++++++++++--- .../interfaces/interface-file-system.php | 2 +- src/php/flat-files/load.php | 17 -- src/php/front-end/class-front-end.php | 33 ++- src/php/load.php | 10 - src/php/snippet-ops.php | 197 ++---------------- 13 files changed, 275 insertions(+), 380 deletions(-) delete mode 100644 src/php/class-active-snippets.php delete mode 100644 src/php/flat-files/load.php diff --git a/src/php/class-active-snippets.php b/src/php/class-active-snippets.php deleted file mode 100644 index b04f04b3..00000000 --- a/src/php/class-active-snippets.php +++ /dev/null @@ -1,112 +0,0 @@ -active_snippets[ $scope_key ] ) ) { - $this->active_snippets[ $scope_key ] = code_snippets()->db->fetch_active_snippets( $scope ); - } - - return $this->active_snippets[ $scope_key ]; - } - - /** - * Print snippet code fetched from the database from a certain scope. - * - * @param string $scope Name of scope to print. - */ - private function print_content_snippets( string $scope ) { - $snippets_list = $this->fetch_active_snippets( [ 'head-content', 'footer-content' ] ); - - foreach ( $snippets_list as $snippets ) { - foreach ( $snippets as $snippet ) { - if ( $scope === $snippet['scope'] ) { - // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped - echo "\n", $snippet['code'], "\n"; - } - } - } - } - - /** - * Print head content snippets. - */ - public function load_head_content() { - $this->print_content_snippets( 'head-content' ); - } - - /** - * Print footer content snippets. - */ - public function load_footer_content() { - $this->print_content_snippets( 'footer-content' ); - } - - public function load_head_content_from_flat_files() { - $this->load_content_snippets_from_flat_files( 'head-content' ); - } - - public function load_footer_content_from_flat_files() { - $this->load_content_snippets_from_flat_files( 'footer-content' ); - } - - private function load_content_snippets_from_flat_files( string $scope ) { - $handler = code_snippets()->snippet_handler_registry->get_handler( 'html' ); - $dir_name = $handler->get_dir_name(); - $ext = $handler->get_file_extension(); - $snippets = Snippet_Files::get_active_snippets_from_flat_files( [ $scope ], $dir_name ); - - foreach ( $snippets as $table_name => $active_snippets ) { - $active_snippets = cs_sort_snippets_by_priority( $active_snippets ); - $base_path = Snippet_Files::get_base_dir( $table_name, $dir_name ); - - foreach ( $active_snippets as $snippet ) { - require_once $base_path . '/' . $snippet['id'] . '.' . $ext; - } - } - } -} diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index 24a93346..1abe2c71 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -137,24 +137,18 @@ public function load_plugin() { // Cloud List Table shared functions. require_once $includes_path . '/cloud/list-table-shared-ops.php'; -<<<<<<< HEAD // Snippet files. - require_once $includes_path . '/flat-files/load.php'; - $this->snippet_handler_registry = new Snippet_Handler_Registry( [ 'php' => new Php_Snippet_Handler(), 'html' => new Html_Snippet_Handler(), ] ); - $fs = new WordPress_Filesystem_Adapter(); + $fs = new WordPress_File_System_Adapter(); $config_repo = new Snippet_Config_Repository( $fs ); ( new Snippet_Files( $this->snippet_handler_registry, $fs, $config_repo ) )->register_hooks(); - $this->active_snippets = new Active_Snippets(); -======= ->>>>>>> core $this->front_end = new Front_End(); $this->cloud_api = new Cloud_API(); diff --git a/src/php/class-snippet.php b/src/php/class-snippet.php index f61ada57..bd3269ff 100644 --- a/src/php/class-snippet.php +++ b/src/php/class-snippet.php @@ -189,21 +189,8 @@ public static function get_type_from_scope( string $scope ): string { * * @return string The snippet type – will be a filename extension. */ -<<<<<<< HEAD public function get_type(): string { - if ( '-css' === substr( $this->scope, -4 ) ) { - return 'css'; - } elseif ( '-js' === substr( $this->scope, -3 ) ) { - return 'js'; - } elseif ( 'content' === substr( $this->scope, -7 ) ) { - return 'html'; - } else { - return 'php'; - } -======= - protected function get_type(): string { return self::get_type_from_scope( $this->scope ); ->>>>>>> core } /** diff --git a/src/php/evaluation/class-evaluate-content.php b/src/php/evaluation/class-evaluate-content.php index f76df4c8..da7a0779 100644 --- a/src/php/evaluation/class-evaluate-content.php +++ b/src/php/evaluation/class-evaluate-content.php @@ -4,6 +4,9 @@ use Code_Snippets\DB; use Code_Snippets\Snippet; +use Code_Snippets\Settings; +use Code_Snippets\Snippet_Files; +use function Code_Snippets\code_snippets; /** * Class for evaluating content snippets. @@ -40,8 +43,13 @@ public function __construct( DB $db ) { * Initialise class functions. */ public function init() { - add_action( 'wp_head', [ $this, 'load_head_content' ] ); - add_action( 'wp_footer', [ $this, 'load_footer_content' ] ); + if ( Snippet_Files::is_active() ) { + add_action( 'wp_head', [ $this, 'load_head_content_from_flat_files' ] ); + add_action( 'wp_footer', [ $this, 'load_footer_content_from_flat_files' ] ); + } else { + add_action( 'wp_head', [ $this, 'load_head_content' ] ); + add_action( 'wp_footer', [ $this, 'load_footer_content' ] ); + } } /** @@ -77,4 +85,46 @@ public function load_head_content() { public function load_footer_content() { $this->print_content_snippets( 'footer-content' ); } + + public function load_head_content_from_flat_files() { + $this->load_content_snippets_from_flat_files( 'head-content' ); + } + + public function load_footer_content_from_flat_files() { + $this->load_content_snippets_from_flat_files( 'footer-content' ); + } + + private function populate_active_snippets_from_flat_files() { + $handler = code_snippets()->snippet_handler_registry->get_handler( 'html' ); + $dir_name = $handler->get_dir_name(); + $ext = $handler->get_file_extension(); + + $scopes = [ 'head-content', 'footer-content' ]; + $all_snippets = Snippet_Files::get_active_snippets_from_flat_files( $scopes, $dir_name ); + + foreach ( $all_snippets as $snippet ) { + $scope = $snippet['scope']; + + // Add file path information to the snippet for later use + $table_name = Snippet_Files::get_hashed_table_name( $snippet['table'] ); + $base_path = Snippet_Files::get_base_dir( $table_name, $dir_name ); + $snippet['file_path'] = $base_path . '/' . $snippet['id'] . '.' . $ext; + + $this->active_snippets[ $scope ][] = $snippet; + } + } + + private function load_content_snippets_from_flat_files( string $scope ) { + if ( is_null( $this->active_snippets ) ) { + $this->populate_active_snippets_from_flat_files(); + } + + if ( ! isset( $this->active_snippets[ $scope ] ) ) { + return; + } + + foreach ( $this->active_snippets[ $scope ] as $snippet ) { + require_once $snippet['file_path']; + } + } } diff --git a/src/php/evaluation/class-evaluate-functions.php b/src/php/evaluation/class-evaluate-functions.php index 0009961e..eca66a08 100644 --- a/src/php/evaluation/class-evaluate-functions.php +++ b/src/php/evaluation/class-evaluate-functions.php @@ -4,9 +4,13 @@ use Code_Snippets\DB; use Code_Snippets\REST_API\Snippets_REST_Controller; +use Code_Snippets\Settings; +use Code_Snippets\Snippet_Files; use function Code_Snippets\clean_active_snippets_cache; use function Code_Snippets\clean_snippets_cache; use function Code_Snippets\execute_snippet; +use function Code_Snippets\code_snippets; +use function Code_Snippets\execute_snippet_from_flat_file; /** * Class for evaluating functions snippets. @@ -104,6 +108,28 @@ private function quick_deactivate_snippet( int $snippet_id, string $table_name ) [ '%d' ] ); clean_snippets_cache( $table_name ); + + $network = $table_name === $this->db->ms_table; + do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); + } + } + + private function evaluate_snippet_flat_file( array $snippet, string $file_path, ?array $edit_snippet = null ) { + $snippet_id = $snippet['id']; + $code = $snippet['code']; + $table_name = $snippet['table']; + + // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. + if ( 'single-use' === $snippet['scope'] ) { + $this->quick_deactivate_snippet( $snippet_id, $table_name ); + } + + if ( ! is_null( $edit_snippet ) && $edit_snippet['id'] === $snippet_id && $edit_snippet['table'] === $table_name ) { + return; + } + + if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) ) { + execute_snippet_from_flat_file( $code, $file_path, $snippet_id ); } } @@ -117,6 +143,14 @@ public function evaluate_early(): bool { return false; } + if ( Snippet_Files::is_active() ) { + return $this->evaluate_file_snippets(); + } + + return $this->evaluate_db_snippets(); + } + + public function evaluate_db_snippets(): bool { $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; $active_snippets = $this->db->fetch_active_snippets( $scopes ); $edit_snippet = $this->get_currently_editing_snippet(); @@ -139,4 +173,21 @@ public function evaluate_early(): bool { return true; } + + private function evaluate_file_snippets(): bool { + $type = 'php'; + $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; + $snippets = Snippet_Files::get_active_snippets_from_flat_files( $scopes, $type ); + $edit_snippet = $this->get_currently_editing_snippet(); + + foreach ( $snippets as $snippet ) { + $table_name = Snippet_Files::get_hashed_table_name( $snippet['table'] ); + $base_path = Snippet_Files::get_base_dir( $table_name, $type ); + $file = $base_path . '/' . $snippet['id'] . '.' . $type; + + $this->evaluate_snippet_flat_file( $snippet, $file, $edit_snippet ); + } + + return true; + } } diff --git a/src/php/flat-files/classes/class-config-repository.php b/src/php/flat-files/classes/class-config-repository.php index 74c0388f..0ec25d90 100644 --- a/src/php/flat-files/classes/class-config-repository.php +++ b/src/php/flat-files/classes/class-config-repository.php @@ -38,10 +38,10 @@ public function save( string $base_dir, array $active_snippets ): void { public function update( string $base_dir, Snippet $snippet, ?bool $remove = false ): void { $active_snippets = $this->load( $base_dir ); - if ( ! $remove ) { - $active_snippets[ $snippet->id ] = $snippet->get_fields(); - } else { + if ( $remove ) { unset( $active_snippets[ $snippet->id ] ); + } else { + $active_snippets[ $snippet->id ] = $snippet->get_fields(); } $this->save( $base_dir, $active_snippets ); diff --git a/src/php/flat-files/classes/class-file-system-adapter.php b/src/php/flat-files/classes/class-file-system-adapter.php index 290f707c..62055253 100644 --- a/src/php/flat-files/classes/class-file-system-adapter.php +++ b/src/php/flat-files/classes/class-file-system-adapter.php @@ -1,7 +1,7 @@ config_repo = $config_repo; } + /** + * Check if flat files are enabled by checking for the flag file. + * This avoids database calls for better performance. + * + * @return bool True if flat files are enabled, false otherwise. + */ + public static function is_active(): bool { + $flag_file_path = self::get_flag_file_path(); + return file_exists( $flag_file_path ); + } + + private static function get_flag_file_path(): string { + return self::get_base_dir() . '/' . self::ENABLED_FLAG_FILE; + } + + private function handle_enabled_file_flag( bool $enabled ): void { + $flag_file_path = self::get_flag_file_path(); + + if ( $enabled ) { + $base_dir = self::get_base_dir(); + $this->maybe_create_directory( $base_dir ); + + $this->fs->put_contents( $flag_file_path, '', FS_CHMOD_FILE ); + } else { + $this->delete_file( $flag_file_path ); + } + } + public function register_hooks(): void { if ( ! $this->fs->is_writable( WP_CONTENT_DIR ) ) { return; } - if ( Settings\get_setting( 'general', 'enable_flat_files' ) ) { + if ( self::is_active() ) { add_action( 'code_snippets/create_snippet', [ $this, 'handle_snippet' ], 10, 2 ); add_action( 'code_snippets/update_snippet', [ $this, 'handle_snippet' ], 10, 2 ); add_action( 'code_snippets/delete_snippet', [ $this, 'delete_snippet' ], 10, 2 ); - add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 2 ); + add_action( 'code_snippets/activate_snippet', [ $this, 'activate_snippet' ], 10, 1 ); add_action( 'code_snippets/deactivate_snippet', [ $this, 'deactivate_snippet' ], 10, 2 ); add_action( 'code_snippets/activate_snippets', [ $this, 'activate_snippets' ], 10, 2 ); @@ -53,8 +86,7 @@ public function handle_snippet( Snippet $snippet, string $table ): void { return; } - $snippet_type = $snippet->get_type(); - $handler = $this->handler_registry->get_handler( $snippet_type ); + $handler = $this->handler_registry->get_handler( $snippet->type ); if ( ! $handler ) { return; @@ -74,8 +106,7 @@ public function handle_snippet( Snippet $snippet, string $table ): void { } public function delete_snippet( Snippet $snippet, bool $network ): void { - $snippet_type = $snippet->get_type(); - $handler = $this->handler_registry->get_handler( $snippet_type ); + $handler = $this->handler_registry->get_handler( $snippet->type ); if ( ! $handler ) { return; @@ -90,16 +121,15 @@ public function delete_snippet( Snippet $snippet, bool $network ): void { $this->config_repo->update( $base_dir, $snippet, true ); } - public function activate_snippet( Snippet $snippet, bool $network ): void { - $snippet = get_snippet( $snippet->id, $network ); - $snippet_type = $snippet->get_type(); - $handler = $this->handler_registry->get_handler( $snippet_type ); + public function activate_snippet( Snippet $snippet ): void { + $snippet = get_snippet( $snippet->id, $snippet->network ); + $handler = $this->handler_registry->get_handler( $snippet->type ); if ( ! $handler ) { return; } - $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $snippet->network ) ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); $this->maybe_create_directory( $base_dir ); @@ -115,8 +145,7 @@ public function activate_snippet( Snippet $snippet, bool $network ): void { public function deactivate_snippet( int $snippet_id, bool $network ): void { $snippet = get_snippet( $snippet_id, $network ); - $snippet_type = $snippet->get_type(); - $handler = $this->handler_registry->get_handler( $snippet_type ); + $handler = $this->handler_registry->get_handler( $snippet->type ); if ( ! $handler ) { return; @@ -212,17 +241,32 @@ public static function get_active_snippets_from_flat_files( array $scopes = [], $snippet_type = 'php' ): array { - $snippets = []; + $active_snippets = []; + $db = code_snippets()->db; - $table = self::get_hashed_table_name( code_snippets()->db->get_table_name() ); - $snippets[ $table ] = self::load_active_snippets_from_file( + $table = self::get_hashed_table_name( $db->get_table_name() ); + $snippets = self::load_active_snippets_from_file( $table, $snippet_type, $scopes ); + if ( $snippets ) { + foreach ( $snippets as $snippet ) { + $active_snippets[] = [ + 'id' => intval( $snippet['id'] ), + 'code' => $snippet['code'], + 'scope' => $snippet['scope'], + 'table' => $db->table, + 'network' => false, + 'priority' => intval( $snippet['priority'] ), + 'condition_id' => intval( $snippet['condition_id'] ), + ]; + } + } + if ( is_multisite() ) { - $ms_table = self::get_hashed_table_name( code_snippets()->db->get_table_name( true ) ); + $ms_table = self::get_hashed_table_name( $db->get_table_name( true ) ); $root_base_dir = self::get_base_dir( $table ); $active_shared_ids_file_path = $root_base_dir . '/active-shared-network-snippets.php'; @@ -230,15 +274,71 @@ public static function get_active_snippets_from_flat_files( ? require $active_shared_ids_file_path : []; - $snippets[ $ms_table ] = self::load_active_snippets_from_file( + $ms_snippets = self::load_active_snippets_from_file( $ms_table, $snippet_type, $scopes, $active_shared_ids ); + + if ( $ms_snippets ) { + $active_shared_ids = is_array( $active_shared_ids ) + ? array_map( 'intval', $active_shared_ids ) + : []; + + foreach ( $ms_snippets as $snippet ) { + $id = intval( $snippet['id'] ); + + if ( ! $snippet['active'] && ! in_array( $id, $active_shared_ids, true ) ) { + continue; + } + + $active_snippets[] = [ + 'id' => $id, + 'code' => $snippet['code'], + 'scope' => $snippet['scope'], + 'table' => $db->ms_table, + 'network' => true, + 'priority' => intval( $snippet['priority'] ), + 'condition_id' => intval( $snippet['condition_id'] ), + ]; + } + + self::sort_active_snippets( $active_snippets, $db ); + } } - return $snippets; + return $active_snippets; + } + + private static function sort_active_snippets( array &$active_snippets, $db ): void { + $comparisons = [ + function ( array $a, array $b ) { + return $a['priority'] <=> $b['priority']; + }, + function ( array $a, array $b ) use ( $db ) { + $a_table = $a['table'] === $db->ms_table ? 0 : 1; + $b_table = $b['table'] === $db->ms_table ? 0 : 1; + return $a_table <=> $b_table; + }, + function ( array $a, array $b ) { + return $a['id'] <=> $b['id']; + }, + ]; + + usort( + $active_snippets, + static function ( $a, $b ) use ( $comparisons ) { + foreach ( $comparisons as $comparison ) { + $result = $comparison( $a, $b ); + if ( 0 !== $result ) { + return $result; + } + } + + return 0; + } + ); } private static function load_active_snippets_from_file( @@ -284,7 +384,7 @@ function ( $snippet ) use ( $scopes, $active_shared_ids ) { ); } - return $is_active && in_array( $snippet['scope'], $scopes, true ); + return ( $is_active || 'condition' === $snippet['scope'] ) && in_array( $snippet['scope'], $scopes, true ); } ); @@ -312,6 +412,8 @@ public function create_all_flat_files( array $settings, array $input ): void { return; } + $this->handle_enabled_file_flag( $settings['general']['enable_flat_files'] ); + if ( ! $settings['general']['enable_flat_files'] ) { return; } @@ -327,11 +429,9 @@ private function create_snippet_flat_files(): void { $data = $db->fetch_active_snippets( $scopes ); - foreach ( $data as $table_name => $active_snippets ) { - foreach ( $active_snippets as $snippet ) { - $snippet_obj = get_snippet( $snippet['id'], $db->ms_table === $table_name ); - $this->handle_snippet( $snippet_obj, $table_name ); - } + foreach ( $data as $snippet ) { + $snippet_obj = get_snippet( $snippet['id'], $db->ms_table === $snippet['table'] ); + $this->handle_snippet( $snippet_obj, $snippet['table'] ); } if ( is_multisite() ) { diff --git a/src/php/flat-files/interfaces/interface-file-system.php b/src/php/flat-files/interfaces/interface-file-system.php index 71b0efe2..b4850beb 100644 --- a/src/php/flat-files/interfaces/interface-file-system.php +++ b/src/php/flat-files/interfaces/interface-file-system.php @@ -5,7 +5,7 @@ interface File_System_Interface { public function put_contents( string $path, string $contents, $chmod ); public function exists( string $path ): bool; - public function delete( string $path ): bool; + public function delete( $file, $recursive = false, $type = false ): bool; public function is_dir( string $path ): bool; public function mkdir( string $path, $chmod ); public function rmdir( string $path, bool $recursive = false ): bool; diff --git a/src/php/flat-files/load.php b/src/php/flat-files/load.php deleted file mode 100644 index 79d551bc..00000000 --- a/src/php/flat-files/load.php +++ /dev/null @@ -1,17 +0,0 @@ -code; } - $should_execute_from_flat_files = Settings\get_setting( 'general', 'enable_flat_files' ); - - if ( ! $should_execute_from_flat_files ) { + if ( ! Snippet_Files::is_active() ) { return $this->evaluate_shortcode_from_db( $snippet, $atts ); } $network = DB::validate_network_param( $snippet->network ); - $table_name = code_snippets()->db->get_table_name( $network ); + $table_name = Snippet_Files::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); $filepath = $this->build_snippet_flat_file_path( $table_name, $snippet ); return file_exists( $filepath ) @@ -301,6 +299,29 @@ private function evaluate_shortcode_from_flat_file( $filepath, array $atts ): st return ob_get_clean(); } + private function get_snippet( int $id, bool $network, string $snippet_type ): Snippet { + if ( ! Snippet_Files::is_active() ) { + return get_snippet( $id, $network ); + } + + $validated_network = DB::validate_network_param( $network ); + $table_name = Snippet_Files::get_hashed_table_name( code_snippets()->db->get_table_name( $validated_network ) ); + $handler = code_snippets()->snippet_handler_registry->get_handler( $snippet_type ); + $config_filepath = Snippet_Files::get_base_dir( $table_name, $handler->get_dir_name() ) . '/index.php'; + + if ( file_exists( $config_filepath ) ) { + $config = require_once $config_filepath; + $snippet_data = $config[ $id ] ?? null; + + if ( $snippet_data ) { + $snippet = new Snippet( $snippet_data ); + return apply_filters( 'code_snippets/get_snippet', $snippet, $id, $network ); + } + } + + return get_snippet( $id, $network ); + } + /** * Render the value of a content shortcode * @@ -331,7 +352,7 @@ public function render_content_shortcode( array $atts ): string { return $this->invalid_id_warning( $id ); } - $snippet = get_snippet( $id, (bool) $atts['network'] ); + $snippet = $this->get_snippet( $id, (bool) $atts['network'], 'html' ); // Render the source code if this is not a shortcode snippet. if ( 'content' !== $snippet->scope ) { @@ -473,7 +494,7 @@ public function render_source_shortcode( array $atts ): string { return $this->invalid_id_warning( $id ); } - $snippet = get_snippet( $id, (bool) $atts['network'] ); + $snippet = $this->get_snippet( $id, (bool) $atts['network'], 'html' ); return $this->render_snippet_source( $snippet, $atts ); } diff --git a/src/php/load.php b/src/php/load.php index 2b17a536..142ae033 100644 --- a/src/php/load.php +++ b/src/php/load.php @@ -63,13 +63,3 @@ function code_snippets(): Plugin { } code_snippets()->load_plugin(); -<<<<<<< HEAD - -// Execute the snippets once the plugins are loaded. -$snippet_execution_fn = Settings\get_setting( 'general', 'enable_flat_files' ) - ? '\execute_active_snippets_from_flat_files' - : '\execute_active_snippets'; - -add_action( 'plugins_loaded', __NAMESPACE__ . $snippet_execution_fn, 1 ); -======= ->>>>>>> core diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 8bf16393..d23ffe36 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -606,189 +606,6 @@ function execute_snippet( string $code, int $id = 0, bool $force = false ) { } /** -<<<<<<< HEAD - * Run the active snippets. - * Read-write-execute operation. - * - * @return bool true on success, false on failure. - * - * @since 2.0.0 - */ -function execute_active_snippets(): bool { - global $wpdb; - - // Bail early if safe mode is active. - if ( ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) || - ! apply_filters( 'code_snippets/execute_snippets', true ) ) { - return false; - } - - $db = code_snippets()->db; - $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); - $data = $db->fetch_active_snippets( $scopes ); - - // Detect if a snippet is currently being edited, and if so, spare it from execution. - $edit_id = 0; - $edit_table = $db->table; - - if ( wp_is_json_request() && ! empty( $_SERVER['REQUEST_URI'] ) ) { - $url = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); - - if ( isset( $url['path'] ) && false !== strpos( $url['path'], Snippets_REST_Controller::get_prefixed_base_route() ) ) { - $path_parts = explode( '/', $url['path'] ); - $edit_id = intval( end( $path_parts ) ); - - if ( ! empty( $url['query'] ) ) { - wp_parse_str( $url['query'], $path_params ); - $edit_table = isset( $path_params['network'] ) && rest_sanitize_boolean( $path_params['network'] ) ? - $db->ms_table : $db->table; - } - } - } - - foreach ( $data as $table_name => $active_snippets ) { - - // Loop through the returned snippets and execute the PHP code. - foreach ( $active_snippets as $snippet ) { - $snippet_id = intval( $snippet['id'] ); - $code = $snippet['code']; - - // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. - if ( 'single-use' === $snippet['scope'] ) { - $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); - - if ( $table_name === $db->ms_table && is_array( $active_shared_ids ) && in_array( $snippet_id, $active_shared_ids, true ) ) { - unset( $active_shared_ids[ array_search( $snippet_id, $active_shared_ids, true ) ] ); - $active_shared_ids = array_values( $active_shared_ids ); - update_option( 'active_shared_network_snippets', $active_shared_ids ); - clean_active_snippets_cache( $table_name ); - } else { - $wpdb->update( - $table_name, - array( 'active' => '0' ), - array( 'id' => $snippet_id ), - array( '%d' ), - array( '%d' ) - ); - clean_snippets_cache( $table_name ); - - $network = $table_name === $db->ms_table; - do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); - } - } - - if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && - ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { - execute_snippet( $code, $snippet_id ); - } - } - } - - return true; -} - -function execute_active_snippets_from_flat_files(): bool { - global $wpdb; - - // Bail early if safe mode is active. - if ( ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) || - ! apply_filters( 'code_snippets/execute_snippets', true ) ) { - return false; - } - - $db = code_snippets()->db; - $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; - $data = Snippet_Files::get_active_snippets_from_flat_files( $scopes ); - - // Detect if a snippet is currently being edited, and if so, spare it from execution. - $edit_id = 0; - $edit_table = Snippet_Files::get_hashed_table_name( $db->table ); - - if ( wp_is_json_request() && ! empty( $_SERVER['REQUEST_URI'] ) ) { - $url = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); - - if ( isset( $url['path'] ) && false !== strpos( $url['path'], Snippets_REST_Controller::get_prefixed_base_route() ) ) { - $path_parts = explode( '/', $url['path'] ); - $edit_id = intval( end( $path_parts ) ); - - if ( ! empty( $url['query'] ) ) { - wp_parse_str( $url['query'], $path_params ); - $edit_table = isset( $path_params['network'] ) && rest_sanitize_boolean( $path_params['network'] ) ? - Snippet_Files::get_hashed_table_name( $db->ms_table ) : Snippet_Files::get_hashed_table_name( $db->table ); - } - } - } - - foreach ( $data as $table_name => $active_snippets ) { - $base_dir = Snippet_Files::get_base_dir( $table_name, 'php' ); - $active_snippets = cs_sort_snippets_by_priority( $active_snippets ); - - // Loop through the returned snippets and execute the PHP code. - foreach ( $active_snippets as $snippet ) { - $snippet_id = intval( $snippet['id'] ); - $code = $snippet['code']; - - // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. - if ( 'single-use' === $snippet['scope'] ) { - $table_to_update = Snippet_Files::get_hashed_table_name( $db->table ) === $table_name ? $db->table : $db->ms_table; - $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); - - if ( Snippet_Files::get_hashed_table_name( $db->ms_table ) === $table_name && is_array( $active_shared_ids ) && in_array( $snippet_id, $active_shared_ids, true ) ) { - unset( $active_shared_ids[ array_search( $snippet_id, $active_shared_ids, true ) ] ); - $active_shared_ids = array_values( $active_shared_ids ); - update_option( 'active_shared_network_snippets', $active_shared_ids ); - clean_active_snippets_cache( $table_to_update ); - } else { - $wpdb->update( - $table_to_update, - array( 'active' => '0' ), - array( 'id' => $snippet_id ), - array( '%d' ), - array( '%d' ) - ); - clean_snippets_cache( $table_to_update ); - - $network = Snippet_Files::get_hashed_table_name( $db->ms_table ) === $table_name; - do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network ); - } - } - - if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && - ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { - $file = $base_dir . '/' . $snippet_id . '.php'; - execute_snippet_from_flat_file( $code, $file, $snippet_id ); - } - } - } - - return true; -} - -function cs_sort_snippets_by_priority( array $snippets ): array { - uasort( $snippets, function ( $a, $b ) { - return $a['priority'] <=> $b['priority']; - } ); - - return $snippets; -} - -function execute_snippet_from_flat_file( $code, $file, int $id = 0, bool $force = false ) { - if ( ! is_file( $file ) ) { - execute_snippet( $code, $id, $force ); - } - - if ( ! $force && defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) { - return false; - } - - require_once $file; - - do_action( 'code_snippets/after_execute_snippet_from_flat_file', $file, $id ); -} - -/** -======= ->>>>>>> core * Retrieve a single snippets from the database using its cloud ID. * * Read operation. @@ -857,3 +674,17 @@ function update_snippet_fields( int $snippet_id, array $fields, ?bool $network = do_action( 'code_snippets/update_snippet', $snippet->id, $table ); clean_snippets_cache( $table ); } + +function execute_snippet_from_flat_file( $code, $file, int $id = 0, bool $force = false ) { + if ( ! is_file( $file ) ) { + return execute_snippet( $code, $id, $force ); + } + + if ( ! $force && defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) { + return false; + } + + require_once $file; + + do_action( 'code_snippets/after_execute_snippet_from_flat_file', $file, $id ); +} From 5a43f3cf56f3eb4b4c6369aeaaf11f4191b8de91 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Tue, 7 Oct 2025 23:17:06 +0200 Subject: [PATCH 43/54] remove unrelated changes --- CHANGELOG.md | 10 ++++++++++ src/readme.txt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1dcd7e..b058eab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 3.7.1 ([unreleased]) + +### Fixed +* Prefixed Composer packages to reduce collisions with other plugins, especially those using Guzzle. +* Functions conditions were loading before loop setup, resulting in some conditions not working. (PRO) +* JavaScript and CSS snippets loading twice due to a conditions bug. (PRO) + +### Removed +* Removed CSS linting within the editor until a modern replacement can be implemented. + ## [3.7.0] (2025-08-29) ### Added diff --git a/src/readme.txt b/src/readme.txt index 5798c93e..28bf767b 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -104,7 +104,7 @@ You can report security bugs found in the source code of this plugin through the == Changelog == -= 3.7.0 (2025-08-29) = += 3.7.0 (beta release) = __Added__ From f384c785d6b121d8a16dbb65ecce3aadb3070a1c Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 00:16:47 +0200 Subject: [PATCH 44/54] file bases E2E tests --- .github/workflows/playwright.yml | 125 +++++++++++++++++++++++--- tests/e2e/flat-files.setup.ts | 25 ++++++ tests/playwright/playwright.config.ts | 23 ++++- 3 files changed, 156 insertions(+), 17 deletions(-) create mode 100644 tests/e2e/flat-files.setup.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 676e4c4a..b12e140e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -23,8 +23,8 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - Playwright: - name: Playwright test on PHP 8.1 + playwright-default: + name: Playwright tests (Default Mode) runs-on: ubuntu-22.04 if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') steps: @@ -46,7 +46,6 @@ jobs: id: deps-hash run: | set -euo pipefail - # concatenate existing lock files (src/composer.lock and package-lock.json) tmpfile=$(mktemp) for f in src/composer.lock package-lock.json; do if [ -f "$f" ]; then @@ -56,7 +55,6 @@ jobs: if [ -s "$tmpfile" ]; then deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) else - # no lock files found, fall back to short commit sha deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) fi echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" @@ -101,8 +99,8 @@ jobs: run: | npx playwright install chromium - - name: Run Playwright tests - run: npm run test:playwright + - name: Run Playwright tests (Default Mode) + run: npx playwright test --project=chromium-db-snippets - name: Stop WordPress environment if: always() @@ -111,19 +109,118 @@ jobs: - uses: actions/upload-artifact@v4 if: always() with: - name: playwright-test-results + name: playwright-test-results-default-mode + path: test-results/ + if-no-files-found: ignore + retention-days: 2 + + playwright-file-based-execution: + name: Playwright tests (File-based Execution) + runs-on: ubuntu-22.04 + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up PHP + uses: codesnippetspro/setup-php@v2 + with: + php-version: "8.1" + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: 'npm' + + - name: Compute dependency hash + id: deps-hash + run: | + set -euo pipefail + tmpfile=$(mktemp) + for f in src/composer.lock package-lock.json; do + if [ -f "$f" ]; then + cat "$f" >> "$tmpfile" + fi + done + if [ -s "$tmpfile" ]; then + deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) + else + deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) + fi + echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" + + - name: Get build cache + id: deps-cache + uses: actions/cache/restore@v4 + with: + path: | + node_modules + src/vendor + key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} + restore-keys: | + ${{ runner.os }}-deps- + + - name: Install workflow dependencies (wp-env, playwright) + if: steps.deps-cache.outputs.cache-hit != 'true' + run: npm run prepare-environment:ci && npm run bundle + + - name: Save vendor and node_modules cache + if: steps.deps-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + src/vendor + node_modules + key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} + + - name: Start WordPress environment + run: | + npx wp-env start + + - name: Activate code-snippets plugin + run: npx wp-env run cli wp plugin activate code-snippets + + - name: WordPress debug information + run: | + npx wp-env run cli wp core version + npx wp-env run cli wp --info + + - name: Install playwright/test + run: | + npx playwright install chromium + + - name: Run Playwright tests (File-based Execution) + run: npx playwright test --project=chromium-file-based-snippets + + - name: Verify file-based execution enabled + if: always() + run: | + npx wp-env run cli wp eval 'echo file_exists(WP_CONTENT_DIR . "/code-snippets/flat-files-enabled.flag") ? "ENABLED" : "DISABLED";' + + - name: Stop WordPress environment + if: always() + run: npx wp-env stop + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-test-results-file-based-execution path: test-results/ if-no-files-found: ignore retention-days: 2 test-result: - needs: [Playwright] - if: always() && (needs.Playwright.result != 'skipped') + needs: [playwright-default, playwright-file-based-execution] + if: always() && (needs.playwright-default.result != 'skipped' || needs.playwright-file-based-execution.result != 'skipped') runs-on: ubuntu-22.04 - name: Playwright - Test Results + name: Playwright - Test Results Summary steps: - - name: Test status - run: echo "Test status is - ${{ needs.Playwright.result }}" - - name: Check Playwright status - if: ${{ needs.Playwright.result != 'success' && needs.Playwright.result != 'skipped' }} + - name: Test status summary + run: | + echo "Default Mode: ${{ needs.playwright-default.result }}" + echo "File-based Execution: ${{ needs.playwright-file-based-execution.result }}" + + - name: Check overall status + if: ${{ (needs.playwright-default.result != 'success' && needs.playwright-default.result != 'skipped') || (needs.playwright-file-based-execution.result != 'success' && needs.playwright-file-based-execution.result != 'skipped') }} run: exit 1 diff --git a/tests/e2e/flat-files.setup.ts b/tests/e2e/flat-files.setup.ts new file mode 100644 index 00000000..c764ae97 --- /dev/null +++ b/tests/e2e/flat-files.setup.ts @@ -0,0 +1,25 @@ +import { test as setup, expect } from '@playwright/test'; + +setup('enable flat files', async ({ page }) => { + await page.goto('/wp-admin/admin.php?page=snippets-settings'); + await page.waitForSelector('#wpbody-content'); + + await page.waitForSelector('form'); + + const flatFilesCheckbox = page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]'); + await expect(flatFilesCheckbox).toBeVisible(); + + const isChecked = await flatFilesCheckbox.isChecked(); + if (!isChecked) { + await flatFilesCheckbox.check(); + } + + await page.click('input[type="submit"][name="submit"]'); + + await page.waitForSelector('.notice-success, .updated', { timeout: 10000 }); + await expect(page.locator('.notice-success, .updated')).toContainText('Settings saved'); + + await page.reload(); + await page.waitForSelector('input[name="code_snippets_settings[general][enable_flat_files]"]'); + await expect(page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]')).toBeChecked(); +}); \ No newline at end of file diff --git a/tests/playwright/playwright.config.ts b/tests/playwright/playwright.config.ts index 696f73de..f5799275 100644 --- a/tests/playwright/playwright.config.ts +++ b/tests/playwright/playwright.config.ts @@ -29,16 +29,33 @@ export default defineConfig({ projects: [ { name: 'setup', - testMatch: /.*\.setup\.ts/ + testMatch: /auth\.setup\.ts/ }, { - name: 'chromium', + name: 'flat-files-setup', + testMatch: /flat-files\.setup\.ts/, + dependencies: ['setup'] + }, + + { + name: 'chromium-db-snippets', use: { ...devices['Desktop Chrome'], storageState: join(__dirname, '../e2e/.auth/user.json') }, - dependencies: ['setup'] + dependencies: ['setup'], + testIgnore: /.*\.setup\.ts/ + }, + + { + name: 'chromium-file-based-snippets', + use: { + ...devices['Desktop Chrome'], + storageState: join(__dirname, '../e2e/.auth/user.json') + }, + dependencies: ['setup', 'flat-files-setup'], + testIgnore: /.*\.setup\.ts/ } ], From 347abe793b9c6401b8d21967ef2418efc73e3b74 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 00:21:24 +0200 Subject: [PATCH 45/54] update playwright command --- .github/workflows/playwright.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index b12e140e..7f9d4440 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -100,7 +100,7 @@ jobs: npx playwright install chromium - name: Run Playwright tests (Default Mode) - run: npx playwright test --project=chromium-db-snippets + run: npm run test:playwright -- --project=chromium-db-snippets - name: Stop WordPress environment if: always() @@ -191,7 +191,7 @@ jobs: npx playwright install chromium - name: Run Playwright tests (File-based Execution) - run: npx playwright test --project=chromium-file-based-snippets + run: npm run test:playwright -- --project=chromium-file-based-snippets - name: Verify file-based execution enabled if: always() From c33165106640c912fe90a57083bb5ebc14c579a2 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 00:30:25 +0200 Subject: [PATCH 46/54] fix playwright storage state --- tests/e2e/flat-files.setup.ts | 6 +++--- tests/playwright/playwright.config.ts | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/e2e/flat-files.setup.ts b/tests/e2e/flat-files.setup.ts index c764ae97..81304c97 100644 --- a/tests/e2e/flat-files.setup.ts +++ b/tests/e2e/flat-files.setup.ts @@ -16,10 +16,10 @@ setup('enable flat files', async ({ page }) => { await page.click('input[type="submit"][name="submit"]'); - await page.waitForSelector('.notice-success, .updated', { timeout: 10000 }); - await expect(page.locator('.notice-success, .updated')).toContainText('Settings saved'); + await page.waitForSelector('.notice-success', { timeout: 10000 }); + await expect(page.locator('.notice-success')).toContainText('Settings saved'); await page.reload(); await page.waitForSelector('input[name="code_snippets_settings[general][enable_flat_files]"]'); await expect(page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]')).toBeChecked(); -}); \ No newline at end of file +}); diff --git a/tests/playwright/playwright.config.ts b/tests/playwright/playwright.config.ts index f5799275..cde090e2 100644 --- a/tests/playwright/playwright.config.ts +++ b/tests/playwright/playwright.config.ts @@ -35,6 +35,10 @@ export default defineConfig({ { name: 'flat-files-setup', testMatch: /flat-files\.setup\.ts/, + use: { + ...devices['Desktop Chrome'], + storageState: join(__dirname, '../e2e/.auth/user.json') + }, dependencies: ['setup'] }, From 555bb317a6cd9c73a4cf068a0a59baf3babdcbe4 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:04:16 +0200 Subject: [PATCH 47/54] clear opcache cache on save --- src/php/flat-files/classes/class-config-repository.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/php/flat-files/classes/class-config-repository.php b/src/php/flat-files/classes/class-config-repository.php index 0ec25d90..07ab5452 100644 --- a/src/php/flat-files/classes/class-config-repository.php +++ b/src/php/flat-files/classes/class-config-repository.php @@ -33,6 +33,12 @@ public function save( string $base_dir, array $active_snippets ): void { ";\n"; $this->fs->put_contents( $config_file_path, $file_content, FS_CHMOD_FILE ); + + if ( is_file( $config_file_path ) ) { + if ( function_exists( 'opcache_invalidate' ) ) { + opcache_invalidate( $config_file_path, true ); + } + } } public function update( string $base_dir, Snippet $snippet, ?bool $remove = false ): void { From 0d36e23b7bc76d69447d52a7490df26d4e5cd929 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:14:06 +0200 Subject: [PATCH 48/54] eslint fix --- tests/e2e/flat-files.setup.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/e2e/flat-files.setup.ts b/tests/e2e/flat-files.setup.ts index 81304c97..3a9307c4 100644 --- a/tests/e2e/flat-files.setup.ts +++ b/tests/e2e/flat-files.setup.ts @@ -1,25 +1,25 @@ -import { test as setup, expect } from '@playwright/test'; +import { expect, test as setup } from '@playwright/test' setup('enable flat files', async ({ page }) => { - await page.goto('/wp-admin/admin.php?page=snippets-settings'); - await page.waitForSelector('#wpbody-content'); + await page.goto('/wp-admin/admin.php?page=snippets-settings') + await page.waitForSelector('#wpbody-content') - await page.waitForSelector('form'); + await page.waitForSelector('form') - const flatFilesCheckbox = page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]'); - await expect(flatFilesCheckbox).toBeVisible(); + const flatFilesCheckbox = page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]') + await expect(flatFilesCheckbox).toBeVisible() - const isChecked = await flatFilesCheckbox.isChecked(); + const isChecked = await flatFilesCheckbox.isChecked() if (!isChecked) { - await flatFilesCheckbox.check(); + await flatFilesCheckbox.check() } - await page.click('input[type="submit"][name="submit"]'); + await page.click('input[type="submit"][name="submit"]') - await page.waitForSelector('.notice-success', { timeout: 10000 }); - await expect(page.locator('.notice-success')).toContainText('Settings saved'); + await page.waitForSelector('.notice-success', { timeout: 10000 }) + await expect(page.locator('.notice-success')).toContainText('Settings saved') - await page.reload(); - await page.waitForSelector('input[name="code_snippets_settings[general][enable_flat_files]"]'); - await expect(page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]')).toBeChecked(); -}); + await page.reload() + await page.waitForSelector('input[name="code_snippets_settings[general][enable_flat_files]"]') + await expect(page.locator('input[name="code_snippets_settings[general][enable_flat_files]"]')).toBeChecked() +}) From 73672e22a77b4ce44e783fa9f2655a0fa8346dba Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:21:31 +0200 Subject: [PATCH 49/54] fix e2e test snippet for list page tests --- tests/e2e/code-snippets-list.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/code-snippets-list.spec.ts b/tests/e2e/code-snippets-list.spec.ts index c83b6515..07ecedf5 100644 --- a/tests/e2e/code-snippets-list.spec.ts +++ b/tests/e2e/code-snippets-list.spec.ts @@ -13,7 +13,7 @@ test.describe('Code Snippets List Page Actions', () => { await helper.createAndActivateSnippet({ name: TEST_SNIPPET_NAME, - code: 'echo "Test snippet for list actions";' + code: "add_filter('show_admin_bar', '__return_false');" }) await helper.navigateToSnippetsAdmin() }) From 819f7c00b373b5714b7574280f5f86b1732ce4a9 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:28:45 +0200 Subject: [PATCH 50/54] reusable playwright workflow --- .github/workflows/playwright-test.yml | 116 ++++++++++++++++ .github/workflows/playwright.yml | 191 ++------------------------ tests/e2e/code-snippets-edit.spec.ts | 2 +- 3 files changed, 126 insertions(+), 183 deletions(-) create mode 100644 .github/workflows/playwright-test.yml diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml new file mode 100644 index 00000000..f86d21df --- /dev/null +++ b/.github/workflows/playwright-test.yml @@ -0,0 +1,116 @@ +name: Playwright Test Runner + +on: + workflow_call: + inputs: + test-mode: + required: true + type: string + description: 'Test mode: default or file-based-execution' + project-name: + required: true + type: string + description: 'Playwright project name to run' + +jobs: + playwright-test: + name: Playwright tests (${{ inputs.test-mode == 'default' && 'Default Mode' || 'File-based Execution' }}) + runs-on: ubuntu-22.04 + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up PHP + uses: codesnippetspro/setup-php@v2 + with: + php-version: "8.1" + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: 'npm' + + - name: Compute dependency hash + id: deps-hash + run: | + set -euo pipefail + tmpfile=$(mktemp) + for f in src/composer.lock package-lock.json; do + if [ -f "$f" ]; then + cat "$f" >> "$tmpfile" + fi + done + if [ -s "$tmpfile" ]; then + deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) + else + deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) + fi + echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" + + - name: Get build cache + id: deps-cache + uses: actions/cache/restore@v4 + with: + path: | + node_modules + src/vendor + key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} + restore-keys: | + ${{ runner.os }}-deps- + + - name: Install workflow dependencies (wp-env, playwright) + if: steps.deps-cache.outputs.cache-hit != 'true' + run: npm run prepare-environment:ci && npm run bundle + + - name: Save vendor and node_modules cache + if: steps.deps-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + src/vendor + node_modules + key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} + + - name: Start WordPress environment + run: | + npx wp-env start + + - name: Activate code-snippets plugin + run: npx wp-env run cli wp plugin activate code-snippets + + - name: WordPress debug information + run: | + npx wp-env run cli wp core version + npx wp-env run cli wp --info + + - name: Install playwright/test + run: | + npx playwright install chromium + + - name: Restart WordPress environment for file-based execution + if: inputs.test-mode == 'file-based-execution' + run: | + echo "Restarting WordPress to clear opcache..." + npx wp-env stop + npx wp-env start + + - name: Run Playwright tests + run: npm run test:playwright -- --project=${{ inputs.project-name }} + + - name: Verify file-based execution enabled + if: always() && inputs.test-mode == 'file-based-execution' + run: | + npx wp-env run cli wp eval 'echo file_exists(WP_CONTENT_DIR . "/code-snippets/flat-files-enabled.flag") ? "ENABLED" : "DISABLED";' + + - name: Stop WordPress environment + if: always() + run: npx wp-env stop + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-test-results-${{ inputs.test-mode }} + path: test-results/ + if-no-files-found: ignore + retention-days: 2 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7f9d4440..f101cdee 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -24,191 +24,18 @@ concurrency: jobs: playwright-default: - name: Playwright tests (Default Mode) - runs-on: ubuntu-22.04 if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up PHP - uses: codesnippetspro/setup-php@v2 - with: - php-version: "8.1" - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - cache: 'npm' - - - name: Compute dependency hash - id: deps-hash - run: | - set -euo pipefail - tmpfile=$(mktemp) - for f in src/composer.lock package-lock.json; do - if [ -f "$f" ]; then - cat "$f" >> "$tmpfile" - fi - done - if [ -s "$tmpfile" ]; then - deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) - else - deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) - fi - echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" - - - name: Get build cache - id: deps-cache - uses: actions/cache/restore@v4 - with: - path: | - node_modules - src/vendor - key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} - restore-keys: | - ${{ runner.os }}-deps- - - - name: Install workflow dependencies (wp-env, playwright) - if: steps.deps-cache.outputs.cache-hit != 'true' - run: npm run prepare-environment:ci && npm run bundle - - - name: Save vendor and node_modules cache - if: steps.deps-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: | - src/vendor - node_modules - key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} - - - name: Start WordPress environment - run: | - npx wp-env start - - - name: Activate code-snippets plugin - run: npx wp-env run cli wp plugin activate code-snippets - - - name: WordPress debug information - run: | - npx wp-env run cli wp core version - npx wp-env run cli wp --info - - - name: Install playwright/test - run: | - npx playwright install chromium - - - name: Run Playwright tests (Default Mode) - run: npm run test:playwright -- --project=chromium-db-snippets - - - name: Stop WordPress environment - if: always() - run: npx wp-env stop - - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-test-results-default-mode - path: test-results/ - if-no-files-found: ignore - retention-days: 2 + uses: ./.github/workflows/playwright-test.yml + with: + test-mode: 'default' + project-name: 'chromium-db-snippets' playwright-file-based-execution: - name: Playwright tests (File-based Execution) - runs-on: ubuntu-22.04 if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up PHP - uses: codesnippetspro/setup-php@v2 - with: - php-version: "8.1" - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - cache: 'npm' - - - name: Compute dependency hash - id: deps-hash - run: | - set -euo pipefail - tmpfile=$(mktemp) - for f in src/composer.lock package-lock.json; do - if [ -f "$f" ]; then - cat "$f" >> "$tmpfile" - fi - done - if [ -s "$tmpfile" ]; then - deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) - else - deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8) - fi - echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT" - - - name: Get build cache - id: deps-cache - uses: actions/cache/restore@v4 - with: - path: | - node_modules - src/vendor - key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} - restore-keys: | - ${{ runner.os }}-deps- - - - name: Install workflow dependencies (wp-env, playwright) - if: steps.deps-cache.outputs.cache-hit != 'true' - run: npm run prepare-environment:ci && npm run bundle - - - name: Save vendor and node_modules cache - if: steps.deps-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: | - src/vendor - node_modules - key: ${{ runner.os }}-deps-${{ steps.deps-hash.outputs.deps_hash }} - - - name: Start WordPress environment - run: | - npx wp-env start - - - name: Activate code-snippets plugin - run: npx wp-env run cli wp plugin activate code-snippets - - - name: WordPress debug information - run: | - npx wp-env run cli wp core version - npx wp-env run cli wp --info - - - name: Install playwright/test - run: | - npx playwright install chromium - - - name: Run Playwright tests (File-based Execution) - run: npm run test:playwright -- --project=chromium-file-based-snippets - - - name: Verify file-based execution enabled - if: always() - run: | - npx wp-env run cli wp eval 'echo file_exists(WP_CONTENT_DIR . "/code-snippets/flat-files-enabled.flag") ? "ENABLED" : "DISABLED";' - - - name: Stop WordPress environment - if: always() - run: npx wp-env stop - - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-test-results-file-based-execution - path: test-results/ - if-no-files-found: ignore - retention-days: 2 + uses: ./.github/workflows/playwright-test.yml + with: + test-mode: 'file-based-execution' + project-name: 'chromium-file-based-snippets' test-result: needs: [playwright-default, playwright-file-based-execution] @@ -223,4 +50,4 @@ jobs: - name: Check overall status if: ${{ (needs.playwright-default.result != 'success' && needs.playwright-default.result != 'skipped') || (needs.playwright-file-based-execution.result != 'success' && needs.playwright-file-based-execution.result != 'skipped') }} - run: exit 1 + run: exit 1 \ No newline at end of file diff --git a/tests/e2e/code-snippets-edit.spec.ts b/tests/e2e/code-snippets-edit.spec.ts index 2f3426a2..de1204b2 100644 --- a/tests/e2e/code-snippets-edit.spec.ts +++ b/tests/e2e/code-snippets-edit.spec.ts @@ -19,7 +19,7 @@ test.describe('Code Snippets Admin', () => { test('Can add a new snippet', async () => { await helper.createSnippet({ name: TEST_SNIPPET_NAME, - code: 'echo "Hello World!";' + code: "add_filter('show_admin_bar', '__return_false');" }) }) From 1df506ba66ccbef89d3c3f624fc124370e4b5347 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:33:44 +0200 Subject: [PATCH 51/54] remove debug steps from playwright workflow --- .github/workflows/playwright-test.yml | 12 ------------ .github/workflows/playwright.yml | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml index f86d21df..7103c136 100644 --- a/.github/workflows/playwright-test.yml +++ b/.github/workflows/playwright-test.yml @@ -88,21 +88,9 @@ jobs: run: | npx playwright install chromium - - name: Restart WordPress environment for file-based execution - if: inputs.test-mode == 'file-based-execution' - run: | - echo "Restarting WordPress to clear opcache..." - npx wp-env stop - npx wp-env start - - name: Run Playwright tests run: npm run test:playwright -- --project=${{ inputs.project-name }} - - name: Verify file-based execution enabled - if: always() && inputs.test-mode == 'file-based-execution' - run: | - npx wp-env run cli wp eval 'echo file_exists(WP_CONTENT_DIR . "/code-snippets/flat-files-enabled.flag") ? "ENABLED" : "DISABLED";' - - name: Stop WordPress environment if: always() run: npx wp-env stop diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f101cdee..f0f5c07c 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -47,7 +47,7 @@ jobs: run: | echo "Default Mode: ${{ needs.playwright-default.result }}" echo "File-based Execution: ${{ needs.playwright-file-based-execution.result }}" - + - name: Check overall status if: ${{ (needs.playwright-default.result != 'success' && needs.playwright-default.result != 'skipped') || (needs.playwright-file-based-execution.result != 'success' && needs.playwright-file-based-execution.result != 'skipped') }} - run: exit 1 \ No newline at end of file + run: exit 1 From 3e4a082c1a530a24fae40955fdbbae50b464b0c7 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:39:43 +0200 Subject: [PATCH 52/54] add workflow permissions --- .github/workflows/playwright-test.yml | 4 ++++ .github/workflows/playwright.yml | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml index 7103c136..11cb0dcc 100644 --- a/.github/workflows/playwright-test.yml +++ b/.github/workflows/playwright-test.yml @@ -12,6 +12,10 @@ on: type: string description: 'Playwright project name to run' +permissions: + contents: read + actions: write + jobs: playwright-test: name: Playwright tests (${{ inputs.test-mode == 'default' && 'Default Mode' || 'File-based Execution' }}) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f0f5c07c..c7bc0c28 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -26,6 +26,10 @@ jobs: playwright-default: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') uses: ./.github/workflows/playwright-test.yml + permissions: + contents: write + pull-requests: write + actions: read with: test-mode: 'default' project-name: 'chromium-db-snippets' @@ -33,6 +37,10 @@ jobs: playwright-file-based-execution: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') uses: ./.github/workflows/playwright-test.yml + permissions: + contents: write + pull-requests: write + actions: read with: test-mode: 'file-based-execution' project-name: 'chromium-file-based-snippets' From 350ba5451ca1f920a1483546c61469186effb8f3 Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:43:18 +0200 Subject: [PATCH 53/54] remove permissions --- .github/workflows/playwright.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index c7bc0c28..f0f5c07c 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -26,10 +26,6 @@ jobs: playwright-default: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') uses: ./.github/workflows/playwright-test.yml - permissions: - contents: write - pull-requests: write - actions: read with: test-mode: 'default' project-name: 'chromium-db-snippets' @@ -37,10 +33,6 @@ jobs: playwright-file-based-execution: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests') uses: ./.github/workflows/playwright-test.yml - permissions: - contents: write - pull-requests: write - actions: read with: test-mode: 'file-based-execution' project-name: 'chromium-file-based-snippets' From 8187aabe497c626a5c89224189971c82469a2d9c Mon Sep 17 00:00:00 2001 From: Louis Wolmarans Date: Fri, 10 Oct 2025 01:44:13 +0200 Subject: [PATCH 54/54] remove permissions --- .github/workflows/playwright-test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml index 11cb0dcc..7103c136 100644 --- a/.github/workflows/playwright-test.yml +++ b/.github/workflows/playwright-test.yml @@ -12,10 +12,6 @@ on: type: string description: 'Playwright project name to run' -permissions: - contents: read - actions: write - jobs: playwright-test: name: Playwright tests (${{ inputs.test-mode == 'default' && 'Default Mode' || 'File-based Execution' }})