diff --git a/dist/oribi-tech-sync.zip b/dist/oribi-tech-sync.zip
index e1ed639..75a0610 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 35333c3..cfcd30c 100644
--- a/includes/admin.php
+++ b/includes/admin.php
@@ -8,27 +8,47 @@
if ( ! defined( 'ABSPATH' ) ) exit;
-// ─── Admin bar pull button (front-end) ──────────────────────────────────────
+// ─── Admin bar pull buttons (front-end) ─────────────────────────────────────
add_action( 'admin_bar_menu', function ( WP_Admin_Bar $wp_admin_bar ) {
- // Front-end only, logged-in admins, singular pages/posts
if ( is_admin() ) return;
if ( ! is_user_logged_in() ) return;
if ( ! current_user_can( 'manage_options' ) ) return;
- if ( ! is_singular() ) return;
-
- $post = get_queried_object();
- if ( ! $post instanceof WP_Post ) return;
+ // "Pull All" — visible everywhere on the front-end
$wp_admin_bar->add_node( [
- 'id' => 'oribi-sync-pull',
- 'title' => 'Pull Page',
+ 'id' => 'oribi-sync-pull-all',
+ 'title' => 'Pull All',
'href' => '#',
'meta' => [
- 'title' => 'Pull this page and theme from Git',
+ 'title' => 'Pull all pages and theme from Git',
],
] );
+
+ // "Pull Page" — only on singular pages/posts
+ if ( is_singular() ) {
+ $post = get_queried_object();
+ if ( $post instanceof WP_Post ) {
+ $wp_admin_bar->add_node( [
+ 'id' => 'oribi-sync-pull',
+ 'title' => 'Pull Page',
+ 'href' => '#',
+ 'meta' => [
+ 'title' => 'Pull this page and theme from Git',
+ ],
+ ] );
+ }
+ }
}, 100 );
+// AJAX handler for the admin bar "Pull All" button
+add_action( 'wp_ajax_oribi_sync_pull_all_pages', function () {
+ check_ajax_referer( 'oribi_sync_pull_all_pages' );
+ if ( ! current_user_can( 'manage_options' ) ) wp_send_json_error( 'Permission denied.', 403 );
+
+ $result = oribi_sync_run();
+ $result['ok'] ? wp_send_json_success( $result ) : wp_send_json_error( $result, 500 );
+} );
+
// AJAX handler for the admin bar pull button (no REST API exposure)
add_action( 'wp_ajax_oribi_sync_pull_page', function () {
check_ajax_referer( 'oribi_sync_pull_page' );
@@ -41,6 +61,57 @@ add_action( 'wp_ajax_oribi_sync_pull_page', function () {
$result['ok'] ? wp_send_json_success( $result ) : wp_send_json_error( $result, 500 );
} );
+// Front-end script for the "Pull All" admin bar button
+add_action( 'wp_footer', function () {
+ if ( ! is_user_logged_in() ) return;
+ if ( ! current_user_can( 'manage_options' ) ) return;
+ if ( ! is_admin_bar_showing() ) return;
+
+ $ajax_url = admin_url( 'admin-ajax.php' );
+ $nonce_all = wp_create_nonce( 'oribi_sync_pull_all_pages' );
+ ?>
+
+ 'POST',
- 'callback' => 'oribi_sync_rest_sync',
- 'permission_callback' => function () {
- return current_user_can( 'manage_options' );
- },
- ] );
-
- // ── Sync status ───────────────────────────────────────────────────────
- register_rest_route( 'oribi-sync/v1', '/status', [
- 'methods' => 'GET',
- 'callback' => 'oribi_sync_rest_status',
- 'permission_callback' => function () {
- return current_user_can( 'manage_options' );
- },
- ] );
-
- // ── Push page to repo ──────────────────────────────────────────────────
- register_rest_route( 'oribi-sync/v1', '/push', [
- 'methods' => 'POST',
- 'callback' => 'oribi_sync_rest_push',
- 'permission_callback' => function () {
- return current_user_can( 'manage_options' );
- },
- ] );
-
- // ── Push all synced pages to repo ──────────────────────────────────────
- register_rest_route( 'oribi-sync/v1', '/push-all', [
- 'methods' => 'POST',
- 'callback' => 'oribi_sync_rest_push_all',
- 'permission_callback' => function () {
- return current_user_can( 'manage_options' );
- },
- ] );
-
- // ── Webhook (secret-based auth, no WP login required) ─────────────────
- register_rest_route( 'oribi-sync/v1', '/webhook', [
- 'methods' => 'POST',
- 'callback' => 'oribi_sync_rest_webhook',
- 'permission_callback' => '__return_true', // Auth handled in callback
- ] );
-} );
-
-/**
- * REST: Trigger sync.
- */
-function oribi_sync_rest_sync( WP_REST_Request $request ): WP_REST_Response {
- $dry_run = (bool) $request->get_param( 'dry_run' );
- $result = oribi_sync_run( $dry_run );
-
- // After pulling, push local changes back (skip during dry-run)
- if ( ! $dry_run ) {
- $push = oribi_sync_push_all();
- $result['push'] = $push['results'];
- }
-
- return new WP_REST_Response( $result, $result['ok'] ? 200 : 500 );
-}
-
-/**
- * REST: Get last sync status.
- */
-function oribi_sync_rest_status(): WP_REST_Response {
- return new WP_REST_Response( [
- 'last_run' => get_option( 'oribi_sync_last_run', null ),
- 'log' => array_slice( get_option( 'oribi_sync_log', [] ), 0, 5 ),
- 'repo' => get_option( 'oribi_sync_repo', '' ),
- 'branch' => get_option( 'oribi_sync_branch', 'main' ),
- 'provider' => oribi_sync_get_provider(),
- 'has_pat' => ! empty( get_option( 'oribi_sync_pat', '' ) ),
- ] );
-}
-
-/**
- * REST: Webhook trigger.
- *
- * Validates using a shared secret stored in the WP option oribi_sync_webhook_secret
- * or the constant ORIBI_SYNC_WEBHOOK_SECRET.
- *
- * Accepts GitHub-style X-Hub-Signature-256 header, or a simple
- * Authorization: Bearer header.
- */
-function oribi_sync_rest_webhook( WP_REST_Request $request ): WP_REST_Response {
- $secret = defined( 'ORIBI_SYNC_WEBHOOK_SECRET' )
- ? ORIBI_SYNC_WEBHOOK_SECRET
- : get_option( 'oribi_sync_webhook_secret', '' );
-
- if ( empty( $secret ) ) {
- return new WP_REST_Response( [ 'error' => 'Webhook secret not configured.' ], 403 );
- }
-
- // Check Authorization: Bearer
- $auth = $request->get_header( 'Authorization' );
- if ( $auth && preg_match( '/^Bearer\s+(.+)$/i', $auth, $m ) ) {
- if ( ! hash_equals( $secret, $m[1] ) ) {
- return new WP_REST_Response( [ 'error' => 'Invalid secret.' ], 403 );
- }
- }
- // Check GitHub X-Hub-Signature-256
- elseif ( $sig = $request->get_header( 'X-Hub-Signature-256' ) ) {
- $body = $request->get_body();
- $expected = 'sha256=' . hash_hmac( 'sha256', $body, $secret );
- if ( ! hash_equals( $expected, $sig ) ) {
- return new WP_REST_Response( [ 'error' => 'Invalid signature.' ], 403 );
- }
- } else {
- return new WP_REST_Response( [ 'error' => 'Missing authentication.' ], 403 );
- }
-
- // Run sync
- $result = oribi_sync_run();
-
- return new WP_REST_Response( $result, $result['ok'] ? 200 : 500 );
-}
-
-/**
- * REST: Push a single page to the repo.
- */
-function oribi_sync_rest_push( WP_REST_Request $request ): WP_REST_Response {
- $post_id = (int) $request->get_param( 'post_id' );
- if ( $post_id < 1 ) {
- return new WP_REST_Response( [ 'ok' => false, 'message' => 'Missing or invalid post_id.' ], 400 );
- }
-
- $opts = [];
- $message = $request->get_param( 'message' );
- if ( ! empty( $message ) ) {
- $opts['message'] = sanitize_text_field( $message );
- }
-
- $result = oribi_sync_push_page( $post_id, $opts );
-
- return new WP_REST_Response( $result, $result['ok'] ? 200 : 500 );
-}
-
-/**
- * REST: Push all synced pages to the repo.
- */
diff --git a/oribi-tech-sync.php b/oribi-tech-sync.php
index b0e535c..264bb06 100644
--- a/oribi-tech-sync.php
+++ b/oribi-tech-sync.php
@@ -22,7 +22,6 @@ require_once ORIBI_SYNC_DIR . 'includes/sync-engine.php';
require_once ORIBI_SYNC_DIR . 'includes/push-client.php';
require_once ORIBI_SYNC_DIR . 'includes/post-sync.php';
require_once ORIBI_SYNC_DIR . 'includes/admin.php';
-require_once ORIBI_SYNC_DIR . 'includes/rest.php';
require_once ORIBI_SYNC_DIR . 'includes/theme-preview.php';
// ─── Activation / Deactivation ────────────────────────────────────────────────