WebOrbiton
v1.0.0.0

StocketBase

189 lines · 8.7 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/includes/config.php';
  6. require_once __DIR__ . '/includes/database.php';
  7. require_once __DIR__ . '/includes/auth.php';
  8. require_once __DIR__ . '/includes/csrf.php';
  9. require_once __DIR__ . '/includes/activity-log.php';
  10. require_once __DIR__ . '/includes/site-front.php';
  11. ​
  12. Auth::boot();
  13. Auth::requireRoleAtLeast(Auth::ROLE_STORE_OWNER);
  14. ​
  15. $currentUser = Auth::user();
  16. $currentRole = Auth::role();
  17. $db = Database::site();
  18. ​
  19. $flashMessage = null;
  20. $flashType = 'success';
  21. ​
  22. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  23. if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
  24. $flashMessage = 'Security check failed, please try again.';
  25. $flashType = 'error';
  26. } else {
  27. require __DIR__ . '/dashboard-actions/manage-ads.php';
  28. ActivityLog::record('ads.' . (string) ($_POST['action'] ?? ''));
  29. $flashMessage = 'Saved.';
  30. }
  31. }
  32. ​
  33. $editId = (int) ($_GET['edit'] ?? 0) ?: null;
  34. $editingAd = null;
  35. if ($editId !== null) {
  36. $statement = $db->prepare('SELECT * FROM ads WHERE id = :id LIMIT 1');
  37. $statement->execute(['id' => $editId]);
  38. $editingAd = $statement->fetch() ?: null;
  39. }
  40. ​
  41. $ads = $db->query('SELECT * FROM ads ORDER BY created_at DESC')->fetchAll();
  42. ​
  43. $dashActivePage = 'ads';
  44. $dashPageTitle = 'Ads';
  45. ​
  46. require __DIR__ . '/includes/dash-header.php';
  47. ​
  48. ?>
  49. ​
  50. <?php if ($flashMessage !== null): ?>
  51. <div class="dash-flash dash-flash-<?= htmlspecialchars($flashType) ?>"><?= Icons::icon($flashType === 'error' ? 'x' : 'check', 'icon icon-sm') ?><?= htmlspecialchars($flashMessage) ?></div>
  52. <?php endif; ?>
  53. ​
  54. <div class="dash-header-row">
  55. <h1 class="dash-title"><?= Icons::icon("ads", "icon icon-lg") ?>Ads</h1>
  56. <button type="button" class="dash-btn dash-btn-primary" id="ad-new-btn"><?= Icons::icon('plus', 'icon icon-sm') ?>New ad</button>
  57. </div>
  58. ​
  59. <table class="dash-table">
  60. <thead>
  61. <tr>
  62. <th><?= Icons::icon('heading', 'icon icon-sm') ?>Name</th>
  63. <th><?= Icons::icon('ads', 'icon icon-sm') ?>Type</th>
  64. <th><?= Icons::icon('flag', 'icon icon-sm') ?>Placement</th>
  65. <th><?= Icons::icon('eye', 'icon icon-sm') ?>Active</th>
  66. <th></th>
  67. </tr>
  68. </thead>
  69. <tbody>
  70. <?php foreach ($ads as $ad): ?>
  71. <tr>
  72. <td><?= htmlspecialchars($ad['name']) ?></td>
  73. <td><?= htmlspecialchars($ad['type']) ?></td>
  74. <td><?= htmlspecialchars(SiteFront::AD_PLACEMENTS[$ad['placement']] ?? $ad['placement']) ?></td>
  75. <td><?= $ad['is_active'] ? 'Yes' : 'No' ?></td>
  76. <td class="dash-table-actions">
  77. <button type="button" class="dash-btn-small js-edit-ad"
  78. data-id="<?= (int) $ad['id'] ?>"
  79. data-name="<?= htmlspecialchars($ad['name'], ENT_QUOTES) ?>"
  80. data-type="<?= htmlspecialchars($ad['type'], ENT_QUOTES) ?>"
  81. data-placement="<?= htmlspecialchars($ad['placement'], ENT_QUOTES) ?>"
  82. data-content="<?= htmlspecialchars($ad['content'], ENT_QUOTES) ?>"
  83. data-link-url="<?= htmlspecialchars((string) $ad['link_url'], ENT_QUOTES) ?>"
  84. data-is-active="<?= (int) $ad['is_active'] ?>"><?= Icons::icon('edit', 'icon icon-sm') ?>Edit</button>
  85. <form method="post" style="display:inline;" onsubmit="return confirm('Delete this ad?');">
  86. <?= Csrf::field() ?>
  87. <input type="hidden" name="action" value="delete_ad">
  88. <input type="hidden" name="ad_id" value="<?= (int) $ad['id'] ?>">
  89. <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete</button>
  90. </form>
  91. </td>
  92. </tr>
  93. <?php endforeach; ?>
  94. <?php if (empty($ads)): ?>
  95. <tr>
  96. <td colspan="5">No ads yet.</td>
  97. </tr>
  98. <?php endif; ?>
  99. </tbody>
  100. </table>
  101. ​
  102. <div id="ad-form-wrapper" class="sidebar-box" style="max-width:560px;margin-top:20px;<?= $editingAd ? '' : 'display:none;' ?>">
  103. <h2 class="dash-subtitle" style="margin-top:0;"><?= Icons::icon('megaphone', 'icon icon-sm') ?><span id="ad-form-title"><?= $editingAd ? 'Edit ad' : 'New ad' ?></span></h2>
  104. <form method="post" id="ad-form">
  105. <?= Csrf::field() ?>
  106. <input type="hidden" name="action" value="save_ad">
  107. <input type="hidden" name="ad_id" id="ad_id_input" value="<?= $editingAd ? (int) $editingAd['id'] : '' ?>">
  108. ​
  109. <label><?= Icons::icon('heading', 'icon icon-sm') ?>Name</label>
  110. <input type="text" name="name" id="ad_name_input" value="<?= htmlspecialchars($editingAd['name'] ?? '') ?>" required>
  111. ​
  112. <label><?= Icons::icon('ads', 'icon icon-sm') ?>Type</label>
  113. <select name="type" id="ad_type_select">
  114. <option value="static_text" <?= ($editingAd['type'] ?? '') === 'static_text' ? 'selected' : '' ?>>Static — text</option>
  115. <option value="static_image" <?= ($editingAd['type'] ?? '') === 'static_image' ? 'selected' : '' ?>>Static — image</option>
  116. <option value="script" <?= ($editingAd['type'] ?? '') === 'script' ? 'selected' : '' ?>>Script (HTML / JS)</option>
  117. </select>
  118. ​
  119. <label><?= Icons::icon('flag', 'icon icon-sm') ?>Placement — where on the site this ad appears</label>
  120. <select name="placement" id="ad_placement_input" required>
  121. <?php foreach (SiteFront::AD_PLACEMENTS as $placementKey => $placementLabel): ?>
  122. <option value="<?= htmlspecialchars($placementKey, ENT_QUOTES) ?>" <?= ($editingAd['placement'] ?? 'header') === $placementKey ? 'selected' : '' ?>><?= htmlspecialchars($placementLabel) ?></option>
  123. <?php endforeach; ?>
  124. </select>
  125. ​
  126. <label id="content_label"><?= Icons::icon('quote', 'icon icon-sm') ?>Content (text, image URL, or raw HTML/JS)</label>
  127. <textarea name="content" id="ad_content_input" rows="6" class="be-code"><?= htmlspecialchars($editingAd['content'] ?? '') ?></textarea>
  128. ​
  129. <label><?= Icons::icon('external', 'icon icon-sm') ?>Link URL (optional, ignored for script ads)</label>
  130. <input type="text" name="link_url" id="ad_link_url_input" value="<?= htmlspecialchars($editingAd['link_url'] ?? '') ?>">
  131. ​
  132. <label><input type="checkbox" name="is_active" id="ad_is_active_input" <?= ($editingAd['is_active'] ?? 1) ? 'checked' : '' ?>> Active</label>
  133. ​
  134. <div class="publish-actions">
  135. <button type="submit" class="dash-btn dash-btn-primary"><?= Icons::icon('check', 'icon icon-sm') ?>Save ad</button>
  136. <button type="button" class="dash-btn" id="ad-cancel-btn"><?= Icons::icon('x', 'icon icon-sm') ?>Cancel</button>
  137. </div>
  138. </form>
  139. </div>
  140. ​
  141. <script>
  142. (function() {
  143. var wrapper = document.getElementById('ad-form-wrapper');
  144. var formTitle = document.getElementById('ad-form-title');
  145. var form = document.getElementById('ad-form');
  146. ​
  147. function resetForm() {
  148. form.reset();
  149. document.getElementById('ad_id_input').value = '';
  150. document.getElementById('ad_placement_input').value = 'header';
  151. document.getElementById('ad_is_active_input').checked = true;
  152. formTitle.textContent = 'New ad';
  153. }
  154. ​
  155. function showForm() {
  156. wrapper.style.display = '';
  157. wrapper.scrollIntoView({
  158. behavior: 'smooth',
  159. block: 'nearest'
  160. });
  161. }
  162. ​
  163. document.getElementById('ad-new-btn').addEventListener('click', function() {
  164. resetForm();
  165. showForm();
  166. });
  167. ​
  168. document.getElementById('ad-cancel-btn').addEventListener('click', function() {
  169. wrapper.style.display = 'none';
  170. resetForm();
  171. });
  172. ​
  173. document.querySelectorAll('.js-edit-ad').forEach(function(button) {
  174. button.addEventListener('click', function() {
  175. document.getElementById('ad_id_input').value = this.dataset.id;
  176. document.getElementById('ad_name_input').value = this.dataset.name;
  177. document.getElementById('ad_type_select').value = this.dataset.type;
  178. document.getElementById('ad_placement_input').value = this.dataset.placement;
  179. document.getElementById('ad_content_input').value = this.dataset.content;
  180. document.getElementById('ad_link_url_input').value = this.dataset.linkUrl;
  181. document.getElementById('ad_is_active_input').checked = this.dataset.isActive === '1';
  182. formTitle.textContent = 'Edit ad';
  183. showForm();
  184. });
  185. });
  186. })();
  187. </script>
  188. ​
  189. <?php require __DIR__ . '/includes/dash-footer.php'; ?>