Refactor Oribi Sync settings: remove pages folder option and enhance case-insensitive directory handling

This commit is contained in:
Matt Batchelder
2026-02-20 22:15:46 -05:00
parent 9e93ca27b4
commit 3c8c38acde
6 changed files with 113 additions and 148 deletions

View File

@@ -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 ───────────────────────────────────────────────────