- Implement encryption helpers for storing and retrieving the Personal Access Token (PAT). - Create REST API endpoints for triggering sync, checking sync status, and handling webhooks. - Develop the sync engine to fetch pages from the Git repository, create/update WordPress pages, and trash removed pages. - Add functionality for previewing and applying theme files from the repository. - Set up plugin activation and deactivation hooks to manage default options and scheduled tasks. - Implement uninstall routine to clean up plugin options and metadata from posts.
266 lines
12 KiB
PHP
266 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Oribi Sync — Admin settings page.
|
|
*
|
|
* Registers the Settings → Oribi Sync page, handles saving,
|
|
* and provides the "Sync Now" action.
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) exit;
|
|
|
|
// ─── Register admin menu ──────────────────────────────────────────────────────
|
|
add_action( 'admin_menu', function () {
|
|
add_options_page(
|
|
'Oribi Sync',
|
|
'Oribi Sync',
|
|
'manage_options',
|
|
'oribi-sync',
|
|
'oribi_sync_settings_page'
|
|
);
|
|
} );
|
|
|
|
// ─── Enqueue admin CSS on our page only ───────────────────────────────────────
|
|
add_action( 'admin_enqueue_scripts', function ( $hook ) {
|
|
if ( $hook !== 'settings_page_oribi-sync' ) return;
|
|
wp_enqueue_style( 'oribi-sync-admin', ORIBI_SYNC_URL . 'assets/admin.css', [], ORIBI_SYNC_VERSION );
|
|
} );
|
|
|
|
// ─── Handle form submissions ──────────────────────────────────────────────────
|
|
add_action( 'admin_post_oribi_sync_save_settings', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_save_settings' );
|
|
|
|
$repo = sanitize_text_field( wp_unslash( $_POST['oribi_sync_repo'] ?? '' ) );
|
|
$branch = sanitize_text_field( wp_unslash( $_POST['oribi_sync_branch'] ?? 'main' ) );
|
|
$provider = sanitize_text_field( wp_unslash( $_POST['oribi_sync_provider'] ?? '' ) );
|
|
$pat = wp_unslash( $_POST['oribi_sync_pat'] ?? '' );
|
|
|
|
update_option( 'oribi_sync_repo', $repo, 'no' );
|
|
update_option( 'oribi_sync_branch', $branch, 'no' );
|
|
update_option( 'oribi_sync_provider', $provider, 'no' );
|
|
|
|
// Only update PAT if a new one was provided (non-empty)
|
|
if ( ! empty( $pat ) ) {
|
|
oribi_sync_save_pat( $pat );
|
|
}
|
|
|
|
wp_redirect( add_query_arg( 'oribi_sync_saved', '1', admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
add_action( 'admin_post_oribi_sync_run', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_run' );
|
|
|
|
$result = oribi_sync_run();
|
|
|
|
set_transient( 'oribi_sync_result', $result, 60 );
|
|
|
|
wp_redirect( add_query_arg( 'oribi_sync_done', '1', admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
add_action( 'admin_post_oribi_sync_dry_run', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_dry_run' );
|
|
|
|
$result = oribi_sync_run( true );
|
|
|
|
set_transient( 'oribi_sync_result', $result, 60 );
|
|
|
|
wp_redirect( add_query_arg( 'oribi_sync_done', 'dry', admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
add_action( 'admin_post_oribi_sync_clear_pat', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_clear_pat' );
|
|
|
|
delete_option( 'oribi_sync_pat' );
|
|
|
|
wp_redirect( add_query_arg( 'oribi_sync_saved', 'pat_cleared', admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
// ─── Settings page renderer ──────────────────────────────────────────────────
|
|
function oribi_sync_settings_page() {
|
|
if ( ! current_user_can( 'manage_options' ) ) return;
|
|
|
|
$repo = get_option( 'oribi_sync_repo', '' );
|
|
$branch = get_option( 'oribi_sync_branch', 'main' );
|
|
$provider = get_option( 'oribi_sync_provider', '' );
|
|
$has_pat = ! empty( get_option( 'oribi_sync_pat', '' ) );
|
|
$last_run = get_option( 'oribi_sync_last_run', '' );
|
|
$log = get_option( 'oribi_sync_log', [] );
|
|
|
|
// Transient result (after sync / dry-run)
|
|
$sync_result = get_transient( 'oribi_sync_result' );
|
|
if ( $sync_result ) delete_transient( 'oribi_sync_result' );
|
|
|
|
$saved = $_GET['oribi_sync_saved'] ?? '';
|
|
$done = $_GET['oribi_sync_done'] ?? '';
|
|
?>
|
|
<div class="wrap oribi-sync-wrap">
|
|
<h1>Oribi Sync</h1>
|
|
|
|
<?php if ( $saved === '1' ): ?>
|
|
<div class="notice notice-success is-dismissible"><p>Settings saved.</p></div>
|
|
<?php elseif ( $saved === 'pat_cleared' ): ?>
|
|
<div class="notice notice-warning is-dismissible"><p>PAT has been cleared.</p></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( $sync_result ): ?>
|
|
<div class="notice <?php echo $sync_result['ok'] ? 'notice-success' : 'notice-error'; ?>">
|
|
<p>
|
|
<strong><?php echo $done === 'dry' ? '🔍 Dry-run results' : '✅ Sync complete'; ?></strong>
|
|
</p>
|
|
<ul style="list-style:disc; padding-left:1.5rem;">
|
|
<?php if ( ! empty( $sync_result['created'] ) ): ?>
|
|
<li>Created: <?php echo esc_html( implode( ', ', $sync_result['created'] ) ); ?></li>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $sync_result['updated'] ) ): ?>
|
|
<li>Updated: <?php echo esc_html( implode( ', ', $sync_result['updated'] ) ); ?></li>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $sync_result['trashed'] ) ): ?>
|
|
<li>Trashed: <?php echo esc_html( implode( ', ', $sync_result['trashed'] ) ); ?></li>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $sync_result['skipped'] ) ): ?>
|
|
<li>Skipped: <?php echo esc_html( implode( ', ', $sync_result['skipped'] ) ); ?></li>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $sync_result['errors'] ) ): ?>
|
|
<li style="color:#d63638;">Errors: <?php echo esc_html( implode( '; ', $sync_result['errors'] ) ); ?></li>
|
|
<?php endif; ?>
|
|
</ul>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Settings form -->
|
|
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="oribi-sync-form">
|
|
<input type="hidden" name="action" value="oribi_sync_save_settings" />
|
|
<?php wp_nonce_field( 'oribi_sync_save_settings' ); ?>
|
|
|
|
<table class="form-table" role="presentation">
|
|
<tr>
|
|
<th scope="row"><label for="oribi_sync_repo">Repository URL</label></th>
|
|
<td>
|
|
<input type="url" name="oribi_sync_repo" id="oribi_sync_repo"
|
|
value="<?php echo esc_attr( $repo ); ?>"
|
|
class="regular-text" placeholder="https://github.com/owner/repo" />
|
|
<p class="description">HTTPS URL to the Git repository (any provider).</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><label for="oribi_sync_provider">Provider</label></th>
|
|
<td>
|
|
<select name="oribi_sync_provider" id="oribi_sync_provider" class="regular-text">
|
|
<option value=""<?php selected( $provider, '' ); ?>>Auto-detect from URL</option>
|
|
<?php foreach ( oribi_sync_providers() as $key => $label ): ?>
|
|
<option value="<?php echo esc_attr( $key ); ?>"<?php selected( $provider, $key ); ?>>
|
|
<?php echo esc_html( $label ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<p class="description">Select your Git hosting provider, or leave on auto-detect.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><label for="oribi_sync_branch">Branch</label></th>
|
|
<td>
|
|
<input type="text" name="oribi_sync_branch" id="oribi_sync_branch"
|
|
value="<?php echo esc_attr( $branch ); ?>"
|
|
class="regular-text" placeholder="main" />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><label for="oribi_sync_pat">Personal Access Token</label></th>
|
|
<td>
|
|
<input type="password" name="oribi_sync_pat" id="oribi_sync_pat"
|
|
value="" class="regular-text"
|
|
placeholder="<?php echo $has_pat ? '•••••••• (saved — leave blank to keep)' : 'ghp_…'; ?>"
|
|
autocomplete="off" />
|
|
<p class="description">
|
|
Read token for your repo. Stored encrypted in the database.
|
|
<?php if ( $has_pat ): ?>
|
|
|
|
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=oribi_sync_clear_pat' ), 'oribi_sync_clear_pat' ) ); ?>"
|
|
onclick="return confirm('Clear the stored PAT?');"
|
|
style="color:#d63638;">Clear PAT</a>
|
|
<?php endif; ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<?php submit_button( 'Save Settings' ); ?>
|
|
</form>
|
|
|
|
<hr />
|
|
|
|
<!-- Sync actions -->
|
|
<h2>Sync Actions</h2>
|
|
<p class="description">
|
|
The plugin reads two folders from the repo: <code>pages/</code> (PHP or HTML files → WP pages)
|
|
and <code>theme/</code> (theme style files → preview & manual apply). Everything else is ignored.<br />
|
|
PHP files are executed using the <code>oribi_b()</code> block helpers (requires Oribi Tech Setup plugin).
|
|
HTML files are used as raw Gutenberg block markup.
|
|
</p>
|
|
<p>
|
|
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=oribi_sync_run' ), 'oribi_sync_run' ) ); ?>"
|
|
class="button button-primary"
|
|
onclick="return confirm('This will overwrite pages with content from the repository. Continue?');">
|
|
🔄 Sync Now
|
|
</a>
|
|
|
|
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=oribi_sync_dry_run' ), 'oribi_sync_dry_run' ) ); ?>"
|
|
class="button">
|
|
🔍 Dry Run
|
|
</a>
|
|
|
|
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=oribi_sync_theme_preview' ), 'oribi_sync_theme_preview' ) ); ?>"
|
|
class="button">
|
|
🎨 Preview Theme Files
|
|
</a>
|
|
</p>
|
|
|
|
<?php if ( $last_run ): ?>
|
|
<p><strong>Last sync:</strong> <?php echo esc_html( $last_run ); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<!-- Theme files preview -->
|
|
<?php if ( isset( $_GET['oribi_sync_tab'] ) && $_GET['oribi_sync_tab'] === 'theme' ): ?>
|
|
<hr />
|
|
<h2>Theme Files Preview</h2>
|
|
<?php oribi_sync_render_theme_preview(); ?>
|
|
<?php endif; ?>
|
|
|
|
<!-- Sync log -->
|
|
<?php if ( ! empty( $log ) ): ?>
|
|
<hr />
|
|
<h2>Sync Log</h2>
|
|
<table class="widefat fixed striped oribi-sync-log">
|
|
<thead><tr>
|
|
<th style="width:160px;">Time</th>
|
|
<th>Created</th>
|
|
<th>Updated</th>
|
|
<th>Trashed</th>
|
|
<th>Skipped</th>
|
|
<th>Errors</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
<?php foreach ( array_slice( $log, 0, 10 ) as $entry ): ?>
|
|
<tr>
|
|
<td><?php echo esc_html( $entry['time'] ?? '—' ); ?></td>
|
|
<td><?php echo esc_html( implode( ', ', $entry['created'] ?? [] ) ?: '—' ); ?></td>
|
|
<td><?php echo esc_html( implode( ', ', $entry['updated'] ?? [] ) ?: '—' ); ?></td>
|
|
<td><?php echo esc_html( implode( ', ', $entry['trashed'] ?? [] ) ?: '—' ); ?></td>
|
|
<td><?php echo esc_html( implode( ', ', $entry['skipped'] ?? [] ) ?: '—' ); ?></td>
|
|
<td style="color:#d63638;"><?php echo esc_html( implode( '; ', $entry['errors'] ?? [] ) ?: '—' ); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
}
|