- 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.
234 lines
9.8 KiB
PHP
234 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Oribi Sync — Theme file preview & apply.
|
|
*
|
|
* Provides an admin screen that fetches files from the repo's `theme/`
|
|
* directory, shows a preview diff against the active theme, and lets the
|
|
* admin selectively apply files.
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) exit;
|
|
|
|
// ─── Handle theme preview request ────────────────────────────────────────────
|
|
add_action( 'admin_post_oribi_sync_theme_preview', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_theme_preview' );
|
|
|
|
$theme_data = oribi_sync_fetch_theme_files();
|
|
set_transient( 'oribi_sync_theme_preview', $theme_data, 300 );
|
|
|
|
wp_redirect( add_query_arg( 'oribi_sync_tab', 'theme', admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
// ─── Handle theme file apply ─────────────────────────────────────────────────
|
|
add_action( 'admin_post_oribi_sync_theme_apply', function () {
|
|
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied.' );
|
|
check_admin_referer( 'oribi_sync_theme_apply' );
|
|
|
|
$selected = $_POST['oribi_sync_theme_files'] ?? [];
|
|
if ( ! is_array( $selected ) || empty( $selected ) ) {
|
|
wp_redirect( add_query_arg( [
|
|
'oribi_sync_tab' => 'theme',
|
|
'oribi_sync_saved' => 'no_files',
|
|
], admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
}
|
|
|
|
$theme_data = get_transient( 'oribi_sync_theme_preview' );
|
|
if ( ! $theme_data || empty( $theme_data['files'] ) ) {
|
|
wp_redirect( add_query_arg( [
|
|
'oribi_sync_tab' => 'theme',
|
|
'oribi_sync_saved' => 'expired',
|
|
], admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
}
|
|
|
|
$applied = [];
|
|
$errors = [];
|
|
$theme_dir = get_template_directory();
|
|
|
|
foreach ( $theme_data['files'] as $file ) {
|
|
$relative = $file['relative'];
|
|
|
|
if ( ! in_array( $relative, $selected, true ) ) continue;
|
|
|
|
$dest = $theme_dir . '/' . $relative;
|
|
|
|
// Safety: only allow CSS, JS, JSON, PHP, HTML extensions
|
|
$ext = strtolower( pathinfo( $relative, PATHINFO_EXTENSION ) );
|
|
$allowed = [ 'css', 'js', 'json', 'php', 'html', 'htm', 'svg', 'txt' ];
|
|
if ( ! in_array( $ext, $allowed, true ) ) {
|
|
$errors[] = $relative . ' — file type not allowed.';
|
|
continue;
|
|
}
|
|
|
|
// Create subdirectory if needed
|
|
$dir = dirname( $dest );
|
|
if ( ! is_dir( $dir ) ) {
|
|
if ( ! wp_mkdir_p( $dir ) ) {
|
|
$errors[] = $relative . ' — could not create directory.';
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Write file
|
|
$written = file_put_contents( $dest, $file['content'] );
|
|
if ( $written === false ) {
|
|
$errors[] = $relative . ' — write failed (check permissions).';
|
|
} else {
|
|
$applied[] = $relative;
|
|
}
|
|
}
|
|
|
|
// Record
|
|
update_option( 'oribi_sync_theme_applied', [
|
|
'time' => current_time( 'mysql' ),
|
|
'applied' => $applied,
|
|
'errors' => $errors,
|
|
], 'no' );
|
|
|
|
set_transient( 'oribi_sync_theme_result', [
|
|
'applied' => $applied,
|
|
'errors' => $errors,
|
|
], 60 );
|
|
|
|
wp_redirect( add_query_arg( [
|
|
'oribi_sync_tab' => 'theme',
|
|
'oribi_sync_saved' => 'theme_applied',
|
|
], admin_url( 'options-general.php?page=oribi-sync' ) ) );
|
|
exit;
|
|
} );
|
|
|
|
// ─── Render theme preview panel (called from settings page) ──────────────────
|
|
|
|
/**
|
|
* Render the theme preview panel within the settings page.
|
|
*/
|
|
function oribi_sync_render_theme_preview(): void {
|
|
$theme_data = get_transient( 'oribi_sync_theme_preview' );
|
|
$theme_result = get_transient( 'oribi_sync_theme_result' );
|
|
if ( $theme_result ) delete_transient( 'oribi_sync_theme_result' );
|
|
|
|
$saved = $_GET['oribi_sync_saved'] ?? '';
|
|
?>
|
|
|
|
<?php if ( $saved === 'theme_applied' && $theme_result ): ?>
|
|
<div class="notice notice-success">
|
|
<p><strong>Theme files applied:</strong></p>
|
|
<?php if ( ! empty( $theme_result['applied'] ) ): ?>
|
|
<ul style="list-style:disc;padding-left:1.5rem;">
|
|
<?php foreach ( $theme_result['applied'] as $f ): ?>
|
|
<li><?php echo esc_html( $f ); ?> ✓</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $theme_result['errors'] ) ): ?>
|
|
<ul style="list-style:disc;padding-left:1.5rem;color:#d63638;">
|
|
<?php foreach ( $theme_result['errors'] as $e ): ?>
|
|
<li><?php echo esc_html( $e ); ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php elseif ( $saved === 'no_files' ): ?>
|
|
<div class="notice notice-warning is-dismissible"><p>No theme files were selected.</p></div>
|
|
<?php elseif ( $saved === 'expired' ): ?>
|
|
<div class="notice notice-warning is-dismissible"><p>Theme preview data expired. Please preview again.</p></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( ! $theme_data ): ?>
|
|
<p>Click <strong>Preview Theme Files</strong> above to fetch files from the repo's <code>theme/</code> directory.</p>
|
|
<?php return; ?>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( ! empty( $theme_data['errors'] ) ): ?>
|
|
<div class="notice notice-error">
|
|
<p><strong>Errors fetching theme files:</strong></p>
|
|
<ul style="list-style:disc;padding-left:1.5rem;">
|
|
<?php foreach ( $theme_data['errors'] as $err ): ?>
|
|
<li><?php echo esc_html( $err ); ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( empty( $theme_data['files'] ) ): ?>
|
|
<p>No files found under <code>theme/</code> in the repository.</p>
|
|
<?php return; ?>
|
|
<?php endif; ?>
|
|
|
|
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
|
|
<input type="hidden" name="action" value="oribi_sync_theme_apply" />
|
|
<?php wp_nonce_field( 'oribi_sync_theme_apply' ); ?>
|
|
|
|
<table class="widefat fixed striped">
|
|
<thead><tr>
|
|
<th style="width:30px;"><input type="checkbox" id="oribi-sync-check-all" /></th>
|
|
<th>File</th>
|
|
<th style="width:100px;">Status</th>
|
|
<th>Preview</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
<?php foreach ( $theme_data['files'] as $file ): ?>
|
|
<tr>
|
|
<td>
|
|
<input type="checkbox" name="oribi_sync_theme_files[]"
|
|
value="<?php echo esc_attr( $file['relative'] ); ?>"
|
|
<?php echo $file['changed'] ? '' : 'disabled'; ?> />
|
|
</td>
|
|
<td><code><?php echo esc_html( $file['relative'] ); ?></code></td>
|
|
<td>
|
|
<?php if ( ! $file['local_exists'] ): ?>
|
|
<span style="color:#00a32a;font-weight:bold;">New</span>
|
|
<?php elseif ( $file['changed'] ): ?>
|
|
<span style="color:#dba617;font-weight:bold;">Changed</span>
|
|
<?php else: ?>
|
|
<span style="color:#999;">Unchanged</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<?php if ( $file['changed'] ): ?>
|
|
<details>
|
|
<summary>View content</summary>
|
|
<pre style="max-height:300px;overflow:auto;background:#f6f7f7;padding:8px;font-size:12px;border:1px solid #ddd;"><?php echo esc_html( $file['content'] ); ?></pre>
|
|
<?php if ( $file['local_exists'] && $file['local_content'] !== null ): ?>
|
|
<details style="margin-top:4px;">
|
|
<summary>Current local content</summary>
|
|
<pre style="max-height:300px;overflow:auto;background:#fef7f1;padding:8px;font-size:12px;border:1px solid #ddd;"><?php echo esc_html( $file['local_content'] ); ?></pre>
|
|
</details>
|
|
<?php endif; ?>
|
|
</details>
|
|
<?php else: ?>
|
|
—
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p style="margin-top:12px;">
|
|
<button type="submit" class="button button-primary"
|
|
onclick="return confirm('Apply selected files to the active theme directory?');">
|
|
Apply Selected Files
|
|
</button>
|
|
<span class="description" style="margin-left:8px;">
|
|
Files will be written directly into the active theme: <code><?php echo esc_html( basename( get_template_directory() ) ); ?></code>
|
|
</span>
|
|
</p>
|
|
</form>
|
|
|
|
<script>
|
|
document.getElementById('oribi-sync-check-all')?.addEventListener('change', function() {
|
|
document.querySelectorAll('input[name="oribi_sync_theme_files[]"]:not(:disabled)')
|
|
.forEach(cb => cb.checked = this.checked);
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
// ─── Hook the theme preview into the settings page ───────────────────────────
|
|
// We append it via a late-bound action so the admin page can call it.
|
|
// The settings page checks for the 'oribi_sync_tab' parameter and renders this.
|