Refactor Oribi Sync settings: remove pages folder option and enhance case-insensitive directory handling
This commit is contained in:
@@ -11,6 +11,39 @@
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Detect the actual casing of the Pages directory in the repo tree.
|
||||
*
|
||||
* Looks at existing synced pages for a stored repo path, extracts the
|
||||
* directory prefix. Falls back to 'Pages/' if nothing found.
|
||||
*/
|
||||
function oribi_sync_detect_pages_prefix(): string {
|
||||
// Check post meta of any previously-synced page for the real path
|
||||
$existing = get_posts( [
|
||||
'post_type' => '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 = "<?php\n";
|
||||
$php .= "/**\n";
|
||||
$php .= " * Page: {$title}\n";
|
||||
$php .= "/*\n";
|
||||
$php .= " * Title: {$title}\n";
|
||||
$php .= " * Slug: {$slug}\n";
|
||||
$php .= " * Pushed by Oribi Tech Sync on {$date}.\n";
|
||||
$php .= " * Post Type: page\n";
|
||||
$php .= " */\n\n";
|
||||
$php .= "return <<<'{$delimiter}'\n";
|
||||
$php .= $safe_content . "\n";
|
||||
@@ -262,6 +292,38 @@ function oribi_sync_generate_php_wrapper( string $content, string $slug, string
|
||||
return $php;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the content body in an existing PHP page-data file.
|
||||
*
|
||||
* Preserves the original header (everything before the `return` statement)
|
||||
* and only replaces the body between the heredoc / nowdoc delimiters.
|
||||
* If the file format can't be parsed, falls back to generating a new wrapper.
|
||||
*
|
||||
* @param string $existing_source Current PHP source from the repo.
|
||||
* @param string $new_content New Gutenberg block HTML.
|
||||
* @param string $slug Page slug (used for fallback wrapper).
|
||||
* @param string $title Page title (used for fallback wrapper).
|
||||
*
|
||||
* @return string Updated PHP source code.
|
||||
*/
|
||||
function oribi_sync_replace_php_body( string $existing_source, string $new_content, string $slug, string $title ): string {
|
||||
// Match: return <<<'DELIMITER' or return <<<DELIMITER (heredoc / nowdoc)
|
||||
if ( preg_match( '/^(.*?return\s+<<<\'?)(\w+)(\'?\s*\n)(.*)(\n\2;?\s*)$/s', $existing_source, $m ) ) {
|
||||
$header = $m[1]; // everything up to and including "return <<<"
|
||||
$delimiter = $m[2]; // e.g. ORIBI_SYNC_CONTENT
|
||||
$quote_end = $m[3]; // closing quote + newline
|
||||
$suffix = $m[5]; // closing delimiter + semicolon
|
||||
|
||||
// Escape content if it contains the delimiter string on its own line
|
||||
$safe_content = str_replace( "\n" . $delimiter, "\n " . $delimiter, $new_content );
|
||||
|
||||
return $header . $delimiter . $quote_end . $safe_content . $suffix;
|
||||
}
|
||||
|
||||
// Couldn't parse the existing file — fall back to a fresh wrapper.
|
||||
return oribi_sync_generate_php_wrapper( $new_content, $slug, $title );
|
||||
}
|
||||
|
||||
// ─── Push orchestrator ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -315,31 +377,30 @@ function oribi_sync_push_page( int $post_id, array $opts = [] ): array {
|
||||
|
||||
if ( ! empty( $source_meta ) ) {
|
||||
// Format: {repo_url}@{branch}:{path}
|
||||
$colon_pos = strpos( $source_meta, ':' );
|
||||
// Use strrpos to find the LAST colon (skips the one in 'https:').
|
||||
$colon_pos = strrpos( $source_meta, ':' );
|
||||
if ( $colon_pos !== false ) {
|
||||
// Find the last occurrence of the pattern @branch:
|
||||
$at_pos = strrpos( substr( $source_meta, 0, $colon_pos ), '@' );
|
||||
if ( $at_pos !== false ) {
|
||||
$repo_path = substr( $source_meta, $colon_pos + 1 );
|
||||
$candidate = substr( $source_meta, $colon_pos + 1 );
|
||||
// Validate: path must start with 'pages/' (case-insensitive).
|
||||
// Discard corrupted values left by earlier bugs.
|
||||
if ( strncasecmp( $candidate, 'pages/', 6 ) === 0 ) {
|
||||
$repo_path = $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $repo_path ) ) {
|
||||
// Derive from settings + slug
|
||||
$pages_folder = get_option( 'oribi_sync_pages_folder', '' );
|
||||
if ( empty( $pages_folder ) ) {
|
||||
return [ 'ok' => 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 ───────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user