Add Oribi Sync plugin for syncing WordPress pages and theme files from a Git repository
- 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.
This commit is contained in:
233
includes/theme-preview.php
Normal file
233
includes/theme-preview.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?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.
|
||||
Reference in New Issue
Block a user