email working - remove clients - preview polish
This commit is contained in:
1 parent
2b1424d127
commit
da83beaec3
19 files changed
+1278
-996
No files matched your search
@@ -30,17 +30,22 @@ A WordPress plugin to manage slides for Hi-School websites.
|
||||
Display slides on any page or post using the shortcode:
|
||||
|
||||
```
|
||||
[hssm_slides]
|
||||
[hssm_slider]
|
||||
```
|
||||
|
||||
`[hssm_slides]` is an alias for the same shortcode.
|
||||
|
||||
Shortcode parameters:
|
||||
- `count` - Number of slides to display (default: 5)
|
||||
- `category` - Filter slides by category (optional)
|
||||
- `site` - Target site slug (manager mode)
|
||||
- `date` - Preview date `YYYY-MM-DD` (manager mode)
|
||||
- `limit` or `count` - Maximum slides to display (default: 10)
|
||||
- `slide` - Optional slide ID to feature first
|
||||
|
||||
Examples:
|
||||
```
|
||||
[hssm_slides count="3"]
|
||||
[hssm_slides count="10" category="featured"]
|
||||
[hssm_slider]
|
||||
[hssm_slides]
|
||||
[hssm_slider site="hi-school" limit="5"]
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
+155
-2
@@ -7,6 +7,68 @@
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
/* Hide Divi theme header/logo on plugin tool pages */
|
||||
body.hssm-tool-page #main-header,
|
||||
body.hssm-tool-page #top-header,
|
||||
body.hssm-tool-page .et-l--header,
|
||||
body.hssm-tool-page header.et-l,
|
||||
body.hssm-tool-page .logo_container,
|
||||
body.hssm-tool-page #et-top-navigation,
|
||||
body.page-template-page-slide-preview #main-header,
|
||||
body.page-template-page-slide-preview #top-header,
|
||||
body.page-template-page-slide-preview .et-l--header,
|
||||
body.page-template-page-slide-preview header.et-l,
|
||||
body.page-template-page-slide-dashboard #main-header,
|
||||
body.page-template-page-slide-dashboard #top-header,
|
||||
body.page-template-page-slide-dashboard .et-l--header,
|
||||
body.page-template-page-slide-dashboard header.et-l {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.hssm-tool-page {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
body.hssm-tool-page #page-container,
|
||||
body.hssm-tool-page #et-boc,
|
||||
body.hssm-tool-page .et-l--body {
|
||||
padding-top: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.hssm-preview-approve-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
max-width: 1400px;
|
||||
margin: 20px auto 0;
|
||||
padding: 16px 24px;
|
||||
background: #fff8e5;
|
||||
border: 1px solid #f0c36d;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.hssm-preview-approve-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.hssm-preview-approve-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.hssm-preview-approve-bar {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
Full-Width Layout Override
|
||||
========================================== */
|
||||
@@ -1117,6 +1179,11 @@
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hssm-slide-item-focus {
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.15);
|
||||
}
|
||||
|
||||
.hssm-slide-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -1399,7 +1466,7 @@
|
||||
|
||||
.hssm-table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 80px 1fr 120px 180px 150px 140px;
|
||||
grid-template-columns: 80px 1fr 150px 180px 150px 180px;
|
||||
gap: 15px;
|
||||
padding: 20px 25px;
|
||||
background: #f8f9fa;
|
||||
@@ -1416,7 +1483,7 @@
|
||||
|
||||
.hssm-slide-row {
|
||||
display: grid;
|
||||
grid-template-columns: 80px 1fr 120px 180px 150px 140px;
|
||||
grid-template-columns: 80px 1fr 150px 180px 150px 180px;
|
||||
gap: 15px;
|
||||
padding: 20px 25px;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
@@ -1428,6 +1495,11 @@
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.hssm-slide-row-focus {
|
||||
background-color: #e8f5e9;
|
||||
outline: 2px solid #46b450;
|
||||
}
|
||||
|
||||
.hssm-slide-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -1532,6 +1604,22 @@
|
||||
border-color: #1e7e34;
|
||||
}
|
||||
|
||||
.hssm-slide-actions .hssm-approved-slide,
|
||||
.hssm-slide-actions .hssm-approved-slide:disabled,
|
||||
.hssm-slide-actions .hssm-approved-slide:hover {
|
||||
background: #275191;
|
||||
background-color: #275191;
|
||||
border-color: #275191;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hssm-slide-actions .hssm-approved-slide:disabled {
|
||||
opacity: 1;
|
||||
cursor: default;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.hssm-empty-state {
|
||||
text-align: center;
|
||||
@@ -1873,3 +1961,68 @@
|
||||
.hssm-datepicker-start.hssm-datepicker-end a {
|
||||
background: linear-gradient(135deg, #d4edda 50%, #f8d7da 50%) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme CSS targets input[type="text"] (higher specificity than .hssm-input)
|
||||
* and paints those fields black/pill-shaped. Keep plugin forms white.
|
||||
*/
|
||||
.hssm-dashboard-main input[type="text"],
|
||||
.hssm-dashboard-main input[type="url"],
|
||||
.hssm-dashboard-main input[type="email"],
|
||||
.hssm-dashboard-main input[type="search"],
|
||||
.hssm-dashboard-main input[type="number"],
|
||||
.hssm-dashboard-main input[type="date"],
|
||||
.hssm-dashboard-main input[type="tel"],
|
||||
.hssm-dashboard-main textarea,
|
||||
.hssm-dashboard-main select,
|
||||
.hssm-dashboard-main .hssm-input,
|
||||
.hssm-dashboard-main .hssm-textarea,
|
||||
.hssm-dashboard-main .hssm-select,
|
||||
input.hssm-input,
|
||||
input.hssm-input-small,
|
||||
textarea.hssm-textarea,
|
||||
select.hssm-select {
|
||||
background-color: #ffffff !important;
|
||||
color: #1d2327 !important;
|
||||
border: 1px solid #ced4da !important;
|
||||
border-radius: 4px !important;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
.hssm-dashboard-main input[type="text"]:focus,
|
||||
.hssm-dashboard-main input[type="url"]:focus,
|
||||
.hssm-dashboard-main input[type="email"]:focus,
|
||||
.hssm-dashboard-main input[type="search"]:focus,
|
||||
.hssm-dashboard-main input[type="number"]:focus,
|
||||
.hssm-dashboard-main input[type="date"]:focus,
|
||||
.hssm-dashboard-main input[type="tel"]:focus,
|
||||
.hssm-dashboard-main textarea:focus,
|
||||
.hssm-dashboard-main select:focus,
|
||||
.hssm-dashboard-main .hssm-input:focus,
|
||||
.hssm-dashboard-main .hssm-textarea:focus,
|
||||
.hssm-dashboard-main .hssm-select:focus,
|
||||
input.hssm-input:focus,
|
||||
input.hssm-input-small:focus,
|
||||
textarea.hssm-textarea:focus,
|
||||
select.hssm-select:focus {
|
||||
background-color: #ffffff !important;
|
||||
color: #1d2327 !important;
|
||||
border-color: #007cba !important;
|
||||
}
|
||||
|
||||
.hssm-dashboard-main input::placeholder,
|
||||
input.hssm-input::placeholder,
|
||||
input.hssm-input-small::placeholder,
|
||||
textarea.hssm-textarea::placeholder {
|
||||
color: #6c757d;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hssm-dashboard-main input:-webkit-autofill,
|
||||
.hssm-dashboard-main input:-webkit-autofill:hover,
|
||||
.hssm-dashboard-main input:-webkit-autofill:focus,
|
||||
input.hssm-input:-webkit-autofill,
|
||||
input.hssm-input-small:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
|
||||
-webkit-text-fill-color: #1d2327 !important;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Hi-School Slide Manager - Frontend Styles
|
||||
*/
|
||||
|
||||
.hssm-slides-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.hssm-slide {
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.hssm-slide:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.hssm-slide h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #333;
|
||||
font-size: 1.4em;
|
||||
border-bottom: 2px solid #0073aa;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.hssm-slide-content {
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.6;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.hssm-slide-image {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hssm-slide-image img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.hssm-slides-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hssm-slide {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
+173
-116
@@ -23,20 +23,24 @@
|
||||
hasMore: false,
|
||||
filters: {
|
||||
status: '',
|
||||
site: '',
|
||||
client: ''
|
||||
site: ''
|
||||
},
|
||||
|
||||
// Edit mode state
|
||||
isEditMode: false,
|
||||
editingSlideId: null,
|
||||
currentSlides: null,
|
||||
focusSlideId: null,
|
||||
|
||||
// Initialize the dashboard
|
||||
init: function() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
this.focusSlideId = params.get('hssm_focus') || null;
|
||||
|
||||
this.bindEvents();
|
||||
this.initializeTabs();
|
||||
this.addInitialSlideForm();
|
||||
this.populateFilterDropdowns();
|
||||
this.loadExistingSlides();
|
||||
// Initialize site data
|
||||
window.hssmSites = window.hssmSites || [];
|
||||
@@ -64,8 +68,6 @@
|
||||
$(document).on('click', '.hssm-remove-button', this.removeButtonRow.bind(this));
|
||||
|
||||
// Slide actions
|
||||
$(document).on('click', '.hssm-save-slide', this.saveSlide.bind(this));
|
||||
$(document).on('click', '#save-all-slides', this.saveAllSlides.bind(this));
|
||||
$(document).on('click', '.hssm-preview-slide', this.previewSlide.bind(this));
|
||||
$(document).on('click', '.hssm-schedule-slide', this.scheduleSlide.bind(this));
|
||||
$(document).on('click', '.hssm-publish-slide', this.publishSlide.bind(this));
|
||||
@@ -89,6 +91,8 @@
|
||||
|
||||
// Filtering and management (slide row edit/delete bound in bindSlideActions after list load)
|
||||
$(document).on('click', '#apply-filters', this.applyFilters.bind(this));
|
||||
$(document).on('click', '#clear-filters', this.clearFilters.bind(this));
|
||||
$(document).on('change', '#filter-site, #filter-status', this.applyFilters.bind(this));
|
||||
|
||||
// Edit form actions — register once; do not re-bind in populateEditForm (prevents duplicate toasts)
|
||||
$(document).on('click.hssmDashboard', '.hssm-cancel-edit, .hssm-cancel-edit-btn', (e) => {
|
||||
@@ -104,7 +108,6 @@
|
||||
// Additional functionality
|
||||
$(document).on('click', '#import-slides', this.importSlides.bind(this));
|
||||
$(document).on('click', '#export-slides', this.exportSlides.bind(this));
|
||||
$(document).on('click', '#save-draft-all', this.saveAllAsDrafts.bind(this));
|
||||
|
||||
// Notification Settings
|
||||
$(document).on('submit', '#notification-settings-form', this.saveNotificationSettings.bind(this));
|
||||
@@ -112,6 +115,11 @@
|
||||
|
||||
// Initialize tab functionality
|
||||
initializeTabs: function() {
|
||||
// Confirmation / email links should land on the manage list.
|
||||
if (this.focusSlideId) {
|
||||
sessionStorage.setItem('hssm_active_tab', 'manage-slides');
|
||||
}
|
||||
|
||||
// Check for saved tab in sessionStorage
|
||||
const savedTab = sessionStorage.getItem('hssm_active_tab');
|
||||
|
||||
@@ -425,84 +433,6 @@
|
||||
});
|
||||
},
|
||||
|
||||
// Save individual slide
|
||||
saveSlide: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formId = $(e.currentTarget).data('form');
|
||||
const $form = $(`.hssm-slide-form[data-form-id="${formId}"]`);
|
||||
|
||||
this.submitSlideForm($form);
|
||||
},
|
||||
|
||||
// Save all slides
|
||||
saveAllSlides: function(e) {
|
||||
if (e) e.preventDefault();
|
||||
|
||||
const $forms = $('.hssm-slide-form');
|
||||
let savedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
$forms.each((index, form) => {
|
||||
this.submitSlideForm($(form), () => {
|
||||
savedCount++;
|
||||
this.checkAllSaved(savedCount, errorCount, $forms.length);
|
||||
}, () => {
|
||||
errorCount++;
|
||||
this.checkAllSaved(savedCount, errorCount, $forms.length);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Check if all forms have been processed
|
||||
checkAllSaved: function(savedCount, errorCount, totalCount) {
|
||||
if (savedCount + errorCount === totalCount) {
|
||||
if (errorCount === 0) {
|
||||
this.showMessage(`All ${savedCount} slides saved successfully!`, 'success');
|
||||
} else {
|
||||
this.showMessage(`${savedCount} slides saved, ${errorCount} failed.`, 'warning');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Submit individual slide form
|
||||
submitSlideForm: function($form, successCallback, errorCallback) {
|
||||
if (!this.validateForm($form)) {
|
||||
if (errorCallback) errorCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = this.collectFormData($form);
|
||||
|
||||
$form.addClass('hssm-loading');
|
||||
|
||||
formData.append('nonce', hssmDashboard.nonce);
|
||||
$.ajax({
|
||||
url: hssmDashboard.ajaxurl,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: (response) => {
|
||||
$form.removeClass('hssm-loading');
|
||||
|
||||
if (response.success) {
|
||||
this.showMessage(response.data.message, 'success');
|
||||
this.clearForm($form);
|
||||
if (successCallback) successCallback();
|
||||
} else {
|
||||
this.showMessage(response.data.message || 'Save failed', 'error');
|
||||
if (errorCallback) errorCallback();
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
$form.removeClass('hssm-loading');
|
||||
this.showMessage('Save failed. Please try again.', 'error');
|
||||
if (errorCallback) errorCallback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Clear all field errors in a form
|
||||
clearFieldErrors: function($form) {
|
||||
$form.find('.hssm-field-error').remove();
|
||||
@@ -771,7 +701,11 @@
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'hssm_get_slides',
|
||||
nonce: hssmDashboard.nonce
|
||||
nonce: hssmDashboard.nonce,
|
||||
page: this.currentPage,
|
||||
per_page: this.perPage,
|
||||
status_filter: this.filters.status || '',
|
||||
site_filter: this.filters.site || ''
|
||||
},
|
||||
beforeSend: function(xhr, settings) {
|
||||
// Optional: Add loading indicators
|
||||
@@ -779,6 +713,7 @@
|
||||
success: (response) => {
|
||||
if (response.success && response.data && response.data.slides) {
|
||||
this.renderSlidesList(response.data.slides);
|
||||
this.highlightFocusedSlide();
|
||||
} else {
|
||||
console.error('Failed to get slides:', response);
|
||||
$slidesList.html('<p>Failed to load slides. Response format unexpected.</p>');
|
||||
@@ -807,7 +742,7 @@
|
||||
<span class="dashicons dashicons-slides"></span>
|
||||
</div>
|
||||
<h3>No slides found</h3>
|
||||
<p>Create your first slide to get started!</p>
|
||||
<p>${this.hasActiveFilters() ? 'No slides match the current filters.' : 'Create your first slide to get started!'}</p>
|
||||
<button class="hssm-btn hssm-btn-primary" onclick="$('.hssm-nav-tabs a[data-tab=create-slides]').click()">
|
||||
<span class="dashicons dashicons-plus-alt"></span> Create First Slide
|
||||
</button>
|
||||
@@ -833,8 +768,11 @@
|
||||
slides.forEach(slide => {
|
||||
const imageUrl = slide.featured_image_url || '';
|
||||
const statusClass = this.getStatusClass(slide.status);
|
||||
const statusLabel = this.getStatusLabel(slide);
|
||||
const sitesText = this.formatSitesList(slide.sites || slide.target_sites);
|
||||
const scheduleText = this.formatScheduleText(slide);
|
||||
const isPending = slide.status === 'pending';
|
||||
const isApproved = slide.status === 'publish' || slide.status === 'future';
|
||||
|
||||
html += `
|
||||
<div class="hssm-slide-row" data-slide-id="${slide.id}">
|
||||
@@ -848,7 +786,7 @@
|
||||
<div class="hssm-slide-subtitle">${slide.subtitle || ''}</div>
|
||||
</div>
|
||||
<div class="hssm-col-status">
|
||||
<span class="hssm-status-badge hssm-status-${statusClass}">${slide.status}</span>
|
||||
<span class="hssm-status-badge hssm-status-${statusClass}">${statusLabel}</span>
|
||||
</div>
|
||||
<div class="hssm-col-dates">
|
||||
<div class="hssm-schedule-info">${scheduleText}</div>
|
||||
@@ -858,15 +796,28 @@
|
||||
</div>
|
||||
<div class="hssm-col-actions">
|
||||
<div class="hssm-slide-actions">
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-preview-slide" data-slide-id="${slide.id}" title="Preview banner">
|
||||
<span class="dashicons dashicons-visibility"></span>
|
||||
</button>
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-edit-slide" data-slide-id="${slide.id}" title="Edit">
|
||||
<span class="dashicons dashicons-edit"></span>
|
||||
</button>
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-duplicate-slide" data-slide-id="${slide.id}" title="Duplicate">
|
||||
<span class="dashicons dashicons-admin-page"></span>
|
||||
</button>
|
||||
${isPending ? `
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-success hssm-approve-slide" data-slide-id="${slide.id}" data-approve-action="schedule" title="Approve">
|
||||
<span class="dashicons dashicons-yes"></span>
|
||||
</button>
|
||||
` : isApproved ? `
|
||||
<button type="button" class="hssm-btn hssm-btn-sm hssm-btn-primary hssm-approved-slide" title="Approved" disabled>
|
||||
<span class="dashicons dashicons-yes"></span>
|
||||
</button>
|
||||
` : `
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-success hssm-toggle-status" data-slide-id="${slide.id}" title="Toggle Status">
|
||||
<span class="dashicons dashicons-${slide.status === 'publish' ? 'pause' : 'controls-play'}"></span>
|
||||
</button>
|
||||
`}
|
||||
<button class="hssm-btn hssm-btn-sm hssm-btn-danger hssm-delete-slide" data-slide-id="${slide.id}" title="Delete">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
@@ -885,17 +836,49 @@
|
||||
this.bindSlideActions();
|
||||
},
|
||||
|
||||
highlightFocusedSlide: function() {
|
||||
if (!this.focusSlideId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $row = $('.hssm-slide-row[data-slide-id="' + this.focusSlideId + '"]');
|
||||
if (!$row.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$row.addClass('hssm-slide-row-focus');
|
||||
if ($row[0] && $row[0].scrollIntoView) {
|
||||
$row[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
},
|
||||
|
||||
// Helper function to get status CSS class
|
||||
getStatusClass: function(status) {
|
||||
const statusMap = {
|
||||
'publish': 'published',
|
||||
'draft': 'draft',
|
||||
'future': 'scheduled',
|
||||
'pending': 'pending',
|
||||
'private': 'private'
|
||||
};
|
||||
return statusMap[status] || 'unknown';
|
||||
},
|
||||
|
||||
getStatusLabel: function(slide) {
|
||||
if (slide.status_display) {
|
||||
return slide.status_display;
|
||||
}
|
||||
|
||||
const labels = {
|
||||
publish: 'Published',
|
||||
draft: 'Draft',
|
||||
future: 'Scheduled',
|
||||
pending: 'Pending Approval',
|
||||
private: 'Private'
|
||||
};
|
||||
return labels[slide.status] || slide.status;
|
||||
},
|
||||
|
||||
// Helper function to format sites list
|
||||
formatSitesList: function(sites) {
|
||||
if (!sites || sites.length === 0) return 'No sites';
|
||||
@@ -943,6 +926,12 @@
|
||||
const slideId = $(e.currentTarget).data('slide-id');
|
||||
this.toggleSlideStatus(slideId);
|
||||
});
|
||||
|
||||
$(document).off('click.hssmSlideActions', '.hssm-approve-slide').on('click.hssmSlideActions', '.hssm-approve-slide', (e) => {
|
||||
e.preventDefault();
|
||||
const $btn = $(e.currentTarget);
|
||||
this.approveSlide($btn.data('slide-id'), $btn.data('approve-action') || 'schedule');
|
||||
});
|
||||
},
|
||||
|
||||
// Edit slide function
|
||||
@@ -1097,6 +1086,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="hssm-form-actions">
|
||||
<button type="button" class="hssm-btn hssm-btn-outline hssm-preview-slide" data-form="${this.formCounter}" data-slide-id="${slideData.id}">
|
||||
<span class="dashicons dashicons-visibility"></span> Preview Banner
|
||||
</button>
|
||||
<button type="button" class="hssm-btn hssm-btn-secondary hssm-schedule-updated-slide" data-form="${this.formCounter}" data-slide-id="${slideData.id}">
|
||||
<span class="dashicons dashicons-calendar-alt"></span> Schedule for Approval
|
||||
</button>
|
||||
@@ -1302,6 +1294,38 @@
|
||||
});
|
||||
},
|
||||
|
||||
approveSlide: function(slideId, approveAction) {
|
||||
const confirmText = (hssmDashboard.strings && hssmDashboard.strings.approve_confirm)
|
||||
? hssmDashboard.strings.approve_confirm
|
||||
: 'Approve this slide?';
|
||||
|
||||
if (!confirm(confirmText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: hssmDashboard.ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'hssm_approve_slide',
|
||||
nonce: hssmDashboard.nonce,
|
||||
slide_id: slideId,
|
||||
approve_action: approveAction || 'schedule'
|
||||
},
|
||||
success: (response) => {
|
||||
if (response.success) {
|
||||
this.showMessage((hssmDashboard.strings && hssmDashboard.strings.approve_success) || 'Slide approved.', 'success');
|
||||
this.loadExistingSlides();
|
||||
} else {
|
||||
this.showMessage(response.data?.message || 'Failed to approve slide', 'error');
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
this.showMessage('Failed to approve slide. Please try again.', 'error');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Toggle slide status
|
||||
toggleSlideStatus: function(slideId) {
|
||||
$.ajax({
|
||||
@@ -1344,15 +1368,73 @@
|
||||
}, dismissMs);
|
||||
},
|
||||
|
||||
// Placeholder methods for future implementation
|
||||
previewSlide: function(e) {
|
||||
e.preventDefault();
|
||||
this.showMessage('Preview functionality coming soon!', 'info');
|
||||
populateFilterDropdowns: function() {
|
||||
const $site = $('#filter-site');
|
||||
|
||||
if ($site.length && window.hssmSites && $site.children().length <= 1) {
|
||||
window.hssmSites.forEach((site) => {
|
||||
$site.append(`<option value="${site.id}">${site.name}</option>`);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
hasActiveFilters: function() {
|
||||
return !!(this.filters.status || this.filters.site);
|
||||
},
|
||||
|
||||
readFiltersFromForm: function() {
|
||||
this.filters.status = $('#filter-status').val() || '';
|
||||
this.filters.site = $('#filter-site').val() || '';
|
||||
},
|
||||
|
||||
applyFilters: function(e) {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
this.showMessage('Filter functionality coming soon!', 'info');
|
||||
}
|
||||
this.readFiltersFromForm();
|
||||
this.currentPage = 1;
|
||||
this.loadExistingSlides();
|
||||
},
|
||||
|
||||
clearFilters: function(e) {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.filters = { status: '', site: '' };
|
||||
$('#filter-status, #filter-site').val('');
|
||||
this.currentPage = 1;
|
||||
this.loadExistingSlides();
|
||||
},
|
||||
|
||||
buildPreviewUrl: function(slide) {
|
||||
const base = (hssmDashboard.previewUrl || '/slide-preview/').replace(/\/?$/, '/');
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (slide && slide.id) {
|
||||
params.set('hssm_preview', slide.id);
|
||||
}
|
||||
if (slide && slide.start_date) {
|
||||
params.set('hssm_date', slide.start_date);
|
||||
}
|
||||
if (slide && slide.site_slugs && slide.site_slugs.length) {
|
||||
params.set('hssm_site', slide.site_slugs[0]);
|
||||
}
|
||||
|
||||
const query = params.toString();
|
||||
return query ? `${base}?${query}` : base;
|
||||
},
|
||||
|
||||
previewSlide: function(e) {
|
||||
e.preventDefault();
|
||||
const slideId = $(e.currentTarget).data('slide-id') || this.editingSlideId;
|
||||
|
||||
if (!slideId) {
|
||||
this.showMessage((hssmDashboard.strings && hssmDashboard.strings.preview_save_first) || 'Save the slide first, then preview it as a banner.', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const slide = this.findSlideById(slideId) || { id: slideId };
|
||||
window.open(this.buildPreviewUrl(slide), '_blank', 'noopener');
|
||||
},
|
||||
|
||||
// Load analytics data
|
||||
@@ -1386,31 +1468,6 @@
|
||||
this.showMessage('Slide export functionality coming soon!', 'info');
|
||||
},
|
||||
|
||||
// Save all slides as drafts
|
||||
saveAllAsDrafts: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $forms = $('.hssm-slide-form');
|
||||
let savedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
$forms.each((index, form) => {
|
||||
const $form = $(form);
|
||||
|
||||
// Override status to draft
|
||||
const formData = this.collectFormData($form);
|
||||
formData.append('post_status', 'draft');
|
||||
|
||||
this.submitSlideForm($form, () => {
|
||||
savedCount++;
|
||||
this.checkAllSaved(savedCount, errorCount, $forms.length);
|
||||
}, () => {
|
||||
errorCount++;
|
||||
this.checkAllSaved(savedCount, errorCount, $forms.length);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Load notification settings
|
||||
loadNotificationSettings: function() {
|
||||
$.ajax({
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Hi-School Slide Manager - Frontend JavaScript
|
||||
*/
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
|
||||
// Initialize slide functionality
|
||||
initializeSlides();
|
||||
|
||||
/**
|
||||
* Initialize slide functionality
|
||||
*/
|
||||
function initializeSlides() {
|
||||
$('.hssm-slides-container').each(function() {
|
||||
var $container = $(this);
|
||||
var $slides = $container.find('.hssm-slide');
|
||||
|
||||
// Add fade-in animation
|
||||
$slides.each(function(index) {
|
||||
var $slide = $(this);
|
||||
setTimeout(function() {
|
||||
$slide.addClass('hssm-fade-in');
|
||||
}, index * 100);
|
||||
});
|
||||
|
||||
// Add click handlers for interactive slides
|
||||
$slides.on('click', function() {
|
||||
var $slide = $(this);
|
||||
$slide.toggleClass('hssm-slide-expanded');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle slide interactions
|
||||
*/
|
||||
$(document).on('click', '.hssm-slide-toggle', function(e) {
|
||||
e.preventDefault();
|
||||
var $button = $(this);
|
||||
var $slide = $button.closest('.hssm-slide');
|
||||
var $content = $slide.find('.hssm-slide-content');
|
||||
|
||||
$content.slideToggle(300);
|
||||
$button.text($content.is(':visible') ? 'Hide' : 'Show');
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle keyboard navigation
|
||||
*/
|
||||
$(document).on('keydown', '.hssm-slide', function(e) {
|
||||
if (e.which === 13 || e.which === 32) { // Enter or Space
|
||||
e.preventDefault();
|
||||
$(this).click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add CSS classes via JavaScript for animations
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.hssm-slide {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||
}
|
||||
|
||||
.hssm-slide.hssm-fade-in {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.hssm-slide.hssm-slide-expanded {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
@@ -110,7 +110,7 @@
|
||||
action: 'hssm_approve_slide',
|
||||
nonce: hssmPreview.nonce,
|
||||
slide_id: slideId,
|
||||
approve_action: 'schedule' // Smart approve: schedules if future date exists, otherwise publishes
|
||||
approve_action: $btn.data('approve-action') || 'schedule'
|
||||
},
|
||||
success: (response) => {
|
||||
if (response.success) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Plugin Name: Hi-School Slide Manager
|
||||
* Plugin URI: https://hi-school.com/slide-manager
|
||||
* Description: Multi-site slide management system for Hi-School websites. Allows staff to create and schedule slides on the main site, display them as headers on companion sites via REST API, with advanced preview and editing capabilities.
|
||||
* Version: 1.1.2
|
||||
* Version: 1.1.9
|
||||
* Author: Hi-School Development Team
|
||||
* Author URI: https://hi-school.com
|
||||
* License: GPL v2 or later
|
||||
@@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
// Plugin constants
|
||||
define( 'HSSM_VERSION', '1.1.2' );
|
||||
define( 'HSSM_VERSION', '1.1.9' );
|
||||
define( 'HSSM_PLUGIN_FILE', __FILE__ );
|
||||
define( 'HSSM_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'HSSM_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
@@ -50,6 +50,79 @@ function hssm_debug_log( $message ) {
|
||||
error_log( 'HSSM: ' . $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Permalink for a published page using a plugin template.
|
||||
*
|
||||
* Prefers a matching slug, then any page with the template, including children.
|
||||
* get_pages() with hierarchical=1 can skip child pages, so this uses WP_Query.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param string $template Page template filename.
|
||||
* @param string $fallback_path Optional path used when no page is found.
|
||||
* @return string
|
||||
*/
|
||||
function hssm_get_page_url_by_template( $template, $fallback_path = '' ) {
|
||||
$preferred_slug = '';
|
||||
if ( 'page-slide-preview.php' === $template ) {
|
||||
$preferred_slug = 'slide-preview';
|
||||
} elseif ( 'page-slide-dashboard.php' === $template ) {
|
||||
$preferred_slug = 'slide-dashboard';
|
||||
} elseif ( 'page-slide-confirmation.php' === $template ) {
|
||||
$preferred_slug = 'slide-confirmation';
|
||||
}
|
||||
|
||||
$query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => array( 'publish', 'private' ),
|
||||
'posts_per_page' => 20,
|
||||
'no_found_rows' => true,
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => $template,
|
||||
'orderby' => array(
|
||||
'menu_order' => 'ASC',
|
||||
'ID' => 'ASC',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$match = null;
|
||||
if ( $query->have_posts() ) {
|
||||
foreach ( $query->posts as $page ) {
|
||||
if ( $preferred_slug && $preferred_slug === $page->post_name ) {
|
||||
$match = $page;
|
||||
break;
|
||||
}
|
||||
if ( ! $match ) {
|
||||
$match = $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
if ( $match ) {
|
||||
return get_permalink( $match->ID );
|
||||
}
|
||||
|
||||
$path_candidates = array();
|
||||
if ( $preferred_slug ) {
|
||||
$path_candidates[] = 'slide-dashboard/' . $preferred_slug;
|
||||
$path_candidates[] = $preferred_slug;
|
||||
}
|
||||
if ( $fallback_path ) {
|
||||
$path_candidates[] = trim( $fallback_path, '/' );
|
||||
}
|
||||
|
||||
foreach ( array_unique( $path_candidates ) as $path ) {
|
||||
$page = get_page_by_path( $path );
|
||||
if ( $page ) {
|
||||
return get_permalink( $page->ID );
|
||||
}
|
||||
}
|
||||
|
||||
return $fallback_path ? home_url( $fallback_path ) : '';
|
||||
}
|
||||
|
||||
// Include core files
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-autoloader.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-main.php';
|
||||
|
||||
@@ -90,7 +90,6 @@ class HSSM_Admin {
|
||||
register_setting( 'hssm_settings', 'hssm_plugin_mode' );
|
||||
register_setting( 'hssm_settings', 'hssm_api_url' );
|
||||
register_setting( 'hssm_settings', 'hssm_client_site_slug' );
|
||||
register_setting( 'hssm_settings', 'hssm_notification_emails' );
|
||||
register_setting( 'hssm_settings', 'hssm_cache_duration' );
|
||||
register_setting( 'hssm_settings', 'hssm_api_keys' );
|
||||
register_setting( 'hssm_settings', 'hssm_client_api_key' );
|
||||
@@ -158,14 +157,6 @@ class HSSM_Admin {
|
||||
'hssm_general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_notification_emails',
|
||||
__( 'Notification Recipients', 'hi-school-slide-manager' ),
|
||||
array( $this, 'notification_emails_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_notifications'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_api_keys',
|
||||
__( 'API Keys', 'hi-school-slide-manager' ),
|
||||
@@ -312,10 +303,6 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
"name": "Hi-School Pharmacy"
|
||||
}
|
||||
],
|
||||
"client": {
|
||||
"slug": "client-name",
|
||||
"name": "Client Name"
|
||||
},
|
||||
"status": "publish",
|
||||
"created": "2025-01-01 12:00:00",
|
||||
"modified": "2025-01-01 12:00:00"
|
||||
@@ -415,12 +402,6 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
if ( isset( $_POST['hssm_cache_duration'] ) ) {
|
||||
update_option( 'hssm_cache_duration', absint( $_POST['hssm_cache_duration'] ) );
|
||||
}
|
||||
if ( isset( $_POST['hssm_notification_emails'] ) ) {
|
||||
$emails = explode( "\n", str_replace( "\r", "", $_POST['hssm_notification_emails'] ) );
|
||||
$emails = array_map( 'sanitize_email', $emails );
|
||||
$emails = array_filter( $emails );
|
||||
update_option( 'hssm_notification_emails', $emails );
|
||||
}
|
||||
add_settings_error( 'hssm_settings', 'settings_saved', __( 'Settings saved successfully.', 'hi-school-slide-manager' ), 'success' );
|
||||
}
|
||||
|
||||
@@ -456,7 +437,11 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
* Notifications section callback
|
||||
*/
|
||||
public function notifications_section_callback() {
|
||||
echo '<p>' . __( 'Configure email notifications for new slides.', 'hi-school-slide-manager' ) . '</p>';
|
||||
$dashboard_url = hssm_get_page_url_by_template( 'page-slide-dashboard.php', '/slide-dashboard/' );
|
||||
echo '<p>' . esc_html__( 'Email recipients are managed on the Slide Dashboard Notifications tab.', 'hi-school-slide-manager' ) . '</p>';
|
||||
if ( $dashboard_url ) {
|
||||
echo '<p><a href="' . esc_url( $dashboard_url ) . '">' . esc_html__( 'Open Slide Dashboard', 'hi-school-slide-manager' ) . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,15 +511,6 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
echo '<span class="description">' . __( 'Recommended cache duration for API responses (seconds)', 'hi-school-slide-manager' ) . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification emails field
|
||||
*/
|
||||
public function notification_emails_field() {
|
||||
$emails = get_option( 'hssm_notification_emails', array() );
|
||||
echo '<textarea name="hssm_notification_emails" rows="5" class="regular-text">' . esc_textarea( implode( "\n", $emails ) ) . '</textarea><br>';
|
||||
echo '<span class="description">' . __( 'Enter email addresses (one per line) to receive notifications when new slides are published.', 'hi-school-slide-manager' ) . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* API keys field
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,7 @@ class HSSM_API_Client {
|
||||
|
||||
$args = array(
|
||||
'timeout' => 15,
|
||||
'sslverify' => $this->should_verify_ssl( $full_url ),
|
||||
'headers' => array(
|
||||
'X-HSSM-API-Key' => $api_key,
|
||||
'Accept' => 'application/json',
|
||||
@@ -82,6 +83,27 @@ class HSSM_API_Client {
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
/**
|
||||
* Verify SSL except on local Local/WP Engine hosts with self-signed certs.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param string $url Request URL.
|
||||
* @return bool
|
||||
*/
|
||||
private function should_verify_ssl( $url ) {
|
||||
$host = wp_parse_url( $url, PHP_URL_HOST );
|
||||
$local_hosts = array( 'localhost', '127.0.0.1' );
|
||||
$is_local = in_array( $host, $local_hosts, true ) || ( is_string( $host ) && preg_match( '/\.local$/', $host ) );
|
||||
|
||||
/**
|
||||
* Filter whether the client API should verify SSL certificates.
|
||||
*
|
||||
* @param bool $verify Whether to verify SSL. False for *.local hosts.
|
||||
* @param string $url Request URL.
|
||||
*/
|
||||
return (bool) apply_filters( 'hssm_api_sslverify', ! $is_local, $url );
|
||||
}
|
||||
|
||||
public function clear_cache() {
|
||||
$site_slug = get_option( 'hssm_client_site_slug' );
|
||||
if ( ! empty( $site_slug ) ) {
|
||||
|
||||
+181
-245
@@ -38,13 +38,12 @@ class HSSM_Frontend {
|
||||
// Register page template
|
||||
add_filter( 'theme_page_templates', array( $this, 'register_page_template' ) );
|
||||
add_filter( 'page_template', array( $this, 'load_page_template' ) );
|
||||
add_filter( 'request', array( $this, 'keep_preview_page_query' ) );
|
||||
|
||||
// Handle page routing
|
||||
add_action( 'template_redirect', array( $this, 'handle_page_routing' ) );
|
||||
|
||||
// AJAX handlers
|
||||
add_action( 'wp_ajax_hssm_save_slide', array( $this, 'ajax_save_slide' ) );
|
||||
add_action( 'wp_ajax_hssm_upload_image', array( $this, 'ajax_upload_image' ) );
|
||||
add_action( 'wp_ajax_hssm_get_slides', array( $this, 'ajax_get_slides' ) );
|
||||
add_action( 'wp_ajax_hssm_get_slide_for_edit', array( $this, 'ajax_get_slide_for_edit' ) );
|
||||
add_action( 'wp_ajax_hssm_delete_slide', array( $this, 'ajax_delete_slide' ) );
|
||||
@@ -54,7 +53,6 @@ class HSSM_Frontend {
|
||||
add_action( 'wp_ajax_hssm_duplicate_slide', array( $this, 'ajax_duplicate_slide' ) );
|
||||
add_action( 'wp_ajax_hssm_toggle_slide_status', array( $this, 'ajax_toggle_slide_status' ) );
|
||||
add_action( 'wp_ajax_hssm_approve_slide', array( $this, 'ajax_approve_slide' ) );
|
||||
add_action( 'wp_ajax_hssm_preview_slides', array( $this, 'ajax_preview_slides' ) );
|
||||
|
||||
// Notification Settings AJAX
|
||||
add_action( 'wp_ajax_hssm_get_notification_settings', array( $this, 'ajax_get_notification_settings' ) );
|
||||
@@ -63,9 +61,12 @@ class HSSM_Frontend {
|
||||
|
||||
// Frontend assets
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
||||
add_filter( 'body_class', array( $this, 'add_tool_page_body_class' ) );
|
||||
|
||||
// Filter slides query on preview page
|
||||
add_action( 'pre_get_posts', array( $this, 'filter_preview_slides_query' ) );
|
||||
|
||||
add_action( 'admin_init', array( $this, 'maybe_nest_plugin_pages' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,6 +116,30 @@ class HSSM_Frontend {
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent ?hssm_slide= from hijacking the preview page.
|
||||
*
|
||||
* The slide CPT used query_var "hssm_slide", so that GET param turned
|
||||
* the page request into a missing slide query and WordPress 404'd.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param array $query_vars Parsed request vars.
|
||||
* @return array
|
||||
*/
|
||||
public function keep_preview_page_query( $query_vars ) {
|
||||
if ( empty( $query_vars['pagename'] ) ) {
|
||||
return $query_vars;
|
||||
}
|
||||
|
||||
unset( $query_vars['hssm_slide'], $query_vars['hssm_slide_item'] );
|
||||
|
||||
if ( isset( $query_vars['name'] ) && isset( $query_vars['post_type'] ) && 'hssm_slide' === $query_vars['post_type'] ) {
|
||||
unset( $query_vars['name'], $query_vars['post_type'] );
|
||||
}
|
||||
|
||||
return $query_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle frontend page routing
|
||||
*
|
||||
@@ -123,14 +148,38 @@ class HSSM_Frontend {
|
||||
public function handle_page_routing() {
|
||||
global $post;
|
||||
|
||||
// Check if this is our slide dashboard page
|
||||
if ( is_page() && get_page_template_slug( $post->ID ) === 'page-slide-dashboard.php' ) {
|
||||
// Check authentication with Members plugin
|
||||
if ( ! $this->check_dashboard_access() ) {
|
||||
$this->redirect_to_login();
|
||||
if ( ! is_page() || ! $post ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$template = get_page_template_slug( $post->ID );
|
||||
if ( ! in_array( $template, array( 'page-slide-dashboard.php', 'page-slide-preview.php' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->check_dashboard_access() ) {
|
||||
$this->redirect_to_login();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark dashboard and preview as plugin tool pages for CSS.
|
||||
*
|
||||
* @since 1.1.5
|
||||
* @param array $classes Body classes.
|
||||
* @return array
|
||||
*/
|
||||
public function add_tool_page_body_class( $classes ) {
|
||||
if ( ! is_page() ) {
|
||||
return $classes;
|
||||
}
|
||||
|
||||
$template = get_page_template_slug();
|
||||
if ( in_array( $template, array( 'page-slide-dashboard.php', 'page-slide-preview.php' ), true ) ) {
|
||||
$classes[] = 'hssm-tool-page';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,8 +231,8 @@ class HSSM_Frontend {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function redirect_to_login() {
|
||||
$login_url = wp_login_url( get_permalink() );
|
||||
wp_redirect( $login_url );
|
||||
$redirect = home_url( add_query_arg( array() ) );
|
||||
wp_safe_redirect( wp_login_url( $redirect ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -208,11 +257,12 @@ class HSSM_Frontend {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check access (preview page may be public, but dashboard requires auth)
|
||||
if ( $is_dashboard && ! $this->check_dashboard_access() ) {
|
||||
if ( ! $this->check_dashboard_access() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style( 'dashicons' );
|
||||
|
||||
// Enqueue styles (both pages need dashboard CSS)
|
||||
wp_enqueue_style(
|
||||
'hssm-dashboard',
|
||||
@@ -239,6 +289,7 @@ class HSSM_Frontend {
|
||||
wp_localize_script( 'hssm-dashboard', 'hssmDashboard', array(
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'hssm_dashboard_nonce' ),
|
||||
'previewUrl' => hssm_get_page_url_by_template( 'page-slide-preview.php', '/slide-preview/' ),
|
||||
'strings' => array(
|
||||
'save_success' => __( 'Slide saved successfully!', 'hi-school-slide-manager' ),
|
||||
'save_error' => __( 'Error saving slide. Please try again.', 'hi-school-slide-manager' ),
|
||||
@@ -246,6 +297,9 @@ class HSSM_Frontend {
|
||||
'delete_success' => __( 'Slide deleted successfully!', 'hi-school-slide-manager' ),
|
||||
'required_fields' => __( 'Please fill in all required fields.', 'hi-school-slide-manager' ),
|
||||
'max_forms' => __( 'Maximum 5 slide forms allowed at once.', 'hi-school-slide-manager' ),
|
||||
'preview_save_first' => __( 'Save the slide first, then preview it as a banner.', 'hi-school-slide-manager' ),
|
||||
'approve_confirm' => __( 'Approve this slide?', 'hi-school-slide-manager' ),
|
||||
'approve_success' => __( 'Slide approved.', 'hi-school-slide-manager' ),
|
||||
),
|
||||
) );
|
||||
|
||||
@@ -286,188 +340,6 @@ class HSSM_Frontend {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for image uploads
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function ajax_upload_image() {
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'upload_files' ) ) {
|
||||
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// This is handled by media_handle_upload in standard WP AJAX flow usually,
|
||||
// but if we need custom handling it goes here.
|
||||
// For now, assuming standard media uploader is used on frontend which might not need this
|
||||
// if using wp.media JS library directly.
|
||||
// However, if we need to process the upload specifically for this plugin:
|
||||
|
||||
if ( ! empty( $_FILES['file'] ) ) {
|
||||
$file = $_FILES['file'];
|
||||
$attachment_id = media_handle_upload( 'file', 0 );
|
||||
|
||||
if ( is_wp_error( $attachment_id ) ) {
|
||||
wp_send_json_error( array( 'message' => 'Upload failed: ' . $attachment_id->get_error_message() ) );
|
||||
} else {
|
||||
wp_send_json_success( array(
|
||||
'attachment_id' => $attachment_id,
|
||||
'url' => wp_get_attachment_url( $attachment_id )
|
||||
) );
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error( array( 'message' => 'No file provided' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for previewing slides
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function ajax_preview_slides() {
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'read' ) ) {
|
||||
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$site_slug = isset( $_POST['site'] ) ? sanitize_text_field( $_POST['site'] ) : '';
|
||||
$date = isset( $_POST['date'] ) ? sanitize_text_field( $_POST['date'] ) : current_time( 'Y-m-d' );
|
||||
|
||||
// This logic mirrors hssm_get_preview_slides_for_date in the template
|
||||
// We need to fetch slides that would be active on this date for this site
|
||||
|
||||
$meta_query = array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => '_hssm_start_date',
|
||||
'value' => '',
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_start_date',
|
||||
'value' => $date,
|
||||
'compare' => '<=',
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_start_date',
|
||||
'compare' => 'NOT EXISTS',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => '_hssm_end_date',
|
||||
'value' => $date,
|
||||
'compare' => '>=',
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_end_date',
|
||||
'compare' => 'NOT EXISTS',
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_end_date',
|
||||
'value' => '',
|
||||
'compare' => '=',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$tax_query = array();
|
||||
if ( ! empty( $site_slug ) ) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'hssm_site',
|
||||
'field' => 'slug',
|
||||
'terms' => $site_slug,
|
||||
);
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'post_status' => array( 'publish', 'future' ),
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'meta_value_num',
|
||||
'meta_key' => '_hssm_order',
|
||||
'order' => 'ASC',
|
||||
'meta_query' => $meta_query,
|
||||
'tax_query' => $tax_query
|
||||
);
|
||||
|
||||
$slides = get_posts( $args );
|
||||
$formatted_slides = array();
|
||||
|
||||
foreach ( $slides as $slide ) {
|
||||
$formatted_slides[] = $this->format_slide_for_frontend( $slide->ID );
|
||||
}
|
||||
|
||||
wp_send_json_success( array( 'slides' => $formatted_slides ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for saving slides
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function ajax_save_slide() {
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) {
|
||||
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize and validate input
|
||||
$slide_data = $this->sanitize_slide_data( $_POST );
|
||||
|
||||
if ( ! $this->validate_slide_data( $slide_data ) ) {
|
||||
wp_send_json_error( array( 'message' => 'Invalid slide data' ) );
|
||||
}
|
||||
|
||||
// Create the slide post
|
||||
$post_args = array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'post_title' => $slide_data['title'],
|
||||
'post_content' => $slide_data['content'],
|
||||
'post_excerpt' => $slide_data['content'],
|
||||
'post_status' => $slide_data['post_status'],
|
||||
'post_author' => get_current_user_id(),
|
||||
);
|
||||
|
||||
// If scheduling for future, set the post date
|
||||
if ( $slide_data['post_status'] === 'future' && ! empty( $slide_data['start_date'] ) ) {
|
||||
$post_args['post_date'] = $slide_data['start_date'] . ' 00:00:00';
|
||||
$post_args['post_date_gmt'] = get_gmt_from_date( $post_args['post_date'] );
|
||||
}
|
||||
|
||||
$slide_id = wp_insert_post( $post_args );
|
||||
|
||||
if ( is_wp_error( $slide_id ) ) {
|
||||
wp_send_json_error( array( 'message' => 'Failed to create slide' ) );
|
||||
}
|
||||
|
||||
// Save meta data
|
||||
$this->save_slide_meta( $slide_id, $slide_data );
|
||||
|
||||
// Save taxonomies
|
||||
$this->save_slide_taxonomies( $slide_id, $slide_data );
|
||||
|
||||
// Set featured image if provided
|
||||
if ( ! empty( $slide_data['featured_image_id'] ) ) {
|
||||
set_post_thumbnail( $slide_id, $slide_data['featured_image_id'] );
|
||||
}
|
||||
|
||||
wp_send_json_success( array(
|
||||
'message' => 'Slide saved successfully!',
|
||||
'slide_id' => $slide_id,
|
||||
'slide_data' => $this->format_slide_for_frontend( $slide_id )
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for image uploads
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
/**
|
||||
* Sanitize slide data from form
|
||||
*
|
||||
@@ -504,10 +376,9 @@ class HSSM_Frontend {
|
||||
'content' => wp_kses_post( $data['content'] ?? '' ),
|
||||
'buttons' => $this->sanitize_buttons( $buttons ),
|
||||
'target_sites' => array_map( 'sanitize_text_field', $target_sites ),
|
||||
'client' => sanitize_text_field( $data['client'] ?? '' ),
|
||||
'start_date' => sanitize_text_field( $data['start_date'] ?? '' ),
|
||||
'end_date' => sanitize_text_field( $data['end_date'] ?? '' ),
|
||||
'post_status' => in_array( $data['post_status'] ?? 'draft', array( 'draft', 'publish', 'future' ) ) ? $data['post_status'] : 'draft',
|
||||
'post_status' => in_array( $data['post_status'] ?? 'draft', array( 'draft', 'publish', 'future' ), true ) ? ( $data['post_status'] ?? 'draft' ) : 'draft',
|
||||
'auto_publish' => ! empty( $data['auto_publish'] ),
|
||||
'auto_unpublish' => ! empty( $data['auto_unpublish'] ),
|
||||
'order' => intval( $data['order'] ?? 0 ),
|
||||
@@ -646,11 +517,6 @@ class HSSM_Frontend {
|
||||
} else {
|
||||
hssm_debug_log( 'No target sites to save for slide ' . $slide_id );
|
||||
}
|
||||
|
||||
// Set client
|
||||
if ( ! empty( $data['client'] ) ) {
|
||||
wp_set_post_terms( $slide_id, array( $data['client'] ), 'hssm_client' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,7 +537,6 @@ class HSSM_Frontend {
|
||||
'buttons' => get_post_meta( $slide_id, '_hssm_buttons', true ),
|
||||
'featured_image' => get_the_post_thumbnail_url( $slide_id, 'medium' ),
|
||||
'target_sites' => wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) ),
|
||||
'client' => wp_get_post_terms( $slide_id, 'hssm_client', array( 'fields' => 'slugs' ) ),
|
||||
'status' => $slide->post_status,
|
||||
'created' => $slide->post_date,
|
||||
);
|
||||
@@ -699,8 +564,10 @@ class HSSM_Frontend {
|
||||
|
||||
// Get filter parameters
|
||||
$status_filter = isset( $_POST['status_filter'] ) ? sanitize_text_field( $_POST['status_filter'] ) : '';
|
||||
if ( 'scheduled' === $status_filter ) {
|
||||
$status_filter = 'future';
|
||||
}
|
||||
$site_filter = isset( $_POST['site_filter'] ) ? sanitize_text_field( $_POST['site_filter'] ) : '';
|
||||
$client_filter = isset( $_POST['client_filter'] ) ? sanitize_text_field( $_POST['client_filter'] ) : '';
|
||||
$month_filter = isset( $_POST['month_filter'] ) ? sanitize_text_field( $_POST['month_filter'] ) : ''; // Format: YYYY-MM
|
||||
$year_filter = isset( $_POST['year_filter'] ) ? intval( $_POST['year_filter'] ) : 0;
|
||||
|
||||
@@ -734,21 +601,6 @@ class HSSM_Frontend {
|
||||
}
|
||||
}
|
||||
|
||||
// Add client filter using taxonomy query
|
||||
if ( ! empty( $client_filter ) ) {
|
||||
// Get client term by slug or ID
|
||||
$client_term = is_numeric( $client_filter )
|
||||
? get_term( intval( $client_filter ), 'hssm_client' )
|
||||
: get_term_by( 'slug', $client_filter, 'hssm_client' );
|
||||
if ( $client_term && ! is_wp_error( $client_term ) ) {
|
||||
$args['tax_query'][] = array(
|
||||
'taxonomy' => 'hssm_client',
|
||||
'field' => 'term_id',
|
||||
'terms' => $client_term->term_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add month/year filter using date query
|
||||
if ( ! empty( $month_filter ) ) {
|
||||
// Format: YYYY-MM
|
||||
@@ -876,9 +728,11 @@ class HSSM_Frontend {
|
||||
|
||||
$site_terms = wp_get_post_terms( $slide->ID, 'hssm_site', array( 'fields' => 'all' ) );
|
||||
$site_names = array();
|
||||
$site_slugs = array();
|
||||
if ( ! empty( $site_terms ) && ! is_wp_error( $site_terms ) ) {
|
||||
foreach ( $site_terms as $site_term ) {
|
||||
$site_names[] = $site_term->name;
|
||||
$site_slugs[] = $site_term->slug;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,8 +754,8 @@ class HSSM_Frontend {
|
||||
'status' => $slide->post_status,
|
||||
'status_display' => $status_display,
|
||||
'sites' => $site_names,
|
||||
'site_slugs' => $site_slugs,
|
||||
'target_sites' => $site_names,
|
||||
'client' => $this->get_slide_client_name( $slide->ID ),
|
||||
'start_date' => $this->format_dashboard_date( $start_date ),
|
||||
'end_date' => $this->format_dashboard_date( $end_date ),
|
||||
'auto_publish' => isset( $slide_meta['_hssm_auto_publish'][0] ) ? $slide_meta['_hssm_auto_publish'][0] : '',
|
||||
@@ -965,8 +819,7 @@ class HSSM_Frontend {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
if ( ! $this->check_dashboard_access() ) {
|
||||
wp_send_json_error( array( 'message' => 'Permission denied' ) );
|
||||
return;
|
||||
}
|
||||
@@ -994,9 +847,9 @@ class HSSM_Frontend {
|
||||
if ( $result ) {
|
||||
hssm_debug_log( "Slide $slide_id approved with action: $approve_action" );
|
||||
|
||||
// Trigger 2: Notification to Creator (Author) when approved
|
||||
$notifications = new HSSM_Notifications();
|
||||
$notifications->notify_creator_slide_approved( $slide_id );
|
||||
$notifications->notify_itc_slide_approved( $slide_id );
|
||||
|
||||
wp_send_json_success( array(
|
||||
'message' => 'Slide approved successfully',
|
||||
@@ -1302,21 +1155,6 @@ class HSSM_Frontend {
|
||||
return implode( ', ', $site_names );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client name for a slide from taxonomy
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $slide_id Slide ID
|
||||
* @return string Client name or empty string
|
||||
*/
|
||||
private function get_slide_client_name( $slide_id ) {
|
||||
$client_terms = wp_get_post_terms( $slide_id, 'hssm_client', array( 'fields' => 'all' ) );
|
||||
if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) {
|
||||
return $client_terms[0]->name;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available sites for targeting
|
||||
*
|
||||
@@ -1356,6 +1194,112 @@ class HSSM_Frontend {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest preview and confirmation pages under the slide dashboard.
|
||||
*
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public function maybe_nest_plugin_pages() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dashboard_query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => array( 'publish', 'private' ),
|
||||
'posts_per_page' => 1,
|
||||
'no_found_rows' => true,
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-dashboard.php',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $dashboard_query->have_posts() ) {
|
||||
wp_reset_postdata();
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_id = (int) $dashboard_query->posts[0]->ID;
|
||||
wp_reset_postdata();
|
||||
|
||||
$this->maybe_create_confirmation_page( $parent_id );
|
||||
|
||||
$child_templates = array( 'page-slide-preview.php', 'page-slide-confirmation.php' );
|
||||
|
||||
foreach ( $child_templates as $template ) {
|
||||
$pages_query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => array( 'publish', 'private' ),
|
||||
'posts_per_page' => 20,
|
||||
'no_found_rows' => true,
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => $template,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $pages_query->have_posts() ) {
|
||||
wp_reset_postdata();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $pages_query->posts as $page ) {
|
||||
if ( (int) $page->ID === $parent_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (int) $page->post_parent !== $parent_id ) {
|
||||
wp_update_post(
|
||||
array(
|
||||
'ID' => $page->ID,
|
||||
'post_parent' => $parent_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ITC confirmation page when it is missing.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $parent_id Dashboard page ID.
|
||||
*/
|
||||
private function maybe_create_confirmation_page( $parent_id ) {
|
||||
$existing = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => array( 'publish', 'private' ),
|
||||
'posts_per_page' => 1,
|
||||
'no_found_rows' => true,
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-confirmation.php',
|
||||
)
|
||||
);
|
||||
|
||||
if ( $existing->have_posts() ) {
|
||||
wp_reset_postdata();
|
||||
return;
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
wp_insert_post(
|
||||
array(
|
||||
'post_title' => 'Slide Confirmation',
|
||||
'post_name' => 'slide-confirmation',
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'page',
|
||||
'post_parent' => absint( $parent_id ),
|
||||
'meta_input' => array(
|
||||
'_wp_page_template' => 'page-slide-confirmation.php',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for duplicating slides
|
||||
*
|
||||
@@ -1440,14 +1384,6 @@ class HSSM_Frontend {
|
||||
wp_send_json_error( array( 'message' => 'Failed to update slide status' ) );
|
||||
}
|
||||
|
||||
// Trigger notification if published via toggle?
|
||||
// Logic check: If toggled to publish, it means it's Live.
|
||||
if ( $new_status === 'publish' ) {
|
||||
// Trigger 3: Notification to ITCGuys when slide goes Live (via manual toggle)
|
||||
$notifications = new HSSM_Notifications();
|
||||
$notifications->notify_itc_slide_live( $slide_id );
|
||||
}
|
||||
|
||||
wp_send_json_success( array(
|
||||
'message' => 'Slide status updated successfully',
|
||||
'new_status' => $new_status
|
||||
|
||||
@@ -177,6 +177,10 @@ class HSSM_Main {
|
||||
|
||||
// Cron hooks for scheduled slides
|
||||
add_action( 'hssm_check_scheduled_slides', array( $this, 'check_scheduled_slides' ) );
|
||||
|
||||
if ( 'manager' === $plugin_mode && $this->email_notifications ) {
|
||||
add_action( 'transition_post_status', array( $this->email_notifications, 'on_slide_status_change' ), 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +236,7 @@ class HSSM_Main {
|
||||
*/
|
||||
public function enqueue_frontend_assets() {
|
||||
// Prevent slider assets from loading on admin-facing "frontend" pages
|
||||
if ( is_page_template('page-slide-dashboard.php') || is_page_template('page-slide-preview.php') ) {
|
||||
if ( is_page_template('page-slide-dashboard.php') || is_page_template('page-slide-preview.php') || is_page_template('page-slide-confirmation.php') ) {
|
||||
// These pages will load their own specific styles (admin-slides-dashboard.css) or rely on explicit shortcode enqueueing.
|
||||
return;
|
||||
}
|
||||
@@ -312,23 +316,28 @@ class HSSM_Main {
|
||||
* @return array List of companion sites
|
||||
*/
|
||||
public static function get_companion_sites() {
|
||||
return array(
|
||||
$sites = array(
|
||||
array(
|
||||
'slug' => 'hi-school',
|
||||
'name' => 'Hi-School Pharmacy',
|
||||
'description' => 'Hi-School Pharmacy companion site'
|
||||
'description' => 'Hi-School Pharmacy companion site',
|
||||
'url' => '',
|
||||
),
|
||||
array(
|
||||
'slug' => 'one-stop',
|
||||
'name' => 'One Stop Hardware',
|
||||
'description' => 'One Stop Hardware companion site'
|
||||
'description' => 'One Stop Hardware companion site',
|
||||
'url' => '',
|
||||
),
|
||||
array(
|
||||
'slug' => 'ace',
|
||||
'name' => 'Ace Hardware',
|
||||
'description' => 'Ace Hardware companion site'
|
||||
'description' => 'Ace Hardware companion site',
|
||||
'url' => '',
|
||||
)
|
||||
);
|
||||
|
||||
return apply_filters( 'hssm_companion_sites', $sites );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,7 +456,10 @@ class HSSM_Main {
|
||||
'hssm_plugin_mode' => 'manager',
|
||||
'hssm_api_url' => '',
|
||||
'hssm_client_site_slug' => '',
|
||||
'hssm_notification_emails' => array( get_option( 'admin_email' ) ),
|
||||
'hssm_notification_settings' => array(
|
||||
'editor_emails' => get_option( 'admin_email' ),
|
||||
'itc_emails' => '',
|
||||
),
|
||||
'hssm_default_slide_duration' => 30, // days
|
||||
'hssm_cache_duration' => 3600, // 1 hour
|
||||
'hssm_api_version' => 'v1',
|
||||
|
||||
@@ -35,17 +35,42 @@ class HSSM_Notifications {
|
||||
public static function get_settings() {
|
||||
$defaults = array(
|
||||
'editor_emails' => '',
|
||||
'itc_emails' => ''
|
||||
'itc_emails' => '',
|
||||
);
|
||||
|
||||
$settings = get_option( 'hssm_notification_settings', $defaults );
|
||||
|
||||
// Ensure we have an array and it contains our expected keys
|
||||
$settings = get_option( 'hssm_notification_settings', false );
|
||||
if ( ! is_array( $settings ) ) {
|
||||
return $defaults;
|
||||
$settings = $defaults;
|
||||
} else {
|
||||
$settings = wp_parse_args( $settings, $defaults );
|
||||
}
|
||||
|
||||
return wp_parse_args( $settings, $defaults );
|
||||
$has_current = '' !== trim( (string) $settings['editor_emails'] ) || '' !== trim( (string) $settings['itc_emails'] );
|
||||
if ( $has_current ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$legacy = get_option( 'hssm_notification_emails', array() );
|
||||
if ( empty( $legacy ) ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
if ( is_array( $legacy ) ) {
|
||||
$legacy_emails = array_filter( array_map( 'sanitize_email', $legacy ) );
|
||||
$legacy_string = implode( ',', $legacy_emails );
|
||||
} else {
|
||||
$legacy_string = sanitize_text_field( (string) $legacy );
|
||||
}
|
||||
|
||||
if ( '' === $legacy_string ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$settings['editor_emails'] = $legacy_string;
|
||||
update_option( 'hssm_notification_settings', $settings );
|
||||
delete_option( 'hssm_notification_emails' );
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +114,215 @@ class HSSM_Notifications {
|
||||
return array_unique( $clean_emails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation token for ITC live emails (does not depend on a logged-in session).
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_confirm_token( $slide_id ) {
|
||||
return hash( 'sha256', absint( $slide_id ) . '|' . wp_salt( 'auth' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a confirmation token is valid.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @param string $token Token from the email link.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_valid_confirm_token( $slide_id, $token ) {
|
||||
return is_string( $token ) && hash_equals( self::get_confirm_token( $slide_id ), $token );
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview page URL for a slide.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return string
|
||||
*/
|
||||
private function get_preview_link( $slide_id ) {
|
||||
$start_date = get_post_meta( $slide_id, '_hssm_start_date', true );
|
||||
if ( $start_date && preg_match( '/^(\d{4}-\d{2}-\d{2})/', $start_date, $matches ) ) {
|
||||
$date = $matches[1];
|
||||
} else {
|
||||
$date = current_time( 'Y-m-d' );
|
||||
}
|
||||
|
||||
$sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) );
|
||||
$first_site = ( ! is_wp_error( $sites ) && ! empty( $sites ) ) ? $sites[0] : '';
|
||||
|
||||
return add_query_arg(
|
||||
array(
|
||||
'hssm_date' => $date,
|
||||
'hssm_site' => $first_site,
|
||||
'hssm_preview' => $slide_id,
|
||||
),
|
||||
hssm_get_page_url_by_template( 'page-slide-preview.php', '/slide-preview/' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ITC confirmation page URL.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return string
|
||||
*/
|
||||
private function get_confirmation_link( $slide_id ) {
|
||||
return add_query_arg(
|
||||
array(
|
||||
'slide_id' => $slide_id,
|
||||
'hssm_token' => self::get_confirm_token( $slide_id ),
|
||||
),
|
||||
hssm_get_page_url_by_template( 'page-slide-confirmation.php', '/slide-confirmation/' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front dashboard URL.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @return string
|
||||
*/
|
||||
private function get_dashboard_link( $slide_id = 0 ) {
|
||||
$url = hssm_get_page_url_by_template( 'page-slide-dashboard.php', '/slide-dashboard/' );
|
||||
if ( $slide_id ) {
|
||||
$url = add_query_arg( 'hssm_focus', absint( $slide_id ), $url );
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the slide is currently visible on companion sites.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return bool
|
||||
*/
|
||||
private function is_slide_currently_live( $slide_id ) {
|
||||
$post = get_post( $slide_id );
|
||||
if ( ! $post || 'publish' !== $post->post_status ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = current_time( 'timestamp' );
|
||||
$start = get_post_meta( $slide_id, '_hssm_start_date', true );
|
||||
if ( $start ) {
|
||||
$start_ts = strtotime( $start );
|
||||
if ( false !== $start_ts && $start_ts > $now ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$end = get_post_meta( $slide_id, '_hssm_end_date', true );
|
||||
if ( $end ) {
|
||||
$end_ts = strtotime( $end );
|
||||
if ( false !== $end_ts && $end_ts < $now ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Human-readable site list, with URLs when configured.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return string
|
||||
*/
|
||||
private function format_site_lines( $slide_id ) {
|
||||
$terms = wp_get_post_terms( $slide_id, 'hssm_site' );
|
||||
if ( is_wp_error( $terms ) || empty( $terms ) ) {
|
||||
return 'None';
|
||||
}
|
||||
|
||||
$catalog = array();
|
||||
if ( class_exists( 'HSSM_Main' ) ) {
|
||||
foreach ( HSSM_Main::get_companion_sites() as $site ) {
|
||||
$catalog[ $site['slug'] ] = $site;
|
||||
}
|
||||
}
|
||||
|
||||
$lines = array();
|
||||
foreach ( $terms as $term ) {
|
||||
$line = $term->name . ' (' . $term->slug . ')';
|
||||
if ( ! empty( $catalog[ $term->slug ]['url'] ) ) {
|
||||
$line .= ' - ' . $catalog[ $term->slug ]['url'];
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
|
||||
return implode( "\n", $lines );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a one-time notification as sent.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @param string $key Meta key.
|
||||
* @return bool True if this is the first send.
|
||||
*/
|
||||
private function mark_sent_once( $slide_id, $key ) {
|
||||
if ( get_post_meta( $slide_id, $key, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_post_meta( $slide_id, $key, current_time( 'mysql' ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ITC the go-live email when a slide first reaches publish.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param string $new_status New status.
|
||||
* @param string $old_status Previous status.
|
||||
* @param WP_Post $post Post object.
|
||||
*/
|
||||
public function on_slide_status_change( $new_status, $old_status, $post ) {
|
||||
if ( ! $post instanceof WP_Post || 'hssm_slide' !== $post->post_type ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'publish' !== $new_status || 'publish' === $old_status ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->notify_itc_slide_live( $post->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify ITC of already-published slides whose start date just arrived.
|
||||
*
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public function notify_due_live_slides() {
|
||||
$slides = get_posts(
|
||||
array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 50,
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_hssm_itc_live_sent',
|
||||
'compare' => 'NOT EXISTS',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $slides as $slide ) {
|
||||
$this->notify_itc_slide_live( $slide->ID );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to Editor when slide is Scheduled or Published Immediately
|
||||
*
|
||||
@@ -105,47 +339,23 @@ class HSSM_Notifications {
|
||||
}
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
$target_sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) );
|
||||
$site_slugs = ! empty( $target_sites ) ? implode( ',', $target_sites ) : '';
|
||||
|
||||
// Determine publish date
|
||||
$publish_date = ( $status === 'scheduled' )
|
||||
? get_post_meta( $slide_id, '_hssm_start_date', true )
|
||||
: current_time( 'Y-m-d' );
|
||||
|
||||
// Construct preview link
|
||||
// page-slide-preview.php logic usually takes 'date' and 'site' params
|
||||
// We'll link to the preview page. Assuming there is a page with the template, or we use a custom URL structure if defined.
|
||||
// The instructions mentioned: "generated link to the slide-preview page with params containing the slide-title, the publish-date... and the site slugs"
|
||||
|
||||
$preview_page_url = home_url( '/slide-preview/' ); // Basic assumption, adjust if needed based on setup
|
||||
// Better: find the page using the template
|
||||
$pages = get_pages(array(
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-preview.php'
|
||||
));
|
||||
|
||||
if ( ! empty( $pages ) ) {
|
||||
$preview_page_url = get_permalink( $pages[0]->ID );
|
||||
if ( ! $slide ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$preview_link = add_query_arg( array(
|
||||
'preview_title' => urlencode( $slide->post_title ),
|
||||
'date' => $publish_date,
|
||||
'sites' => $site_slugs,
|
||||
'preview_mode' => 'editor_check'
|
||||
), $preview_page_url );
|
||||
$start_date = get_post_meta( $slide_id, '_hssm_start_date', true );
|
||||
$publish_date = $start_date ? $start_date : current_time( 'Y-m-d' );
|
||||
$status_label = ( 'scheduled' === $status ) ? 'Scheduled' : 'Published Immediately';
|
||||
|
||||
$subject = sprintf( 'New Slide %s: "%s"', ucfirst( $status ), $slide->post_title );
|
||||
$subject = sprintf( 'New Slide %s: "%s"', $status_label, $slide->post_title );
|
||||
|
||||
$message = "A new slide has been " . ( $status === 'scheduled' ? 'Scheduled' : 'Published Immediately' ) . ".\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Target Date: " . $publish_date . "\n";
|
||||
$message .= "Target Sites: " . implode( ', ', $target_sites ) . "\n\n";
|
||||
$message .= "Preview Link: " . $preview_link . "\n\n";
|
||||
$message .= "Please review the slide.";
|
||||
$message = "A new slide has been {$status_label}.\n\n";
|
||||
$message .= 'Title: ' . $slide->post_title . "\n";
|
||||
$message .= 'Target Date: ' . $publish_date . "\n";
|
||||
$message .= "Target Sites:\n" . $this->format_site_lines( $slide_id ) . "\n\n";
|
||||
$message .= 'Preview: ' . $this->get_preview_link( $slide_id ) . "\n";
|
||||
$message .= 'Dashboard: ' . $this->get_dashboard_link( $slide_id ) . "\n\n";
|
||||
$message .= 'Please review the slide.';
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
@@ -159,80 +369,102 @@ class HSSM_Notifications {
|
||||
*/
|
||||
public function notify_creator_slide_approved( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
if ( ! $slide ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$author_email = get_the_author_meta( 'user_email', $slide->post_author );
|
||||
|
||||
if ( ! is_email( $author_email ) ) return;
|
||||
if ( ! is_email( $author_email ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subject = 'Your Slide Has Been Approved: "' . $slide->post_title . '"';
|
||||
|
||||
$message = "Good news! Your slide has been approved.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "It is now scheduled/published according to your settings.";
|
||||
$message .= 'Title: ' . $slide->post_title . "\n";
|
||||
$message .= "Target Sites:\n" . $this->format_site_lines( $slide_id ) . "\n\n";
|
||||
$message .= 'Preview: ' . $this->get_preview_link( $slide_id ) . "\n";
|
||||
$message .= 'Dashboard: ' . $this->get_dashboard_link( $slide_id ) . "\n\n";
|
||||
$message .= 'It is now scheduled or published according to your settings.';
|
||||
|
||||
wp_mail( $author_email, $subject, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify ITC to review date range after approval.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
*/
|
||||
public function notify_itc_slide_approved( $slide_id ) {
|
||||
if ( ! $this->mark_sent_once( $slide_id, '_hssm_itc_approved_sent' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = self::get_settings();
|
||||
$emails = $this->get_emails_array( $settings['itc_emails'] );
|
||||
if ( empty( $emails ) ) {
|
||||
hssm_debug_log( 'No ITC emails configured for approval notification.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$start_date = get_post_meta( $slide_id, '_hssm_start_date', true );
|
||||
$end_date = get_post_meta( $slide_id, '_hssm_end_date', true );
|
||||
|
||||
$subject = 'Slide Approved for Review: "' . $slide->post_title . '"';
|
||||
|
||||
$message = "A slide has been approved. Please review the date range and preview before it goes live.\n\n";
|
||||
$message .= 'Title: ' . $slide->post_title . "\n";
|
||||
$message .= 'Start Date: ' . ( $start_date ? $start_date : 'Immediate' ) . "\n";
|
||||
$message .= 'End Date: ' . ( $end_date ? $end_date : 'None' ) . "\n";
|
||||
$message .= "Target Sites:\n" . $this->format_site_lines( $slide_id ) . "\n\n";
|
||||
$message .= 'Preview: ' . $this->get_preview_link( $slide_id ) . "\n";
|
||||
$message .= 'Dashboard: ' . $this->get_dashboard_link( $slide_id );
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to ITCGuys when slide goes LIVE
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
*/
|
||||
public function notify_itc_slide_live( $slide_id ) {
|
||||
if ( ! $this->is_slide_currently_live( $slide_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->mark_sent_once( $slide_id, '_hssm_itc_live_sent' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = self::get_settings();
|
||||
$emails = $this->get_emails_array( $settings['itc_emails'] );
|
||||
|
||||
if ( empty( $emails ) ) return;
|
||||
if ( empty( $emails ) ) {
|
||||
hssm_debug_log( 'No ITC emails configured for live notification.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
// Generate Confirmation Link
|
||||
// This link needs to trigger the "Slide is Live" confirmation page
|
||||
// We probably need a specialized page or endpoint for this.
|
||||
// Let's assume we use the Dashboard page with a specific action param or a separate page.
|
||||
// User requested: "We need a page 'Slide is Live' ... with a green confirmation button"
|
||||
// Let's use the preview page or dashboard page with a specific query arg to show this UI.
|
||||
// Or better, a dedicated endpoint. For now, let's point to the dashboard with a "confirm_live" param.
|
||||
|
||||
$confirm_url = home_url( '/slide-live-confirmation/' ); // Ideally we create this page or rewrite rule
|
||||
// For now, let's use the dashboard url with arguments, and we'll handle the UI there or create a rewrite.
|
||||
// Actually, creating a specific page 'Slide is Live' was requested.
|
||||
// I'll create a new template `page-slide-confirmation.php` later.
|
||||
|
||||
// Find confirmation page if exists, or construct URL
|
||||
$pages = get_pages(array(
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-confirmation.php'
|
||||
));
|
||||
$confirmation_page_url = !empty($pages) ? get_permalink($pages[0]->ID) : home_url('/?hssm_action=confirm_live');
|
||||
|
||||
$confirm_link = add_query_arg( array(
|
||||
'slide_id' => $slide_id,
|
||||
'token' => wp_create_nonce( 'hssm_confirm_live_' . $slide_id ) // Note: Nonces depend on user session, might be tricky for email links if user isn't logged in.
|
||||
// Better to use a hash that doesn't expire or relies on a shared secret if ITC guys are not logged in.
|
||||
// Assuming ITC guys are logged in users for now or we just use a hash.
|
||||
), $confirmation_page_url );
|
||||
|
||||
// Since email links are clicked outside sessions often, maybe just a hash of slide ID + secret salt.
|
||||
$token = hash( 'sha256', $slide_id . 'hssm_secret_salt' );
|
||||
$confirm_link = add_query_arg( array(
|
||||
'slide_id' => $slide_id,
|
||||
'hssm_token' => $token
|
||||
), $confirmation_page_url );
|
||||
|
||||
|
||||
$target_sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) );
|
||||
if ( ! $slide ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subject = 'ACTION REQUIRED: Slide is LIVE - "' . $slide->post_title . '"';
|
||||
|
||||
$message = "A slide has just gone LIVE.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Sites: " . implode( ', ', $target_sites ) . "\n\n";
|
||||
$message .= 'Title: ' . $slide->post_title . "\n";
|
||||
$message .= "Sites:\n" . $this->format_site_lines( $slide_id ) . "\n\n";
|
||||
$message .= "Please check the sites to ensure it is displaying correctly.\n";
|
||||
$message .= "Once verified, click the link below to confirm to the Editor and Author:\n\n";
|
||||
$message .= $confirm_link;
|
||||
$message .= "Once verified, click the confirmation link to notify the Editor and Author:\n\n";
|
||||
$message .= $this->get_confirmation_link( $slide_id );
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
@@ -243,32 +475,43 @@ class HSSM_Notifications {
|
||||
* Send FINAL notification to Editor and Author (triggered by ITC confirmation)
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
* @return bool True if the confirmation email was sent.
|
||||
*/
|
||||
public function notify_all_confirmed_live( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
if ( ! $slide ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->mark_sent_once( $slide_id, '_hssm_live_confirmed' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Editor Emails
|
||||
$settings = self::get_settings();
|
||||
$editor_emails = $this->get_emails_array( $settings['editor_emails'] );
|
||||
|
||||
// Author Email
|
||||
$author_email = get_the_author_meta( 'user_email', $slide->post_author );
|
||||
$recipients = array_unique( array_filter( array_merge( $editor_emails, array( $author_email ) ) ) );
|
||||
|
||||
$recipients = array_merge( $editor_emails, array( $author_email ) );
|
||||
$recipients = array_unique( array_filter( $recipients ) );
|
||||
|
||||
if ( empty( $recipients ) ) return;
|
||||
if ( empty( $recipients ) ) {
|
||||
hssm_debug_log( 'No recipients for live confirmation email.' );
|
||||
return true;
|
||||
}
|
||||
|
||||
$subject = 'CONFIRMED: Slide is Live and Verified - "' . $slide->post_title . '"';
|
||||
|
||||
$message = "The slide has been verified as LIVE by the ITC team.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Status: Live & Verified\n\n";
|
||||
$message .= "You can view the slide on the live sites.";
|
||||
$message .= 'Title: ' . $slide->post_title . "\n";
|
||||
$message .= "Status: Live & Verified\n";
|
||||
$message .= "Sites:\n" . $this->format_site_lines( $slide_id ) . "\n\n";
|
||||
$message .= 'Dashboard: ' . $this->get_dashboard_link( $slide_id ) . "\n";
|
||||
$message .= 'Preview: ' . $this->get_preview_link( $slide_id );
|
||||
|
||||
foreach ( $recipients as $email ) {
|
||||
if ( is_email( $email ) ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -360,7 +360,6 @@ class HSSM_REST_API {
|
||||
'auto_unpublish' => get_post_meta( $slide->ID, '_hssm_auto_unpublish', true ) === '1',
|
||||
),
|
||||
'target_sites' => $this->get_slide_target_sites( $slide->ID ),
|
||||
'client' => $this->get_slide_client( $slide->ID ),
|
||||
'status' => $slide->post_status,
|
||||
'created' => $slide->post_date,
|
||||
'modified' => $slide->post_modified,
|
||||
@@ -427,27 +426,6 @@ class HSSM_REST_API {
|
||||
return $sites_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slide client
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $slide_id Slide ID
|
||||
* @return array|null Client data
|
||||
*/
|
||||
private function get_slide_client( $slide_id ) {
|
||||
$clients = wp_get_post_terms( $slide_id, 'hssm_client' );
|
||||
|
||||
if ( ! is_wp_error( $clients ) && ! empty( $clients ) ) {
|
||||
$client = $clients[0];
|
||||
return array(
|
||||
'slug' => $client->slug,
|
||||
'name' => $client->name,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slide count for a site
|
||||
*
|
||||
|
||||
@@ -22,6 +22,7 @@ class HSSM_Shortcode {
|
||||
*/
|
||||
public function __construct() {
|
||||
add_shortcode( 'hssm_slider', array( $this, 'render_shortcode' ) );
|
||||
add_shortcode( 'hssm_slides', array( $this, 'render_shortcode' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,14 +50,37 @@ class HSSM_Shortcode {
|
||||
$slides = $result;
|
||||
} else {
|
||||
// Manager mode - Fetch locally
|
||||
$site_slug = isset( $atts['site'] ) ? sanitize_text_field( $atts['site'] ) : get_option( 'hssm_client_site_slug', 'hi-school' );
|
||||
$date = isset( $atts['date'] ) ? sanitize_text_field( $atts['date'] ) : current_time( 'Y-m-d' );
|
||||
if ( isset( $atts['count'] ) && ! isset( $atts['limit'] ) ) {
|
||||
$atts['limit'] = $atts['count'];
|
||||
}
|
||||
|
||||
$atts = shortcode_atts(
|
||||
array(
|
||||
'site' => get_option( 'hssm_client_site_slug', 'hi-school' ),
|
||||
'date' => current_time( 'Y-m-d' ),
|
||||
'limit' => 10,
|
||||
'slide' => 0,
|
||||
'include_pending' => '0',
|
||||
),
|
||||
$atts,
|
||||
'hssm_slider'
|
||||
);
|
||||
|
||||
$site_slug = sanitize_text_field( $atts['site'] );
|
||||
$date = sanitize_text_field( $atts['date'] );
|
||||
$focus_slide_id = absint( $atts['slide'] );
|
||||
$include_pending = filter_var( $atts['include_pending'], FILTER_VALIDATE_BOOLEAN );
|
||||
|
||||
$post_status = array( 'publish', 'future' );
|
||||
if ( $include_pending ) {
|
||||
$post_status[] = 'pending';
|
||||
}
|
||||
|
||||
// Build query args similar to what's in the REST API or Frontend class
|
||||
$args = array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'posts_per_page' => isset( $atts['limit'] ) ? intval( $atts['limit'] ) : 10,
|
||||
'post_status' => array( 'publish', 'future', 'pending' ),
|
||||
'posts_per_page' => intval( $atts['limit'] ),
|
||||
'post_status' => $post_status,
|
||||
'meta_key' => '_hssm_order',
|
||||
'orderby' => 'meta_value_num',
|
||||
'order' => 'ASC',
|
||||
@@ -94,21 +118,14 @@ class HSSM_Shortcode {
|
||||
if ( $query->have_posts() ) {
|
||||
while ( $query->have_posts() ) {
|
||||
$query->the_post();
|
||||
$slide_id = get_the_ID();
|
||||
|
||||
// Format data to match what the API returns
|
||||
$slides[] = array(
|
||||
'id' => $slide_id,
|
||||
'title' => get_the_title(),
|
||||
'subtitle' => get_post_meta( $slide_id, '_hssm_subtitle', true ),
|
||||
'content' => get_the_content(),
|
||||
'buttons' => get_post_meta( $slide_id, '_hssm_buttons', true ),
|
||||
'featured_image' => get_the_post_thumbnail_url( $slide_id, 'full' ),
|
||||
'hide_all_text' => get_post_meta( $slide_id, '_hssm_hide_all_text', true ) === '1',
|
||||
);
|
||||
$slides[] = $this->format_slide_for_slider( get_the_ID() );
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
if ( $focus_slide_id ) {
|
||||
$slides = $this->prepend_focus_slide( $slides, $focus_slide_id, $include_pending );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $slides ) ) {
|
||||
@@ -118,4 +135,77 @@ class HSSM_Shortcode {
|
||||
$slider = new HSSM_Slider();
|
||||
return $slider->render( $slides, $atts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a slide for the public slider.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param int $slide_id Slide ID.
|
||||
* @return array|null
|
||||
*/
|
||||
private function format_slide_for_slider( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide || 'hssm_slide' !== $slide->post_type ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $slide->ID,
|
||||
'title' => $slide->post_title,
|
||||
'subtitle' => get_post_meta( $slide->ID, '_hssm_subtitle', true ),
|
||||
'content' => $slide->post_content,
|
||||
'buttons' => get_post_meta( $slide->ID, '_hssm_buttons', true ),
|
||||
'featured_image' => get_the_post_thumbnail_url( $slide->ID, 'full' ),
|
||||
'hide_all_text' => get_post_meta( $slide->ID, '_hssm_hide_all_text', true ) === '1',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a specific slide first in the slider.
|
||||
*
|
||||
* Pending and draft slides are only prepended when include_pending is true
|
||||
* (preview). The public slider stays publish + future.
|
||||
*
|
||||
* @since 1.1.3
|
||||
* @param array $slides Existing slides.
|
||||
* @param int $slide_id Focus slide ID.
|
||||
* @param bool $include_pending Whether pending/draft focus slides are allowed.
|
||||
* @return array
|
||||
*/
|
||||
private function prepend_focus_slide( $slides, $slide_id, $include_pending = false ) {
|
||||
foreach ( $slides as $index => $slide ) {
|
||||
if ( (int) $slide['id'] === (int) $slide_id ) {
|
||||
if ( 0 !== $index ) {
|
||||
$focus = $slides[ $index ];
|
||||
unset( $slides[ $index ] );
|
||||
array_unshift( $slides, $focus );
|
||||
}
|
||||
return array_values( $slides );
|
||||
}
|
||||
}
|
||||
|
||||
$post = get_post( $slide_id );
|
||||
if ( ! $post || 'hssm_slide' !== $post->post_type ) {
|
||||
return $slides;
|
||||
}
|
||||
|
||||
$allowed_statuses = array( 'publish', 'future' );
|
||||
if ( $include_pending ) {
|
||||
$allowed_statuses[] = 'pending';
|
||||
if ( current_user_can( 'edit_post', $slide_id ) ) {
|
||||
$allowed_statuses[] = 'draft';
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! in_array( $post->post_status, $allowed_statuses, true ) ) {
|
||||
return $slides;
|
||||
}
|
||||
|
||||
$formatted = $this->format_slide_for_slider( $slide_id );
|
||||
if ( $formatted ) {
|
||||
array_unshift( $slides, $formatted );
|
||||
}
|
||||
|
||||
return $slides;
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,25 @@ class HSSM_Slider {
|
||||
}
|
||||
|
||||
$hide_text = isset( $slide['hide_all_text'] ) && $slide['hide_all_text'];
|
||||
$valid_buttons = array();
|
||||
if ( ! empty( $slide['buttons'] ) && is_array( $slide['buttons'] ) ) {
|
||||
$valid_buttons = array_filter(
|
||||
$slide['buttons'],
|
||||
function( $button ) {
|
||||
return ! empty( $button['text'] ) && ! empty( $button['url'] );
|
||||
}
|
||||
);
|
||||
}
|
||||
$has_text = ! $hide_text && (
|
||||
! empty( $slide['title'] ) ||
|
||||
! empty( $slide['subtitle'] ) ||
|
||||
! empty( $slide['content'] ) ||
|
||||
! empty( $valid_buttons )
|
||||
);
|
||||
?>
|
||||
<div class="hssm-slide" data-index="<?php echo esc_attr( $index ); ?>">
|
||||
<div class="hssm-slide-inner <?php echo $hide_text ? 'hssm-hide-text' : ''; ?>" style="background-image: url('<?php echo esc_url( $bg_image ); ?>');">
|
||||
<?php if ( $has_text ) : ?>
|
||||
<div class="hssm-slide-content-wrapper">
|
||||
<div class="hssm-slide-content">
|
||||
<?php if ( ! empty( $slide['title'] ) ) : ?>
|
||||
@@ -70,13 +86,6 @@ class HSSM_Slider {
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $slide['buttons'] ) && is_array( $slide['buttons'] ) ) : ?>
|
||||
<?php
|
||||
// Filter out empty buttons
|
||||
$valid_buttons = array_filter($slide['buttons'], function($b) {
|
||||
return !empty($b['text']) && !empty($b['url']);
|
||||
});
|
||||
?>
|
||||
<?php if ( ! empty( $valid_buttons ) ) : ?>
|
||||
<div class="hssm-button-container">
|
||||
<?php foreach ( $valid_buttons as $button ) : ?>
|
||||
@@ -86,11 +95,11 @@ class HSSM_Slider {
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,14 +36,6 @@ class HSSM_Slides_CPT {
|
||||
*/
|
||||
const SITE_TAXONOMY = 'hssm_site';
|
||||
|
||||
/**
|
||||
* Client taxonomy slug
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var string
|
||||
*/
|
||||
const CLIENT_TAXONOMY = 'hssm_client';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -99,7 +91,7 @@ class HSSM_Slides_CPT {
|
||||
'publicly_queryable' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'query_var' => true,
|
||||
'query_var' => 'hssm_slide_item',
|
||||
'rewrite' => array( 'slug' => 'slide' ),
|
||||
'capability_type' => 'post',
|
||||
'has_archive' => true,
|
||||
@@ -147,33 +139,6 @@ class HSSM_Slides_CPT {
|
||||
|
||||
register_taxonomy( self::SITE_TAXONOMY, array( self::POST_TYPE ), $site_args );
|
||||
|
||||
// Client taxonomy for organizing slides by client (optional)
|
||||
$client_labels = array(
|
||||
'name' => _x( 'Clients', 'taxonomy general name', 'hi-school-slide-manager' ),
|
||||
'singular_name' => _x( 'Client', 'taxonomy singular name', 'hi-school-slide-manager' ),
|
||||
'search_items' => __( 'Search Clients', 'hi-school-slide-manager' ),
|
||||
'all_items' => __( 'All Clients', 'hi-school-slide-manager' ),
|
||||
'parent_item' => __( 'Parent Client', 'hi-school-slide-manager' ),
|
||||
'parent_item_colon' => __( 'Parent Client:', 'hi-school-slide-manager' ),
|
||||
'edit_item' => __( 'Edit Client', 'hi-school-slide-manager' ),
|
||||
'update_item' => __( 'Update Client', 'hi-school-slide-manager' ),
|
||||
'add_new_item' => __( 'Add New Client', 'hi-school-slide-manager' ),
|
||||
'new_item_name' => __( 'New Client Name', 'hi-school-slide-manager' ),
|
||||
'menu_name' => __( 'Clients', 'hi-school-slide-manager' ),
|
||||
);
|
||||
|
||||
$client_args = array(
|
||||
'hierarchical' => false,
|
||||
'labels' => $client_labels,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'slide-client' ),
|
||||
'show_in_rest' => true,
|
||||
);
|
||||
|
||||
register_taxonomy( self::CLIENT_TAXONOMY, array( self::POST_TYPE ), $client_args );
|
||||
|
||||
// Add default terms for the three companion sites
|
||||
$this->create_default_site_terms();
|
||||
}
|
||||
@@ -535,7 +500,6 @@ class HSSM_Slides_CPT {
|
||||
public function slide_targeting_meta_box( $post ) {
|
||||
// Get current terms
|
||||
$current_sites = wp_get_post_terms( $post->ID, self::SITE_TAXONOMY, array( 'fields' => 'ids' ) );
|
||||
$current_clients = wp_get_post_terms( $post->ID, self::CLIENT_TAXONOMY, array( 'fields' => 'ids' ) );
|
||||
|
||||
// Get all available site terms
|
||||
$site_terms = get_terms( array(
|
||||
@@ -543,12 +507,6 @@ class HSSM_Slides_CPT {
|
||||
'hide_empty' => false,
|
||||
) );
|
||||
|
||||
// Get all available client terms
|
||||
$client_terms = get_terms( array(
|
||||
'taxonomy' => self::CLIENT_TAXONOMY,
|
||||
'hide_empty' => false,
|
||||
) );
|
||||
|
||||
?>
|
||||
<div class="hssm-targeting-section">
|
||||
<p><strong><?php _e( 'Target Sites', 'hi-school-slide-manager' ); ?></strong></p>
|
||||
@@ -569,27 +527,6 @@ class HSSM_Slides_CPT {
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="hssm-targeting-section" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
||||
<p><strong><?php _e( 'Client (Optional)', 'hi-school-slide-manager' ); ?></strong></p>
|
||||
<?php if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) : ?>
|
||||
<select name="hssm_client" style="width: 100%;">
|
||||
<option value=""><?php _e( 'Select a client (optional)', 'hi-school-slide-manager' ); ?></option>
|
||||
<?php foreach ( $client_terms as $term ) : ?>
|
||||
<option value="<?php echo esc_attr( $term->term_id ); ?>" <?php selected( in_array( $term->term_id, $current_clients ) ); ?>>
|
||||
<?php echo esc_html( $term->name ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php else : ?>
|
||||
<p class="description">
|
||||
<?php _e( 'No clients available.', 'hi-school-slide-manager' ); ?>
|
||||
<a href="<?php echo admin_url( 'edit-tags.php?taxonomy=' . self::CLIENT_TAXONOMY . '&post_type=' . self::POST_TYPE ); ?>">
|
||||
<?php _e( 'Add clients here', 'hi-school-slide-manager' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hssm-targeting-section label {
|
||||
display: flex;
|
||||
@@ -682,14 +619,6 @@ class HSSM_Slides_CPT {
|
||||
wp_set_post_terms( $post_id, array(), self::SITE_TAXONOMY );
|
||||
}
|
||||
|
||||
// Save client taxonomy
|
||||
if ( isset( $_POST['hssm_client'] ) && ! empty( $_POST['hssm_client'] ) ) {
|
||||
$client_id = intval( $_POST['hssm_client'] );
|
||||
wp_set_post_terms( $post_id, array( $client_id ), self::CLIENT_TAXONOMY );
|
||||
} else {
|
||||
wp_set_post_terms( $post_id, array(), self::CLIENT_TAXONOMY );
|
||||
}
|
||||
|
||||
// Send email notification for new slides
|
||||
if ( get_post_status( $post_id ) === 'publish' && ! get_post_meta( $post_id, '_hssm_notification_sent', true ) ) {
|
||||
$this->send_new_slide_notification( $post_id );
|
||||
@@ -709,7 +638,6 @@ class HSSM_Slides_CPT {
|
||||
$new_columns['cb'] = $columns['cb'];
|
||||
$new_columns['title'] = $columns['title'];
|
||||
$new_columns['target_sites'] = __( 'Target Sites', 'hi-school-slide-manager' );
|
||||
$new_columns['client'] = __( 'Client', 'hi-school-slide-manager' );
|
||||
$new_columns['schedule'] = __( 'Schedule', 'hi-school-slide-manager' );
|
||||
$new_columns['order'] = __( 'Order', 'hi-school-slide-manager' );
|
||||
$new_columns['date'] = $columns['date'];
|
||||
@@ -741,15 +669,6 @@ class HSSM_Slides_CPT {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'client':
|
||||
$client_terms = wp_get_post_terms( $post_id, self::CLIENT_TAXONOMY );
|
||||
if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) {
|
||||
echo esc_html( $client_terms[0]->name );
|
||||
} else {
|
||||
echo '<em>' . __( 'No client', 'hi-school-slide-manager' ) . '</em>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'schedule':
|
||||
$start_date = get_post_meta( $post_id, '_hssm_start_date', true );
|
||||
$end_date = get_post_meta( $post_id, '_hssm_end_date', true );
|
||||
@@ -782,30 +701,9 @@ class HSSM_Slides_CPT {
|
||||
* @param int $post_id Post ID
|
||||
*/
|
||||
private function send_new_slide_notification( $post_id ) {
|
||||
$notification_emails = get_option( 'hssm_notification_emails', array() );
|
||||
|
||||
if ( empty( $notification_emails ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$subject = sprintf( __( 'New Slide Created: %s', 'hi-school-slide-manager' ), $post->post_title );
|
||||
|
||||
$message = sprintf(
|
||||
__( 'A new slide has been created and published on %s.' . "\n\n" .
|
||||
'Title: %s' . "\n" .
|
||||
'Content: %s' . "\n" .
|
||||
'Edit URL: %s', 'hi-school-slide-manager' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$post->post_title,
|
||||
wp_strip_all_tags( $post->post_content ),
|
||||
admin_url( 'post.php?post=' . $post_id . '&action=edit' )
|
||||
);
|
||||
|
||||
$headers = array( 'Content-Type: text/plain; charset=UTF-8' );
|
||||
|
||||
foreach ( $notification_emails as $email ) {
|
||||
wp_mail( $email, $subject, $message, $headers );
|
||||
if ( class_exists( 'HSSM_Notifications' ) ) {
|
||||
$notifications = new HSSM_Notifications();
|
||||
$notifications->notify_editor_slide_created( $post_id, 'published' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,8 +715,8 @@ class HSSM_Slides_CPT {
|
||||
public function check_scheduled_slides() {
|
||||
$current_time = current_time( 'mysql' );
|
||||
|
||||
// Auto-publish slides that should start now
|
||||
$slides_to_publish = get_posts( array(
|
||||
// WordPress publishes `future` posts. Cron only handles auto-publish drafts.
|
||||
$auto_publish = get_posts( array(
|
||||
'post_type' => self::POST_TYPE,
|
||||
'post_status' => 'draft',
|
||||
'meta_query' => array(
|
||||
@@ -826,21 +724,21 @@ class HSSM_Slides_CPT {
|
||||
array(
|
||||
'key' => '_hssm_auto_publish',
|
||||
'value' => '1',
|
||||
'compare' => '='
|
||||
'compare' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_start_date',
|
||||
'value' => $current_time,
|
||||
'compare' => '<='
|
||||
)
|
||||
'compare' => '<=',
|
||||
),
|
||||
'numberposts' => -1
|
||||
),
|
||||
'numberposts' => -1,
|
||||
) );
|
||||
|
||||
foreach ( $slides_to_publish as $slide ) {
|
||||
foreach ( $auto_publish as $slide ) {
|
||||
wp_update_post( array(
|
||||
'ID' => $slide->ID,
|
||||
'post_status' => 'publish'
|
||||
'post_status' => 'publish',
|
||||
) );
|
||||
}
|
||||
|
||||
@@ -870,6 +768,11 @@ class HSSM_Slides_CPT {
|
||||
'post_status' => 'draft'
|
||||
) );
|
||||
}
|
||||
|
||||
if ( class_exists( 'HSSM_Notifications' ) ) {
|
||||
$notifications = new HSSM_Notifications();
|
||||
$notifications->notify_due_live_slides();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,34 +15,34 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
||||
get_header();
|
||||
|
||||
$slide_id = isset( $_GET['slide_id'] ) ? intval( $_GET['slide_id'] ) : 0;
|
||||
// Note: We used a simplified token/hash check in the email link generation.
|
||||
// Ideally, we would verify that here.
|
||||
// For now, let's assume if they have the link, they can confirm (or we add basic checks).
|
||||
$slide_id = isset( $_REQUEST['slide_id'] ) ? absint( $_REQUEST['slide_id'] ) : 0;
|
||||
$token = isset( $_REQUEST['hssm_token'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['hssm_token'] ) ) : '';
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
$is_valid_slide = ( $slide && $slide->post_type === 'hssm_slide' );
|
||||
$confirmed = false;
|
||||
$slide = $slide_id ? get_post( $slide_id ) : null;
|
||||
$is_valid_slide = ( $slide && 'hssm_slide' === $slide->post_type );
|
||||
$has_valid_token = $is_valid_slide && class_exists( 'HSSM_Notifications' ) && HSSM_Notifications::is_valid_confirm_token( $slide_id, $token );
|
||||
$already_confirmed = $is_valid_slide && (bool) get_post_meta( $slide_id, '_hssm_live_confirmed', true );
|
||||
$confirmed = $already_confirmed;
|
||||
$error_message = '';
|
||||
|
||||
// Handle confirmation action
|
||||
if ( $is_valid_slide && isset( $_POST['confirm_live_action'] ) && $_POST['confirm_live_action'] === 'confirm' ) {
|
||||
if ( wp_verify_nonce( $_POST['hssm_live_nonce'], 'hssm_confirm_live_action' ) ) {
|
||||
// Trigger the final notification
|
||||
if ( class_exists( 'HSSM_Notifications' ) ) {
|
||||
if ( $is_valid_slide && ! $has_valid_token ) {
|
||||
$error_message = 'This confirmation link is invalid or expired.';
|
||||
}
|
||||
|
||||
if ( $has_valid_token && ! $already_confirmed && isset( $_POST['confirm_live_action'] ) && 'confirm' === $_POST['confirm_live_action'] ) {
|
||||
if ( isset( $_POST['hssm_live_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['hssm_live_nonce'] ) ), 'hssm_confirm_live_action' ) ) {
|
||||
$notifications = new HSSM_Notifications();
|
||||
$notifications->notify_all_confirmed_live( $slide_id );
|
||||
|
||||
// Mark as confirmed in metadata
|
||||
update_post_meta( $slide_id, '_hssm_live_confirmed', current_time( 'timestamp' ) );
|
||||
$confirmed = true;
|
||||
} else {
|
||||
$error_message = 'Notification system not available.';
|
||||
$confirmed = (bool) get_post_meta( $slide_id, '_hssm_live_confirmed', true );
|
||||
if ( ! $confirmed ) {
|
||||
$error_message = 'Could not send the confirmation email. Please try again.';
|
||||
}
|
||||
} else {
|
||||
$error_message = 'Security check failed. Please refresh and try again.';
|
||||
}
|
||||
}
|
||||
|
||||
$thumbnail = $is_valid_slide ? get_the_post_thumbnail_url( $slide_id, 'medium' ) : '';
|
||||
?>
|
||||
|
||||
<div id="main-content">
|
||||
@@ -55,16 +55,23 @@ if ( $is_valid_slide && isset( $_POST['confirm_live_action'] ) && $_POST['confir
|
||||
<div class="hssm-error">
|
||||
<h2>Invalid Slide</h2>
|
||||
<p>The slide you are trying to confirm could not be found.</p>
|
||||
<a href="<?php echo home_url(); ?>" class="button">Return Home</a>
|
||||
<a href="<?php echo esc_url( home_url() ); ?>" class="button">Return Home</a>
|
||||
</div>
|
||||
|
||||
<?php elseif ( ! $has_valid_token ) : ?>
|
||||
<div class="hssm-error">
|
||||
<h2>Invalid Confirmation Link</h2>
|
||||
<p><?php echo esc_html( $error_message ); ?></p>
|
||||
<a href="<?php echo esc_url( home_url() ); ?>" class="button">Return Home</a>
|
||||
</div>
|
||||
|
||||
<?php elseif ( $confirmed ) : ?>
|
||||
<div class="hssm-success">
|
||||
<h2 style="color: #46b450;">Confirmation Sent!</h2>
|
||||
<h2 style="color: #46b450;"><?php echo $already_confirmed && empty( $_POST['confirm_live_action'] ) ? 'Already Confirmed' : 'Confirmation Sent!'; ?></h2>
|
||||
<p class="dashicons dashicons-yes-alt" style="font-size: 64px; width: 64px; height: 64px; color: #46b450;"></p>
|
||||
<p><strong>Thank you!</strong></p>
|
||||
<p>Notifications have been sent to the Editor and Creator confirming that "<?php echo esc_html( $slide->post_title ); ?>" is Live.</p>
|
||||
<a href="<?php echo home_url(); ?>" class="button">Return Home</a>
|
||||
<a href="<?php echo esc_url( home_url() ); ?>" class="button">Return Home</a>
|
||||
</div>
|
||||
|
||||
<?php else : ?>
|
||||
@@ -81,19 +88,23 @@ if ( $is_valid_slide && isset( $_POST['confirm_live_action'] ) && $_POST['confir
|
||||
<h3 style="margin-top: 0;"><?php echo esc_html( $slide->post_title ); ?></h3>
|
||||
<?php
|
||||
$target_sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'names' ) );
|
||||
if ( ! empty( $target_sites ) ) {
|
||||
if ( ! empty( $target_sites ) && ! is_wp_error( $target_sites ) ) {
|
||||
echo '<p><strong>Target Sites:</strong> ' . esc_html( implode( ', ', $target_sites ) ) . '</p>';
|
||||
}
|
||||
if ( $thumbnail ) :
|
||||
?>
|
||||
<div style="margin-top: 15px;">
|
||||
<img src="<?php echo get_the_post_thumbnail_url( $slide_id, 'medium' ); ?>" style="max-width: 100%; height: auto; border-radius: 4px;">
|
||||
<img src="<?php echo esc_url( $thumbnail ); ?>" alt="" style="max-width: 100%; height: auto; border-radius: 4px;">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p>I confirm that the slide <strong>"<?php echo esc_html( $slide->post_title ); ?>"</strong> is Live and displaying correctly on the target sites.</p>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="confirm_live_action" value="confirm">
|
||||
<input type="hidden" name="slide_id" value="<?php echo esc_attr( $slide_id ); ?>">
|
||||
<input type="hidden" name="hssm_token" value="<?php echo esc_attr( $token ); ?>">
|
||||
<?php wp_nonce_field( 'hssm_confirm_live_action', 'hssm_live_nonce' ); ?>
|
||||
|
||||
<button type="submit" class="button" style="background-color: #46b450; color: #fff; border: none; padding: 15px 30px; font-size: 18px; cursor: pointer; border-radius: 4px;">
|
||||
|
||||
@@ -37,7 +37,7 @@ get_header(); ?>
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
Admin
|
||||
</a>
|
||||
<a href="<?php echo home_url( '/slide-preview/' ); ?>" class="hssm-nav-btn hssm-nav-preview" target="_blank">
|
||||
<a href="<?php echo esc_url( hssm_get_page_url_by_template( 'page-slide-preview.php', '/slide-preview/' ) ); ?>" class="hssm-nav-btn hssm-nav-preview" target="_blank">
|
||||
<span class="dashicons dashicons-visibility"></span>
|
||||
Slide Preview
|
||||
</a>
|
||||
@@ -109,12 +109,6 @@ get_header(); ?>
|
||||
</div>
|
||||
|
||||
<div class="hssm-bulk-actions">
|
||||
<button id="save-all-slides" class="hssm-btn hssm-btn-success hssm-btn-large">
|
||||
<span class="dashicons dashicons-yes"></span> Save All Slides
|
||||
</button>
|
||||
<button id="save-draft-all" class="hssm-btn hssm-btn-secondary">
|
||||
<span class="dashicons dashicons-edit"></span> Save as Drafts
|
||||
</button>
|
||||
<button id="clear-all-forms" class="hssm-btn hssm-btn-outline">
|
||||
<span class="dashicons dashicons-trash"></span> Clear All Forms
|
||||
</button>
|
||||
@@ -128,22 +122,20 @@ get_header(); ?>
|
||||
<div class="hssm-filters">
|
||||
<select id="filter-site" class="hssm-select">
|
||||
<option value="">All Sites</option>
|
||||
<option value="hi-school">Hi-School Pharmacy</option>
|
||||
<option value="one-stop">One Stop Hardware</option>
|
||||
<option value="ace">Ace Hardware</option>
|
||||
</select>
|
||||
<select id="filter-client" class="hssm-select">
|
||||
<option value="">All Clients</option>
|
||||
</select>
|
||||
<select id="filter-status" class="hssm-select">
|
||||
<option value="">All Status</option>
|
||||
<option value="publish">Published</option>
|
||||
<option value="draft">Draft</option>
|
||||
<option value="pending">Pending Approval</option>
|
||||
<option value="scheduled">Scheduled</option>
|
||||
</select>
|
||||
<button id="apply-filters" class="hssm-btn hssm-btn-primary">
|
||||
<span class="dashicons dashicons-filter"></span> Apply Filters
|
||||
</button>
|
||||
<button id="clear-filters" class="hssm-btn hssm-btn-outline">
|
||||
Clear
|
||||
</button>
|
||||
<button id="export-slides" class="hssm-btn hssm-btn-secondary">
|
||||
<span class="dashicons dashicons-download"></span> Export
|
||||
</button>
|
||||
@@ -229,141 +221,4 @@ get_header(); ?>
|
||||
</div> <!-- .container-fluid -->
|
||||
</div> <!-- #main-content -->
|
||||
|
||||
<!-- Slide Form Template (Hidden) -->
|
||||
<script type="text/html" id="slide-form-template">
|
||||
<div class="hssm-slide-form" data-form-id="{{form_id}}">
|
||||
<div class="hssm-form-header">
|
||||
<h3><strong>New Slide #{{form_number}}</strong></h3>
|
||||
<button type="button" class="hssm-remove-form" aria-label="Remove this form">
|
||||
<span class="dashicons dashicons-no"> test</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="hssm-form-grid">
|
||||
<!-- Left Column: Basic Information -->
|
||||
<div class="hssm-form-section hssm-form-basic">
|
||||
<h4>Basic Information</h4>
|
||||
|
||||
<div class="hssm-field-row">
|
||||
<div class="hssm-field">
|
||||
<label for="title_{{form_id}}">Title *</label>
|
||||
<input type="text" id="title_{{form_id}}" name="title" class="hssm-input" required>
|
||||
</div>
|
||||
<div class="hssm-field">
|
||||
<label for="client_{{form_id}}">Client</label>
|
||||
<input type="text" id="client_{{form_id}}" name="client" class="hssm-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hssm-field">
|
||||
<label for="subtitle_{{form_id}}">Subtitle</label>
|
||||
<input type="text" id="subtitle_{{form_id}}" name="subtitle" class="hssm-input">
|
||||
</div>
|
||||
|
||||
<div class="hssm-field">
|
||||
<label for="content_{{form_id}}">Content</label>
|
||||
<textarea id="content_{{form_id}}" name="content" class="hssm-textarea" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Center Column: Image & Buttons -->
|
||||
<div class="hssm-form-section hssm-form-media">
|
||||
<h4>Background Image</h4>
|
||||
|
||||
<div class="hssm-image-upload-compact">
|
||||
<div class="hssm-image-preview-small" id="image_preview_{{form_id}}">
|
||||
<span class="hssm-placeholder">Click to upload</span>
|
||||
</div>
|
||||
<div class="hssm-image-controls">
|
||||
<input type="file" id="image_upload_{{form_id}}" name="background_image" accept="image/*" style="display: none;">
|
||||
<input type="hidden" id="image_id_{{form_id}}" name="featured_image_id" value="">
|
||||
<button type="button" class="hssm-btn hssm-btn-small hssm-upload-btn" data-target="{{form_id}}">
|
||||
Choose Image
|
||||
</button>
|
||||
<button type="button" class="hssm-btn hssm-btn-small hssm-btn-text hssm-remove-image" data-target="{{form_id}}" style="display: none;">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Call-to-Action Buttons</h4>
|
||||
<div class="hssm-buttons-container-compact" id="buttons_{{form_id}}">
|
||||
<div class="hssm-button-row-compact">
|
||||
<input type="text" name="buttons[0][text]" placeholder="Button Text" class="hssm-input-small">
|
||||
<input type="url" name="buttons[0][url]" placeholder="Button URL" class="hssm-input-small">
|
||||
<button type="button" class="hssm-btn hssm-btn-tiny hssm-remove-button">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="hssm-btn hssm-btn-small hssm-add-button" data-form="{{form_id}}">
|
||||
<span class="dashicons dashicons-plus"></span> Add Button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Targeting & Scheduling -->
|
||||
<div class="hssm-form-section hssm-form-targeting">
|
||||
<h4>Targeting & Scheduling</h4>
|
||||
|
||||
<div class="hssm-field">
|
||||
<label>Target Sites *</label>
|
||||
<div class="hssm-checkboxes-compact">
|
||||
<label class="hssm-checkbox-compact">
|
||||
<input type="checkbox" name="target_sites[]" value="hi-school">
|
||||
Hi-School Pharmacy
|
||||
</label>
|
||||
<label class="hssm-checkbox-compact">
|
||||
<input type="checkbox" name="target_sites[]" value="one-stop">
|
||||
One Stop Hardware
|
||||
</label>
|
||||
<label class="hssm-checkbox-compact">
|
||||
<input type="checkbox" name="target_sites[]" value="ace">
|
||||
Ace Hardware
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hssm-field" style="margin-top: 15px;">
|
||||
<label class="hssm-checkbox-compact hssm-hide-text-option">
|
||||
<input type="checkbox" name="hide_all_text" value="1">
|
||||
Hide All Text (Image Only)
|
||||
<small style="display: block; color: #666; margin-top: 5px;">Check this to display only the image without any overlay text</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="hssm-field-row">
|
||||
<div class="hssm-field">
|
||||
<label for="start_date_{{form_id}}">Start Date</label>
|
||||
<input type="date" id="start_date_{{form_id}}" name="start_date" class="hssm-input">
|
||||
</div>
|
||||
<div class="hssm-field">
|
||||
<label for="end_date_{{form_id}}">End Date</label>
|
||||
<input type="date" id="end_date_{{form_id}}" name="end_date" class="hssm-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hssm-field-row">
|
||||
<div class="hssm-field">
|
||||
<label for="order_{{form_id}}">Display Order</label>
|
||||
<input type="number" id="order_{{form_id}}" name="order" class="hssm-input" value="0" min="0">
|
||||
</div>
|
||||
<div class="hssm-field">
|
||||
<!-- Spacer for alignment -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hssm-form-actions">
|
||||
<button type="button" class="hssm-btn hssm-btn-secondary hssm-schedule-slide" data-form="{{form_id}}">
|
||||
<span class="dashicons dashicons-calendar-alt"></span> Schedule for Approval
|
||||
</button>
|
||||
<button type="button" class="hssm-btn hssm-btn-primary hssm-publish-slide" data-form="{{form_id}}">
|
||||
<span class="dashicons dashicons-yes"></span> Publish Immediately
|
||||
</button>
|
||||
<button type="button" class="hssm-btn hssm-btn-outline hssm-preview-slide" data-form="{{form_id}}">
|
||||
<span class="dashicons dashicons-visibility"></span> Preview
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<?php get_footer(); ?>
|
||||
@@ -108,6 +108,33 @@ function hssm_get_preview_request() {
|
||||
$date_param = isset( $_GET['hssm_date'] ) ? sanitize_text_field( wp_unslash( $_GET['hssm_date'] ) ) : $today->format( 'Y-m-d' );
|
||||
$site_param = isset( $_GET['hssm_site'] ) ? sanitize_text_field( wp_unslash( $_GET['hssm_site'] ) ) : '';
|
||||
$show_past = isset( $_GET['hssm_past'] ) && $_GET['hssm_past'] === '1';
|
||||
$slide_id = 0;
|
||||
if ( isset( $_GET['hssm_preview'] ) ) {
|
||||
$slide_id = absint( $_GET['hssm_preview'] );
|
||||
} elseif ( isset( $_GET['hssm_slide'] ) ) {
|
||||
$slide_id = absint( $_GET['hssm_slide'] );
|
||||
}
|
||||
|
||||
if ( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( $slide && 'hssm_slide' === $slide->post_type ) {
|
||||
if ( empty( $_GET['hssm_date'] ) ) {
|
||||
$start_date = get_post_meta( $slide_id, '_hssm_start_date', true );
|
||||
if ( $start_date && preg_match( '/^(\d{4}-\d{2}-\d{2})/', $start_date, $matches ) ) {
|
||||
$date_param = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $site_param ) ) {
|
||||
$site_terms = wp_get_post_terms( $slide_id, HSSM_Slides_CPT::SITE_TAXONOMY );
|
||||
if ( ! is_wp_error( $site_terms ) && ! empty( $site_terms ) ) {
|
||||
$site_param = $site_terms[0]->slug;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$slide_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate date (YYYY-MM-DD)
|
||||
$date_obj = DateTime::createFromFormat( 'Y-m-d', $date_param );
|
||||
@@ -119,6 +146,7 @@ function hssm_get_preview_request() {
|
||||
'date' => $date_obj->format( 'Y-m-d' ),
|
||||
'site_slug' => $site_param,
|
||||
'show_past' => $show_past,
|
||||
'slide_id' => $slide_id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -317,6 +345,11 @@ function hssm_get_preview_slides_for_date( $date, $site_slug = '' ) {
|
||||
$request = hssm_get_preview_request();
|
||||
$sites = hssm_get_preview_sites();
|
||||
$events = hssm_get_calendar_events( $request['site_slug'] );
|
||||
$can_approve = current_user_can( 'edit_posts' );
|
||||
$focused_slide = $request['slide_id'] ? get_post( $request['slide_id'] ) : null;
|
||||
if ( $focused_slide && 'hssm_slide' !== $focused_slide->post_type ) {
|
||||
$focused_slide = null;
|
||||
}
|
||||
|
||||
$current_site_name = '';
|
||||
if ( ! empty( $request['site_slug'] ) ) {
|
||||
@@ -341,23 +374,69 @@ if ( empty( $current_site_name ) && ! empty( $sites ) ) {
|
||||
<div class="container-fluid">
|
||||
<div id="content-area" class="clearfix full-width">
|
||||
|
||||
<div class="hssm-header-bar">
|
||||
<div class="hssm-header-content">
|
||||
<div class="hssm-logo-section">
|
||||
<h1 class="hssm-site-title"><?php esc_html_e( 'Slide Preview', 'hi-school-slide-manager' ); ?></h1>
|
||||
</div>
|
||||
<div class="hssm-header-nav">
|
||||
<a href="<?php echo esc_url( hssm_get_page_url_by_template( 'page-slide-dashboard.php', '/slide-dashboard/' ) ); ?>" class="hssm-nav-btn hssm-nav-home">
|
||||
<span class="dashicons dashicons-admin-home"></span>
|
||||
<?php esc_html_e( 'Dashboard', 'hi-school-slide-manager' ); ?>
|
||||
</a>
|
||||
<a href="<?php echo esc_url( admin_url() ); ?>" class="hssm-nav-btn hssm-nav-admin">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<?php esc_html_e( 'Admin', 'hi-school-slide-manager' ); ?>
|
||||
</a>
|
||||
<div class="hssm-user-menu">
|
||||
<span class="hssm-user-name"><?php echo esc_html( wp_get_current_user()->display_name ); ?></span>
|
||||
<a href="<?php echo esc_url( wp_logout_url( home_url() ) ); ?>" class="hssm-nav-btn hssm-nav-logout">
|
||||
<span class="dashicons dashicons-exit"></span>
|
||||
<?php esc_html_e( 'Logout', 'hi-school-slide-manager' ); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Render the live slider preview using the modular shortcode
|
||||
echo do_shortcode( sprintf(
|
||||
'[hssm_slider site="%s" date="%s"]',
|
||||
'[hssm_slider site="%s" date="%s" slide="%d" include_pending="1"]',
|
||||
esc_attr( $request['site_slug'] ),
|
||||
esc_attr( $request['date'] )
|
||||
esc_attr( $request['date'] ),
|
||||
absint( $request['slide_id'] )
|
||||
) );
|
||||
|
||||
// Normal page content (Divi builder, header, etc.).
|
||||
if ( have_posts() ) :
|
||||
while ( have_posts() ) :
|
||||
the_post();
|
||||
// the_content(); // We might not want the_content if we're replacing it with the shortcode,
|
||||
// but let's keep it in case there's other content.
|
||||
endwhile;
|
||||
endif;
|
||||
if ( $focused_slide && 'pending' === $focused_slide->post_status && $can_approve ) :
|
||||
$focus_start = get_post_meta( $focused_slide->ID, '_hssm_start_date', true );
|
||||
$start_ts = $focus_start ? strtotime( $focus_start ) : false;
|
||||
$is_future = $start_ts && $start_ts > current_time( 'timestamp' );
|
||||
?>
|
||||
<div class="hssm-preview-approve-bar">
|
||||
<div class="hssm-preview-approve-copy">
|
||||
<strong><?php echo esc_html( get_the_title( $focused_slide ) ); ?></strong>
|
||||
<span><?php esc_html_e( 'This slide is pending approval.', 'hi-school-slide-manager' ); ?></span>
|
||||
</div>
|
||||
<div class="hssm-preview-approve-actions">
|
||||
<?php if ( $is_future ) : ?>
|
||||
<button type="button" class="hssm-btn hssm-btn-success hssm-approve-slide" data-id="<?php echo esc_attr( $focused_slide->ID ); ?>" data-approve-action="schedule">
|
||||
<span class="dashicons dashicons-yes"></span>
|
||||
<?php esc_html_e( 'Approve & Schedule', 'hi-school-slide-manager' ); ?>
|
||||
</button>
|
||||
<button type="button" class="hssm-btn hssm-btn-primary hssm-approve-slide" data-id="<?php echo esc_attr( $focused_slide->ID ); ?>" data-approve-action="publish">
|
||||
<span class="dashicons dashicons-upload"></span>
|
||||
<?php esc_html_e( 'Approve & Publish Now', 'hi-school-slide-manager' ); ?>
|
||||
</button>
|
||||
<?php else : ?>
|
||||
<button type="button" class="hssm-btn hssm-btn-success hssm-approve-slide" data-id="<?php echo esc_attr( $focused_slide->ID ); ?>" data-approve-action="publish">
|
||||
<span class="dashicons dashicons-yes"></span>
|
||||
<?php esc_html_e( 'Approve & Publish', 'hi-school-slide-manager' ); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="hssm-dashboard-main hssm-mt-20">
|
||||
<div class="hssm-tab-header">
|
||||
@@ -374,6 +453,9 @@ if ( empty( $current_site_name ) && ! empty( $sites ) ) {
|
||||
</div>
|
||||
|
||||
<form method="get" class="hssm-filters hssm-mt-20">
|
||||
<?php if ( ! empty( $request['slide_id'] ) ) : ?>
|
||||
<input type="hidden" name="hssm_preview" value="<?php echo esc_attr( $request['slide_id'] ); ?>" />
|
||||
<?php endif; ?>
|
||||
<div>
|
||||
<label for="hssm_date"><strong><?php esc_html_e( 'Preview Date', 'hi-school-slide-manager' ); ?></strong></label><br>
|
||||
<input type="text" id="hssm_date" name="hssm_date" class="hssm-input" value="<?php echo esc_attr( $request['date'] ); ?>" autocomplete="off" />
|
||||
@@ -412,6 +494,23 @@ if ( empty( $current_site_name ) && ! empty( $sites ) ) {
|
||||
<?php
|
||||
$slides = hssm_get_preview_slides_for_date( $request['date'], $request['site_slug'] );
|
||||
|
||||
if ( ! empty( $request['slide_id'] ) ) {
|
||||
$has_focus = false;
|
||||
foreach ( $slides as $existing_slide ) {
|
||||
if ( (int) $existing_slide->ID === (int) $request['slide_id'] ) {
|
||||
$has_focus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $has_focus ) {
|
||||
$focus_slide = get_post( $request['slide_id'] );
|
||||
if ( $focus_slide && 'hssm_slide' === $focus_slide->post_type ) {
|
||||
array_unshift( $slides, $focus_slide );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $slides ) ) :
|
||||
?>
|
||||
<div class="hssm-empty-state">
|
||||
@@ -434,7 +533,7 @@ if ( empty( $current_site_name ) && ! empty( $sites ) ) {
|
||||
}
|
||||
$thumb_url = get_the_post_thumbnail_url( $slide->ID, 'thumbnail' );
|
||||
?>
|
||||
<div class="hssm-slide-item">
|
||||
<div class="hssm-slide-item<?php echo ( (int) $slide->ID === (int) $request['slide_id'] ) ? ' hssm-slide-item-focus' : ''; ?>">
|
||||
<div class="hssm-slide-info">
|
||||
<div class="hssm-list-slide-title">
|
||||
<?php echo esc_html( get_the_title( $slide ) ); ?>
|
||||
@@ -467,9 +566,9 @@ if ( empty( $current_site_name ) && ! empty( $sites ) ) {
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ( $slide->post_status === 'pending' && current_user_can( 'edit_others_posts' ) ) : ?>
|
||||
<?php if ( $slide->post_status === 'pending' && $can_approve ) : ?>
|
||||
<div class="hssm-slide-actions hssm-mt-10">
|
||||
<button type="button" class="hssm-btn hssm-btn-success hssm-approve-slide" data-id="<?php echo esc_attr( $slide->ID ); ?>">
|
||||
<button type="button" class="hssm-btn hssm-btn-success hssm-approve-slide" data-id="<?php echo esc_attr( $slide->ID ); ?>" data-approve-action="schedule">
|
||||
<span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Approve Slide', 'hi-school-slide-manager' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user