PHP Classes

File: public/quick-edit.php

Recommend this page to a friend!
  Packages of Adrian M   QuickWP   public/quick-edit.php   Download  
File: public/quick-edit.php
Role: Application script
Content type: text/plain
Description: Application script
Class: QuickWP
Manage WordPress sites using the REST API
Author: By
Last change:
Date: 3 months ago
Size: 27,356 bytes
 

Contents

Class file image Download
<?php /** * QuickWP v2 - Quick Edit by ID Controller */ require_once __DIR__ . '/../vendor/autoload.php'; use QuickWP\QuickWP; use QuickWP\Config\ConfigLoader; use QuickWP\Http\AccessControl; // Initialize $baseDir = dirname(__DIR__); $loader = new ConfigLoader($baseDir); $siteKey = $loader->resolveSiteKey(); $config = $loader->createSiteConfig($siteKey); // Enforce access $access = new AccessControl($config); $access->enforce(); $siteSuffix = $siteKey !== '' ? '?site=' . urlencode($siteKey) : ''; // Initialize QuickWP $qwp = new QuickWP($config); $message = null; $error = null; $loadedItem = null; // Handle actions $action = $_POST['action'] ?? ($_GET['action'] ?? ''); $itemType = $_POST['item_type'] ?? ($_GET['item_type'] ?? 'post'); $cptSlug = $_POST['cpt_slug'] ?? ($_GET['cpt_slug'] ?? ''); $itemId = (int)($_POST['item_id'] ?? ($_GET['item_id'] ?? 0)); // Helper to get appropriate service function getServiceForType(QuickWP $qwp, string $itemType, string $cptSlug = '') { if ($itemType === 'cpt' && $cptSlug !== '') { return ['service' => $qwp->cpt(), 'slug' => $cptSlug]; } elseif ($itemType === 'page') { return ['service' => $qwp->pages(), 'slug' => null]; } return ['service' => $qwp->posts(), 'slug' => null]; } // Load item for editing if ($action === 'load' && $itemId > 0) { $svcInfo = getServiceForType($qwp, $itemType, $cptSlug); if ($svcInfo['slug'] !== null) { $result = $svcInfo['service']->get($svcInfo['slug'], $itemId); } else { $result = $svcInfo['service']->get($itemId); } if (QuickWP::isSuccess($result)) { $loadedItem = $result['json']; } else { $error = 'Could not load item: ' . QuickWP::getError($result); } } // Save changes if ($action === 'save' && $itemId > 0 && $_SERVER['REQUEST_METHOD'] === 'POST') { $data = []; if (isset($_POST['title'])) $data['title'] = trim($_POST['title']); if (isset($_POST['content'])) $data['content'] = trim($_POST['content']); if (isset($_POST['excerpt'])) $data['excerpt'] = trim($_POST['excerpt']); if (isset($_POST['status'])) $data['status'] = $_POST['status']; if (isset($_POST['slug']) && trim($_POST['slug']) !== '') $data['slug'] = trim($_POST['slug']); // Featured image (all types) if (isset($_POST['featured_media'])) { $data['featured_media'] = (int)$_POST['featured_media']; } // Template (posts and pages) if (isset($_POST['template'])) { $template = trim($_POST['template']); if ($template === '__custom__') { $template = trim($_POST['template_custom'] ?? ''); } $data['template'] = $template; } // Post and CPT: categories & tags if ($itemType === 'post' || $itemType === 'cpt') { if (isset($_POST['categories']) && trim($_POST['categories']) !== '') { $data['categories'] = trim($_POST['categories']); } if (isset($_POST['tags']) && trim($_POST['tags']) !== '') { $data['tags'] = trim($_POST['tags']); } } // Page-specific: parent & menu_order if ($itemType === 'page') { if (isset($_POST['parent'])) { $data['parent'] = (int)$_POST['parent']; } if (isset($_POST['menu_order'])) { $data['menu_order'] = (int)$_POST['menu_order']; } } $svcInfo = getServiceForType($qwp, $itemType, $cptSlug); if ($svcInfo['slug'] !== null) { $result = $svcInfo['service']->update($svcInfo['slug'], $itemId, $data); } else { $result = $svcInfo['service']->update($itemId, $data); } if (QuickWP::isSuccess($result)) { $message = ucfirst($itemType === 'cpt' ? $cptSlug : $itemType) . ' updated successfully.'; $loadedItem = $result['json']; } else { $error = 'Update failed: ' . QuickWP::getError($result); // Reload the item if ($svcInfo['slug'] !== null) { $loadResult = $svcInfo['service']->get($svcInfo['slug'], $itemId); } else { $loadResult = $svcInfo['service']->get($itemId); } if (QuickWP::isSuccess($loadResult)) { $loadedItem = $loadResult['json']; } } } // Delete item if ($action === 'delete' && $itemId > 0 && $_SERVER['REQUEST_METHOD'] === 'POST') { $svcInfo = getServiceForType($qwp, $itemType, $cptSlug); $force = isset($_POST['force_delete']) && $_POST['force_delete'] === '1'; if ($svcInfo['slug'] !== null) { $result = $svcInfo['service']->delete($svcInfo['slug'], $itemId, $force); } else { $result = $svcInfo['service']->delete($itemId, $force); } if (QuickWP::isSuccess($result)) { $message = ucfirst($itemType === 'cpt' ? $cptSlug : $itemType) . ' deleted successfully.' . ($force ? ' (permanently)' : ' (trashed)'); $loadedItem = null; // Clear loaded item $itemId = 0; } else { $error = 'Delete failed: ' . QuickWP::getError($result); // Reload the item if it still exists if ($svcInfo['slug'] !== null) { $loadResult = $svcInfo['service']->get($svcInfo['slug'], $itemId); } else { $loadResult = $svcInfo['service']->get($itemId); } if (QuickWP::isSuccess($loadResult)) { $loadedItem = $loadResult['json']; } } } // Navigation links $navLinks = [ ['url' => 'index.php', 'label' => 'Home'], ['url' => 'quick-post.php', 'label' => 'Post'], ['url' => 'quick-page.php', 'label' => 'Page'], ['url' => 'quick-media.php', 'label' => 'Media'], ['url' => 'quick-cpt.php', 'label' => 'CPT'], ['url' => 'quick-taxonomy.php', 'label' => 'Taxonomy'], ['url' => 'quick-list.php', 'label' => 'Browse'], ]; ?><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Quick Edit - QuickWP</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> :root { --primary: #0073aa; --primary-hover: #006298; --bg: #f5f5f5; --card-bg: #fff; --text: #333; --text-muted: #666; --border: #ddd; --success: #46b450; --error: #dc3232; --radius: 8px; } * { box-sizing: border-box; } body { font-family: system-ui, -apple-system, sans-serif; margin: 0; padding: 20px; background: var(--bg); } .container { max-width: 800px; margin: 0 auto; background: var(--card-bg); padding: 20px 24px; border-radius: var(--radius); box-shadow: 0 2px 6px rgba(0,0,0,0.08); } h1 { font-size: 1.4rem; margin: 0 0 16px; } .nav { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--border); } .nav a { padding: 6px 12px; border-radius: 4px; text-decoration: none; color: var(--primary); background: #e5f2fa; font-size: 0.9rem; } .nav a:hover { background: #d0e7f6; } .nav a.active { background: var(--primary); color: #fff; } label { display: block; margin: 14px 0 4px; font-weight: 600; } input[type="text"], input[type="number"], textarea, select { width: 100%; padding: 10px 12px; border-radius: 4px; border: 1px solid var(--border); font-size: 0.95rem; } textarea { min-height: 180px; resize: vertical; font-family: inherit; } .row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; } .row-4 { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 12px; align-items: end; } @media (max-width: 700px) { .row, .row-3, .row-4 { grid-template-columns: 1fr; } } button { margin-top: 16px; padding: 12px 24px; background: var(--primary); color: #fff; border: none; border-radius: var(--radius); font-size: 1rem; font-weight: 600; cursor: pointer; } button:hover { background: var(--primary-hover); } button.secondary { background: #666; } button.secondary:hover { background: #555; } .button-row { margin-top: 20px; } .delete-form { margin-top: 30px; padding-top: 20px; border-top: 2px dashed #f5c6cb; } .delete-section { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; } .checkbox-label { display: flex; align-items: center; gap: 6px; font-size: 0.9rem; cursor: pointer; } .checkbox-label input { margin: 0; } .btn-delete { background: #dc3545; margin-top: 0; } .btn-delete:hover { background: #c82333; } .message { padding: 12px 16px; border-radius: 4px; margin-bottom: 16px; } .message.success { background: #d4edda; color: #155724; } .message.error { background: #f8d7da; color: #721c24; } .load-section { background: #fafafa; border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; margin-bottom: 20px; } .hint { font-size: 0.85rem; color: var(--text-muted); margin-top: 4px; } /* Item Metadata Display */ .item-meta { background: #f0f7fc; border: 1px solid #c3ddf0; border-radius: var(--radius); padding: 14px 16px; margin-bottom: 20px; } .item-meta h3 { margin: 0 0 10px; font-size: 1rem; color: var(--primary); } .meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px 16px; font-size: 0.9rem; } .meta-item { display: flex; flex-wrap: wrap; gap: 4px; } .meta-label { font-weight: 600; color: var(--text); } .meta-value { color: var(--text-muted); word-break: break-word; } .meta-value a { color: var(--primary); } /* Taxonomy Picker Styles */ .picker-container { margin: 4px 0 8px; max-height: 180px; overflow: auto; border: 1px solid var(--border); padding: 8px; border-radius: 4px; background: #fafafa; font-size: 0.9rem; } .picker-chip { display: inline-block; margin: 3px 6px 3px 0; white-space: nowrap; cursor: pointer; } .picker-chip:hover { background: #e5f2fa; border-radius: 3px; padding: 2px 4px; margin: 1px 4px 1px -2px; } .picker-chip input { margin-right: 4px; } .picker-id { color: var(--text-muted); font-size: 0.8rem; } .picker-empty { color: var(--text-muted); font-size: 0.85rem; } .picker-select { width: 100%; padding: 8px; border-radius: 4px; border: 1px solid var(--border); font-size: 0.9rem; background: #fff; } .picker-info { margin-top: 6px; } </style> <script src="assets/taxonomy-picker.js"></script> </head> <body> <div class="container"> <nav class="nav"> <?php foreach ($navLinks as $link): ?> <a href="<?= $link['url'] . $siteSuffix ?>" class="<?= !empty($link['active']) ? 'active' : '' ?>"> <?= htmlspecialchars($link['label']) ?> </a> <?php endforeach; ?> </nav> <h1>Quick Edit by ID</h1> <?php if ($message): ?> <div class="message success"><?= htmlspecialchars($message) ?></div> <?php endif; ?> <?php if ($error): ?> <div class="message error"><?= htmlspecialchars($error) ?></div> <?php endif; ?> <!-- Load Item Form --> <div class="load-section"> <form method="get"> <input type="hidden" name="action" value="load"> <?php if ($siteKey !== ''): ?> <input type="hidden" name="site" value="<?= htmlspecialchars($siteKey) ?>"> <?php endif; ?> <div class="row-4"> <div> <label for="item_type">Type</label> <select id="item_type" name="item_type" onchange="toggleCptField()"> <option value="post" <?= $itemType === 'post' ? 'selected' : '' ?>>Post</option> <option value="page" <?= $itemType === 'page' ? 'selected' : '' ?>>Page</option> <option value="cpt" <?= $itemType === 'cpt' ? 'selected' : '' ?>>Custom Post Type</option> </select> </div> <div id="cpt-slug-field" style="<?= $itemType === 'cpt' ? '' : 'display: none;' ?>"> <label for="cpt_slug">CPT Slug</label> <input type="text" id="cpt_slug" name="cpt_slug" value="<?= htmlspecialchars($cptSlug) ?>" placeholder="e.g. product"> </div> <div> <label for="item_id">Item ID</label> <input type="number" id="item_id" name="item_id" min="1" value="<?= $itemId > 0 ? $itemId : '' ?>" required> </div> <div style="display: flex; align-items: flex-end;"> <button type="submit" class="secondary" style="margin-top: 0;">Load Item</button> </div> </div> <p class="hint" id="cpt-hint" style="<?= $itemType === 'cpt' ? '' : 'display: none;' ?>">Enter the CPT slug (e.g., <code>product</code>, <code>portfolio</code>, <code>event</code>)</p> </form> </div> <?php if ($loadedItem): ?> <!-- Item Metadata --> <div class="item-meta"> <h3>? Item Info</h3> <div class="meta-grid"> <div class="meta-item"> <span class="meta-label">ID:</span> <span class="meta-value"><?= $loadedItem['id'] ?? 'N/A' ?></span> </div> <div class="meta-item"> <span class="meta-label">Type:</span> <span class="meta-value"><?= htmlspecialchars($loadedItem['type'] ?? $itemType) ?></span> </div> <div class="meta-item"> <span class="meta-label">Status:</span> <span class="meta-value"><?= htmlspecialchars($loadedItem['status'] ?? 'unknown') ?></span> </div> <div class="meta-item"> <span class="meta-label">Slug:</span> <span class="meta-value"><?= htmlspecialchars($loadedItem['slug'] ?? '') ?></span> </div> <?php if (!empty($loadedItem['link'])): ?> <div class="meta-item"> <span class="meta-label">Link:</span> <span class="meta-value"><a href="<?= htmlspecialchars($loadedItem['link']) ?>" target="_blank" rel="noopener">View &rarr;</a></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['categories'])): ?> <div class="meta-item"> <span class="meta-label">Category IDs:</span> <span class="meta-value"><?= implode(', ', (array)$loadedItem['categories']) ?></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['tags'])): ?> <div class="meta-item"> <span class="meta-label">Tag IDs:</span> <span class="meta-value"><?= implode(', ', (array)$loadedItem['tags']) ?></span> </div> <?php endif; ?> <?php if (isset($loadedItem['parent']) && $loadedItem['parent'] > 0): ?> <div class="meta-item"> <span class="meta-label">Parent ID:</span> <span class="meta-value"><?= (int)$loadedItem['parent'] ?></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['featured_media'])): ?> <div class="meta-item"> <span class="meta-label">Featured Image ID:</span> <span class="meta-value"><?= (int)$loadedItem['featured_media'] ?></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['author'])): ?> <div class="meta-item"> <span class="meta-label">Author ID:</span> <span class="meta-value"><?= (int)$loadedItem['author'] ?></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['date'])): ?> <div class="meta-item"> <span class="meta-label">Created:</span> <span class="meta-value"><?= htmlspecialchars($loadedItem['date']) ?></span> </div> <?php endif; ?> <?php if (!empty($loadedItem['modified'])): ?> <div class="meta-item"> <span class="meta-label">Modified:</span> <span class="meta-value"><?= htmlspecialchars($loadedItem['modified']) ?></span> </div> <?php endif; ?> </div> </div> <!-- Edit Form --> <form method="post"> <input type="hidden" name="action" value="save"> <input type="hidden" name="item_type" value="<?= htmlspecialchars($itemType) ?>"> <input type="hidden" name="cpt_slug" value="<?= htmlspecialchars($cptSlug) ?>"> <input type="hidden" name="item_id" value="<?= $itemId ?>"> <?php if ($siteKey !== ''): ?> <input type="hidden" name="site" value="<?= htmlspecialchars($siteKey) ?>"> <?php endif; ?> <label for="title">Title</label> <input type="text" id="title" name="title" value="<?= htmlspecialchars($loadedItem['title']['rendered'] ?? $loadedItem['title']['raw'] ?? '') ?>"> <label for="content">Content</label> <textarea id="content" name="content"><?= htmlspecialchars($loadedItem['content']['raw'] ?? $loadedItem['content']['rendered'] ?? '') ?></textarea> <label for="excerpt">Excerpt</label> <textarea id="excerpt" name="excerpt" style="min-height: 80px;"><?= htmlspecialchars($loadedItem['excerpt']['raw'] ?? $loadedItem['excerpt']['rendered'] ?? '') ?></textarea> <div class="row"> <div> <label for="status">Status</label> <select id="status" name="status"> <?php $currentStatus = $loadedItem['status'] ?? 'draft'; $statuses = ['draft', 'publish', 'pending', 'private']; foreach ($statuses as $s): ?> <option value="<?= $s ?>" <?= $currentStatus === $s ? 'selected' : '' ?>><?= ucfirst($s) ?></option> <?php endforeach; ?> </select> </div> <div> <label for="slug">Slug</label> <input type="text" id="slug" name="slug" value="<?= htmlspecialchars($loadedItem['slug'] ?? '') ?>"> </div> </div> <?php if ($itemType === 'post' || $itemType === 'cpt'): ?> <div class="row"> <div> <label for="categories">Categories</label> <div id="categories-picker" class="picker-container"> <small class="picker-empty">Loading categories...</small> </div> <input type="text" id="categories" name="categories" placeholder="e.g. 1, 5, 12" value="<?= htmlspecialchars(implode(', ', (array)($loadedItem['categories'] ?? []))) ?>"> <p class="hint">Select above or enter comma-separated IDs</p> </div> <div> <label for="tags">Tags</label> <div id="tags-picker" class="picker-container"> <small class="picker-empty">Loading tags...</small> </div> <input type="text" id="tags" name="tags" placeholder="e.g. 3, 7" value="<?= htmlspecialchars(implode(', ', (array)($loadedItem['tags'] ?? []))) ?>"> <p class="hint">Select above or enter comma-separated IDs</p> </div> </div> <?php endif; ?> <?php if ($itemType === 'page'): ?> <div class="row"> <div> <label for="parent_id">Parent Page</label> <div id="parent-picker" class="picker-container"> <small class="picker-empty">Loading pages...</small> </div> <input type="number" id="parent_id" name="parent" min="0" value="<?= (int)($loadedItem['parent'] ?? 0) ?>"> <p class="hint">Select above or enter ID (0 = no parent)</p> </div> <div> <label for="menu_order">Menu Order</label> <input type="number" id="menu_order" name="menu_order" min="0" value="<?= (int)($loadedItem['menu_order'] ?? 0) ?>"> </div> </div> <?php endif; ?> <div class="row"> <div> <label for="featured_media">Featured Image (Media ID)</label> <input type="number" id="featured_media" name="featured_media" min="0" value="<?= (int)($loadedItem['featured_media'] ?? 0) ?>"> <p class="hint">Enter media ID or leave 0 for no featured image</p> </div> <div> <label for="template">Template</label> <?php // Fetch templates dynamically from WordPress (with config fallback) $templates = ($itemType === 'page') ? $qwp->templates()->getPageTemplates() : $qwp->templates()->getPostTemplates(); $currentTemplate = $loadedItem['template'] ?? ''; if (!empty($templates)): ?> <select id="template" name="template"> <?php foreach ($templates as $file => $name): ?> <option value="<?= htmlspecialchars($file) ?>" <?= $currentTemplate === $file ? 'selected' : '' ?>> <?= htmlspecialchars($name) ?> </option> <?php endforeach; ?> <option value="__custom__" <?= ($currentTemplate !== '' && !isset($templates[$currentTemplate])) ? 'selected' : '' ?>>Custom...</option> </select> <input type="text" id="template_custom" name="template_custom" value="<?= ($currentTemplate !== '' && !isset($templates[$currentTemplate])) ? htmlspecialchars($currentTemplate) : '' ?>" style="margin-top: 6px; <?= ($currentTemplate === '' || isset($templates[$currentTemplate])) ? 'display: none;' : '' ?>"> <?php else: ?> <input type="text" id="template" name="template" value="<?= htmlspecialchars($currentTemplate) ?>"> <?php endif; ?> </div> </div> <div class="button-row"> <button type="submit">Save Changes</button> </div> </form> <!-- Delete Form (separate to avoid accidental submission) --> <form method="post" class="delete-form" onsubmit="return confirm('Are you sure you want to delete this <?= $itemType === 'cpt' ? htmlspecialchars($cptSlug) : $itemType ?>?');"> <input type="hidden" name="action" value="delete"> <input type="hidden" name="item_type" value="<?= htmlspecialchars($itemType) ?>"> <input type="hidden" name="cpt_slug" value="<?= htmlspecialchars($cptSlug) ?>"> <input type="hidden" name="item_id" value="<?= $itemId ?>"> <?php if ($siteKey !== ''): ?> <input type="hidden" name="site" value="<?= htmlspecialchars($siteKey) ?>"> <?php endif; ?> <div class="delete-section"> <label class="checkbox-label"> <input type="checkbox" name="force_delete" value="1"> <span>Permanently delete (skip trash)</span> </label> <button type="submit" class="btn-delete">Delete <?= ucfirst($itemType === 'cpt' ? $cptSlug : $itemType) ?></button> </div> </form> <?php endif; ?> </div> <?php if ($loadedItem): ?> <script> TaxonomyPicker.init({ <?php if ($itemType === 'post' || $itemType === 'cpt'): ?> categoriesEndpoint: <?= json_encode($config->getCategoriesEndpoint()) ?>, tagsEndpoint: <?= json_encode($config->getTagsEndpoint()) ?> <?php else: ?> pagesEndpoint: <?= json_encode($config->getPagesEndpoint()) ?> <?php endif; ?> }); // Template dropdown toggle (function() { const templateSelect = document.getElementById('template'); const templateCustom = document.getElementById('template_custom'); if (templateSelect && templateSelect.tagName === 'SELECT' && templateCustom) { templateSelect.addEventListener('change', function() { if (this.value === '__custom__') { templateCustom.style.display = ''; templateCustom.focus(); } else { templateCustom.style.display = 'none'; templateCustom.value = ''; } }); } })(); </script> <?php endif; ?> <script> function toggleCptField() { const typeSelect = document.getElementById('item_type'); const cptField = document.getElementById('cpt-slug-field'); const cptHint = document.getElementById('cpt-hint'); if (typeSelect.value === 'cpt') { cptField.style.display = ''; cptHint.style.display = ''; } else { cptField.style.display = 'none'; cptHint.style.display = 'none'; } } </script> </body> </html>