diff --git a/dist/oribi-tech-sync.zip b/dist/oribi-tech-sync.zip index 7bde34e..e1ed639 100644 Binary files a/dist/oribi-tech-sync.zip and b/dist/oribi-tech-sync.zip differ diff --git a/includes/admin.php b/includes/admin.php index 1f070ff..3b303d0 100644 --- a/includes/admin.php +++ b/includes/admin.php @@ -35,12 +35,9 @@ add_action( 'admin_post_oribi_sync_save_settings', function () { $provider = sanitize_text_field( wp_unslash( $_POST['oribi_sync_provider'] ?? '' ) ); $pat = wp_unslash( $_POST['oribi_sync_pat'] ?? '' ); - $pages_folder = sanitize_text_field( wp_unslash( $_POST['oribi_sync_pages_folder'] ?? '' ) ); - update_option( 'oribi_sync_repo', $repo, 'no' ); update_option( 'oribi_sync_branch', $branch, 'no' ); update_option( 'oribi_sync_provider', $provider, 'no' ); - update_option( 'oribi_sync_pages_folder', $pages_folder, 'no' ); // Only update PAT if a new one was provided (non-empty) if ( ! empty( $pat ) ) { @@ -135,7 +132,6 @@ function oribi_sync_settings_page() { $repo = get_option( 'oribi_sync_repo', '' ); $branch = get_option( 'oribi_sync_branch', 'main' ); $provider = get_option( 'oribi_sync_provider', '' ); - $pages_folder = get_option( 'oribi_sync_pages_folder', '' ); $has_pat = ! empty( get_option( 'oribi_sync_pat', '' ) ); $last_run = get_option( 'oribi_sync_last_run', '' ); $log = get_option( 'oribi_sync_log', [] ); @@ -251,22 +247,6 @@ function oribi_sync_settings_page() {

- - - - - - -

Sub-folder inside Pages/ to sync from.

- - @@ -386,42 +366,6 @@ function oribi_sync_settings_page() { - - 'page', + 'meta_key' => '_oribi_sync_source', + 'numberposts' => 1, + 'fields' => 'ids', + ] ); + + if ( ! empty( $existing ) ) { + $source = get_post_meta( $existing[0], '_oribi_sync_source', true ); + // Extract the repo-path portion after the last colon (skip 'https:'). + $colon = strrpos( $source, ':' ); + if ( $colon !== false ) { + $path_part = substr( $source, $colon + 1 ); // e.g. 'pages/about.php' + // Validate it looks like a pages/ path before trusting it. + if ( strncasecmp( $path_part, 'pages/', 6 ) === 0 ) { + return substr( $path_part, 0, 6 ); // preserve original casing + } + } + } + + return 'pages/'; +} + // ─── Auto-push on page save ────────────────────────────────────────────────── add_action( 'save_post_page', 'oribi_sync_maybe_push_on_save', 20, 3 ); @@ -242,18 +275,15 @@ function oribi_sync_generate_php_wrapper( string $content, string $slug, string $title = oribi_sync_slug_to_title( $slug ); } - $date = current_time( 'Y-m-d H:i:s' ); - // Use a nowdoc so the content is treated as a literal string (no interpolation). - // Escape the content if it accidentally contains the heredoc delimiter on its own line. $delimiter = 'ORIBI_SYNC_CONTENT'; $safe_content = str_replace( "\n" . $delimiter, "\n " . $delimiter, $content ); $php = " false, 'action' => 'error', 'message' => 'Cannot determine repo path — no pages folder configured and no source meta on this page.' ]; - } - $repo_path = 'Pages/' . $pages_folder . '/' . $post->post_name . '.php'; + // Derive from slug — files live under pages/ + $repo_path = 'pages/' . $post->post_name . '.php'; } // ── Generate content ────────────────────────────────────────────────── $slug = $post->post_name; $title = $post->post_title; $wp_content = $post->post_content; - $php_source = oribi_sync_generate_php_wrapper( $wp_content, $slug, $title ); - $new_checksum = hash( 'sha256', $php_source ); $commit_msg = $opts['message'] ?? "Sync: update {$slug} from WordPress"; @@ -350,6 +411,14 @@ function oribi_sync_push_page( int $post_id, array $opts = [] ): array { return [ 'ok' => false, 'action' => 'error', 'message' => 'Failed to check remote file: ' . $remote->get_error_message() ]; } + // Build PHP source: preserve original header for existing files, fresh wrapper for new ones. + if ( $remote !== null && ! empty( $remote['content'] ) ) { + $php_source = oribi_sync_replace_php_body( $remote['content'], $wp_content, $slug, $title ); + } else { + $php_source = oribi_sync_generate_php_wrapper( $wp_content, $slug, $title ); + } + $new_checksum = hash( 'sha256', $php_source ); + $stored_sha = get_post_meta( $post_id, '_oribi_sync_git_sha', true ); // ── Decide strategy ─────────────────────────────────────────────────── diff --git a/includes/rest.php b/includes/rest.php index 9103d79..23c768b 100644 --- a/includes/rest.php +++ b/includes/rest.php @@ -30,15 +30,6 @@ add_action( 'rest_api_init', function () { }, ] ); - // ── List Pages sub-folders from the repo ─────────────────────────── - register_rest_route( 'oribi-sync/v1', '/repo-folders', [ - 'methods' => 'GET', - 'callback' => 'oribi_sync_rest_repo_folders', - 'permission_callback' => function () { - return current_user_can( 'manage_options' ); - }, - ] ); - // ── Push page to repo ────────────────────────────────────────────────── register_rest_route( 'oribi-sync/v1', '/push', [ 'methods' => 'POST', @@ -160,53 +151,3 @@ function oribi_sync_rest_push( WP_REST_Request $request ): WP_REST_Response { /** * REST: Push all synced pages to the repo. */ -function oribi_sync_rest_push_all( WP_REST_Request $request ): WP_REST_Response { - $result = oribi_sync_push_all(); - - return new WP_REST_Response( $result, $result['ok'] ? 200 : 500 ); -} - -/** - * REST: List available sub-folders under Pages/ in the configured repository. - * - * Returns a JSON object: { folders: ["folder-a", "folder-b", …] } - */ -function oribi_sync_rest_repo_folders(): WP_REST_Response { - $repo_url = get_option( 'oribi_sync_repo', '' ); - $branch = get_option( 'oribi_sync_branch', 'main' ) ?: 'main'; - $pat = oribi_sync_get_pat(); - - if ( empty( $repo_url ) || empty( $pat ) ) { - return new WP_REST_Response( [ 'error' => 'Repository URL or PAT is not configured.' ], 400 ); - } - - $parsed = oribi_sync_parse_repo_url( $repo_url ); - if ( is_wp_error( $parsed ) ) { - return new WP_REST_Response( [ 'error' => $parsed->get_error_message() ], 400 ); - } - - $provider = oribi_sync_get_provider(); - $api_base = oribi_sync_api_base( $provider, $parsed ); - - $tree = oribi_sync_fetch_tree( $api_base, $branch, $provider, $pat ); - if ( is_wp_error( $tree ) ) { - return new WP_REST_Response( [ 'error' => 'Tree fetch failed: ' . $tree->get_error_message() ], 500 ); - } - - // Find direct sub-directories of Pages/ - $folders = []; - $prefix = 'Pages/'; - foreach ( $tree as $entry ) { - if ( $entry['type'] !== 'tree' ) continue; - if ( strpos( $entry['path'], $prefix ) !== 0 ) continue; - $relative = substr( $entry['path'], strlen( $prefix ) ); - // Only direct children (no nested slash) - if ( strpos( $relative, '/' ) !== false ) continue; - if ( $relative === '' ) continue; - $folders[] = $relative; - } - - sort( $folders ); - - return new WP_REST_Response( [ 'folders' => $folders ], 200 ); -} diff --git a/includes/sync-engine.php b/includes/sync-engine.php index 2859093..2f9ce88 100644 --- a/includes/sync-engine.php +++ b/includes/sync-engine.php @@ -9,6 +9,21 @@ if ( ! defined( 'ABSPATH' ) ) exit; +// ─── Helpers ────────────────────────────────────────────────────────────────── + +/** + * Strip a case-insensitive directory prefix from a file path. + * + * Example: oribi_sync_strip_prefix( 'Theme/header.php', 'theme' ) → 'header.php' + */ +function oribi_sync_strip_prefix( string $path, string $prefix ): string { + $prefix = rtrim( $prefix, '/' ) . '/'; + if ( strncasecmp( $path, $prefix, strlen( $prefix ) ) === 0 ) { + return substr( $path, strlen( $prefix ) ); + } + return $path; +} + // ─── Gutenberg block helpers ────────────────────────────────────────────────── /** Generate a self-closing block comment (standalone or child blocks). */ @@ -129,18 +144,12 @@ function oribi_sync_run( bool $dry_run = false ): array { return $result; } - // ── Filter to selected Pages sub-folder ──────────────────────────────── - $pages_folder = get_option( 'oribi_sync_pages_folder', '' ); + // ── Filter to Pages/ directory ───────────────────────────────────────── $synced_slugs = []; + $page_files = oribi_sync_filter_tree( $tree, 'Pages' ); - if ( empty( $pages_folder ) ) { - $result['skipped'][] = 'Pages sync skipped — no folder selected in settings.'; - $page_files = []; - } else { - $page_files = oribi_sync_filter_tree( $tree, 'Pages/' . $pages_folder ); - if ( empty( $page_files ) ) { - $result['errors'][] = 'No files found under Pages/' . $pages_folder . '/ in the repository.'; - } + if ( empty( $page_files ) ) { + $result['skipped'][] = 'No files found under Pages/ in the repository.'; } // ── Process each page file ───────────────────────────────────────────── @@ -274,7 +283,7 @@ function oribi_sync_run( bool $dry_run = false ): array { } // ── Trash pages removed from repo ────────────────────────────────────── - if ( ! $dry_run && ! empty( $pages_folder ) ) { + if ( ! $dry_run ) { $trashed = oribi_sync_trash_removed_pages( $synced_slugs ); $result['trashed'] = $trashed; } @@ -456,7 +465,7 @@ function oribi_sync_apply_theme_files( string $api_base, string $branch, string $theme_entries = oribi_sync_filter_tree( $tree, 'theme', true ); foreach ( $theme_entries as $entry ) { - $relative = substr( $entry['path'], strlen( 'theme/' ) ); + $relative = oribi_sync_strip_prefix( $entry['path'], 'theme' ); $ext = strtolower( pathinfo( $relative, PATHINFO_EXTENSION ) ); if ( ! in_array( $ext, $allowed, true ) ) { @@ -540,7 +549,7 @@ function oribi_sync_fetch_theme_files(): array { foreach ( $theme_entries as $entry ) { // Derive relative path by stripping the 'theme/' prefix - $relative = substr( $entry['path'], strlen( 'theme/' ) ); + $relative = oribi_sync_strip_prefix( $entry['path'], 'theme' ); $content = oribi_sync_fetch_file( $api_base, $branch, $entry['path'], $provider, $pat ); if ( is_wp_error( $content ) ) {