audit clean-up

add 'no dates' logic
This commit is contained in:
2026-02-22 19:05:49 -08:00
parent 999c2cc866
commit 9bb2171b92
9 changed files with 291 additions and 76 deletions
+190
View File
@@ -0,0 +1,190 @@
# Hi-School Slide Manager — Plugin Audit Report
**Date:** February 10, 2025
**Context:** WordPress site using **Divi 5** theme
**Plugin version:** 1.1.1
---
## Executive summary
The plugin is well-structured with clear separation (CPT, REST API, Admin, Frontend, Shortcode, Slider). Security basics are in place (nonces, capability checks, sanitization). Several issues should be fixed: a missing REST controller class, unsafe REST API image handling, unprofessional file in the package, production `error_log` usage, a broken API key delete UI, and incomplete uninstall. Divi 5 compatibility is likely fine but should be confirmed in your environment.
---
## 1. Critical issues
### 1.1 Missing REST controller class (CPT)
**File:** `includes/class-hssm-slides-cpt.php` (line 113)
The slides CPT is registered with:
```php
'rest_controller_class' => 'HSSM_REST_Slides_Controller',
```
The class `HSSM_REST_Slides_Controller` is **not defined** anywhere in the plugin. WordPress may throw a fatal error or warning when the REST API is used for this post type, or fall back to default behavior unpredictably.
**Recommendation:** Either:
- Remove `rest_controller_class` and `rest_base` to use the default `WP_REST_Posts_Controller`, or
- Implement `HSSM_REST_Slides_Controller` extending `WP_REST_Posts_Controller` and register it (and ensure the class is loaded before `register_post_type`).
---
### 1.2 REST API — possible PHP notice/error in image sizes
**File:** `includes/class-hssm-rest-api.php` (lines 386391)
In `get_slide_background_image()`, the `sizes` array is built by calling `wp_get_attachment_image_src( $thumbnail_id, 'large' )[0]` (and similarly for `medium`, `thumbnail`). If a size does not exist (e.g. image too small), `wp_get_attachment_image_src()` returns `false`, and `[0]` on `false` can cause a notice or error.
**Recommendation:** For each size, check the return value before using `[0]`, and only add the URL when the result is valid (e.g. use a helper that returns `$src ? $src[0] : ''` or omit the key).
---
### 1.3 API key delete — wrong key can be deleted
**File:** `includes/class-hssm-admin.php` (api_keys_field)
Each API key row outputs:
- A “Delete” submit button (same `name="delete_api_key" value="1"` for all rows).
- A hidden input `name="api_key_index" value="<?php echo $index; ?>"`.
With multiple keys, there are multiple `api_key_index` inputs. On submit, PHP receives a single `$_POST['api_key_index']`; with multiple fields with the same name, the value is often the **last** one in the form. So clicking “Delete” on the first key can submit the index of the last key and delete the wrong key.
**Recommendation:** Use one hidden input for `api_key_index` and set its value via JavaScript when the user clicks a specific rows Delete button. Alternatively, use a separate form per row or a single delete endpoint that takes the key index as a parameter (with nonce and capability checks).
---
## 2. Security and best practices
### 2.1 Good practices observed
- **Direct access:** All PHP files guard with `ABSPATH` check.
- **Nonces:** Admin settings and AJAX use nonces (`hssm_settings`, `hssm_dashboard_nonce`, `hssm_update_slide_order`).
- **Capabilities:** Settings require `manage_options`; slide actions use `edit_posts`, `delete_post`, `edit_post`, `upload_files`, etc., as appropriate.
- **Sanitization:** Inputs sanitized with `sanitize_text_field`, `esc_url_raw`, `wp_kses_post`, `absint`, etc.
- **Escaping in output:** Templates and admin use `esc_html`, `esc_attr`, `esc_url`, `esc_js` where needed.
- **REST API:** Protected endpoints use a custom permission callback and API key validation; parameters are validated and sanitized.
### 2.2 Public AJAX test endpoint
**File:** `includes/class-hssm-frontend.php`
```php
add_action( 'wp_ajax_nopriv_hssm_test', array( $this, 'ajax_test' ) );
```
`ajax_test()` only returns a success message, but it is callable by unauthenticated users. It is unnecessary in production and could be used to probe for the plugin.
**Recommendation:** Remove the `wp_ajax_nopriv_hssm_test` registration (or restrict it to `WP_DEBUG` / admin-only) before production.
### 2.3 AJAX handlers and `$_POST` / `$_FILES`
Some handlers assume `$_POST['nonce']` or other keys exist (e.g. `ajax_upload_image`, `ajax_save_slide`, `ajax_preview_slides`). If a request is sent without the nonce, `wp_verify_nonce( $_POST['nonce'], ... )` can trigger an “undefined index” notice.
**Recommendation:** Use `isset( $_POST['nonce'] )` (and similar for other keys) before using them, and return a clear error response when required parameters are missing.
### 2.4 Settings form and `options.php`
The settings form uses `action=""` and does not use `options.php`. Saving is handled in `handle_settings_save()` on POST. Thats valid, but ensure all saved options are registered with `register_setting` and that no extra options are written without sanitization (currently looks consistent).
---
## 3. Uninstall and cleanup
**File:** `includes/class-hssm-main.php``remove_plugin_options()`
Removed options are:
- `hssm_notification_emails`, `hssm_default_slide_duration`, `hssm_cache_duration`, `hssm_api_version`, `hssm_preview_page_slug`, `hssm_manager_page_slug`, `hssm_api_keys`
Not removed:
- `hssm_plugin_mode`
- `hssm_api_url`
- `hssm_client_site_slug`
- `hssm_notification_settings` (used by `HSSM_Notifications::get_settings()`)
**Recommendation:** Add these option names to the list in `remove_plugin_options()` so uninstall leaves no HSSM options behind (and document that uninstall removes all plugin data).
---
## 4. Code quality and maintainability
### 4.1 Production `error_log` usage
**File:** `includes/class-hssm-frontend.php` (and possibly others)
Multiple `error_log()` calls are used for debugging (e.g. “HSSM: AJAX handlers registered”, “Raw form data received”, “Validation failed”, “Schedule slide AJAX called”). These run for every request/hit and can clutter logs and leak internal details.
**Recommendation:** Remove or wrap all `error_log()` calls in `if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG )` (or a small helper) so they only run when debug logging is enabled.
### 4.2 Duplicate / redundant comment
**File:** `includes/class-hssm-frontend.php`
There are two consecutive docblocks for “AJAX handler for image uploads” (around `ajax_upload_image` and before `sanitize_slide_data`). One is redundant and can be removed for clarity.
### 4.3 REST API date filter vs. status
**File:** `includes/class-hssm-rest-api.php``get_slides_for_site()`
When `$status === 'scheduled'` or `'all'`, the existing `meta_query` built for `$date` can be overwritten. If both `date` and `status` are passed, the date filter may not apply in those branches. Worth a quick logic review so date and status filters compose as intended.
---
## 5. Files and packaging
### 5.1 Unprofessional file in plugin directory
**File:** `fuck-you-google.html` (root of plugin)
This file exists in the plugin directory (content is empty). The name is unprofessional and should not be distributed or deployed.
**Recommendation:** Delete this file from the plugin and from any repo or deployment package.
### 5.2 Development / internal docs
The plugin contains internal docs such as `FRONTEND_SETUP.md`, `mop-up.md`, `Notifications.md`, `prompt-builder-reference-and-notes.md`, and `mimic-carousel.html`. These are fine for development but could be excluded from production builds or moved to a `/docs` folder so the plugin root stays clean.
---
## 6. Divi 5 compatibility
- The plugin uses standard WordPress mechanisms: shortcode `[hssm_slider]`, page templates (with fallback to plugin templates), `get_header()` in templates, and normal enqueue/localize for CSS/JS. None of this is Divi-specific.
- Divi 5 continues to support shortcodes and standard theme hooks, so **the plugin is expected to work with Divi 5**.
- **Recommendation:** Manually test on a Divi 5 site:
- Place `[hssm_slider]` in a Divi layout (e.g. Code module or shortcode module).
- Use the “Slide Dashboard” and “Slide Preview” page templates and confirm layout and scripts (e.g. datepicker, slider) behave correctly.
- If Divi 5 uses different wrapper classes or JS namespaces, minor CSS/JS tweaks might be needed; no such issues were evident from code review alone.
---
## 7. Summary checklist
| Area | Status | Notes |
|-------------------|--------|--------|
| Security (nonces, caps, sanitize/escape) | OK | A few improvements recommended (nopriv test, isset checks). |
| REST API | Fix | Missing CPT REST controller; image sizes may error. |
| Admin settings | Fix | API key delete can delete wrong key. |
| Uninstall | Fix | Add missing options to cleanup. |
| Logging | Fix | Restrict or remove `error_log` in production. |
| Packaging | Fix | Remove `fuck-you-google.html`. |
| Divi 5 | Verify | Expected to work; confirm with testing. |
---
## 8. Recommended fix order
1. **Immediate:** Remove `fuck-you-google.html` from the plugin directory.
2. **High:** Fix or remove `rest_controller_class` for the slides CPT (implement or drop custom REST controller).
3. **High:** Harden `get_slide_background_image()` so missing image sizes never cause notices/errors.
4. **High:** Fix API key delete so the correct key is removed (single hidden + JS or per-row form).
5. **Medium:** Remove or restrict `wp_ajax_nopriv_hssm_test` and add `isset()` checks for AJAX parameters.
6. **Medium:** Add all HSSM options to uninstall cleanup; wrap or remove `error_log()` for production.
7. **Low:** Clean duplicate docblock and optional doc/file organization.
If you want, I can suggest concrete code changes (diffs) for any of these items next.
+4 -18
View File
@@ -221,11 +221,11 @@
<div class="hssm-field-row"> <div class="hssm-field-row">
<div class="hssm-field"> <div class="hssm-field">
<label>Start Date</label> <label>Start Date</label>
<input type="date" name="slides[${this.formCounter}][start_date]" id="start_date_${this.formCounter}" class="hssm-input" required> <input type="date" name="slides[${this.formCounter}][start_date]" id="start_date_${this.formCounter}" class="hssm-input">
</div> </div>
<div class="hssm-field"> <div class="hssm-field">
<label>End Date</label> <label>End Date</label>
<input type="date" name="slides[${this.formCounter}][end_date]" id="end_date_${this.formCounter}" class="hssm-input" required> <input type="date" name="slides[${this.formCounter}][end_date]" id="end_date_${this.formCounter}" class="hssm-input">
</div> </div>
</div> </div>
@@ -559,20 +559,6 @@
this.showFieldError($titleField, 'Title is required'); this.showFieldError($titleField, 'Title is required');
hasErrors = true; hasErrors = true;
} }
// Validate start date
if (!startDate) {
const $startDateField = $form.find('input[name*="[start_date]"]');
this.showFieldError($startDateField, 'Start Date is required');
hasErrors = true;
}
// Validate end date
if (!endDate) {
const $endDateField = $form.find('input[name*="[end_date]"]');
this.showFieldError($endDateField, 'End Date is required');
hasErrors = true;
}
// Validate target sites - check all checkbox formats // Validate target sites - check all checkbox formats
if (targetSitesCount === 0) { if (targetSitesCount === 0) {
@@ -1058,11 +1044,11 @@
<div class="hssm-field-row"> <div class="hssm-field-row">
<div class="hssm-field"> <div class="hssm-field">
<label>Start Date</label> <label>Start Date</label>
<input type="date" name="slides[${this.formCounter}][start_date]" id="start_date_${this.formCounter}" class="hssm-input" value="${slideData.start_date || ''}" required> <input type="date" name="slides[${this.formCounter}][start_date]" id="start_date_${this.formCounter}" class="hssm-input" value="${slideData.start_date || ''}">
</div> </div>
<div class="hssm-field"> <div class="hssm-field">
<label>End Date</label> <label>End Date</label>
<input type="date" name="slides[${this.formCounter}][end_date]" id="end_date_${this.formCounter}" class="hssm-input" value="${slideData.end_date || ''}" required> <input type="date" name="slides[${this.formCounter}][end_date]" id="end_date_${this.formCounter}" class="hssm-input" value="${slideData.end_date || ''}">
</div> </div>
</div> </div>
View File
+16 -6
View File
@@ -406,8 +406,8 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
add_settings_error( 'hssm_settings', 'api_key_generated', __( 'New API key generated successfully.', 'hi-school-slide-manager' ), 'success' ); add_settings_error( 'hssm_settings', 'api_key_generated', __( 'New API key generated successfully.', 'hi-school-slide-manager' ), 'success' );
} }
// Handle API key deletion // Handle API key deletion (only when delete was explicitly triggered via JS with index)
if ( isset( $_POST['delete_api_key'] ) && isset( $_POST['api_key_index'] ) ) { if ( ! empty( $_POST['delete_api_key'] ) && isset( $_POST['api_key_index'] ) && $_POST['api_key_index'] !== '' ) {
$api_keys = get_option( 'hssm_api_keys', array() ); $api_keys = get_option( 'hssm_api_keys', array() );
$index = (int) $_POST['api_key_index']; $index = (int) $_POST['api_key_index'];
if ( isset( $api_keys[ $index ] ) ) { if ( isset( $api_keys[ $index ] ) ) {
@@ -511,6 +511,8 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
if ( empty( $api_keys ) ) { if ( empty( $api_keys ) ) {
echo '<p>' . __( 'No API keys generated yet.', 'hi-school-slide-manager' ) . '</p>'; echo '<p>' . __( 'No API keys generated yet.', 'hi-school-slide-manager' ) . '</p>';
} else { } else {
echo '<input type="hidden" name="api_key_index" id="hssm-delete-api-key-index" value="" />';
echo '<input type="hidden" name="delete_api_key" id="hssm-delete-api-key-flag" value="0" />';
echo '<table class="widefat">'; echo '<table class="widefat">';
echo '<thead><tr><th>' . __( 'API Key', 'hi-school-slide-manager' ) . '</th><th>' . __( 'Actions', 'hi-school-slide-manager' ) . '</th></tr></thead>'; echo '<thead><tr><th>' . __( 'API Key', 'hi-school-slide-manager' ) . '</th><th>' . __( 'Actions', 'hi-school-slide-manager' ) . '</th></tr></thead>';
echo '<tbody>'; echo '<tbody>';
@@ -519,9 +521,8 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
echo '<tr>'; echo '<tr>';
echo '<td><code>' . esc_html( $key ) . '</code></td>'; echo '<td><code>' . esc_html( $key ) . '</code></td>';
echo '<td>'; echo '<td>';
echo '<button type="button" class="button copy-api-key" data-key="' . esc_attr( $key ) . '">' . __( 'Copy', 'hi-school-slide-manager' ) . '</button> '; echo '<button type="button" class="button copy-api-key" data-key="' . esc_attr( $key ) . '">' . __( 'Copy', 'hi-school-slide-manager' ) . '</button> ';
echo '<button type="submit" name="delete_api_key" value="1" class="button button-link-delete" onclick="return confirm(\'' . esc_js( __( 'Are you sure you want to delete this API key?', 'hi-school-slide-manager' ) ) . '\')">' . __( 'Delete', 'hi-school-slide-manager' ) . '</button>'; echo '<button type="button" class="button button-link-delete hssm-delete-api-key" data-index="' . esc_attr( $index ) . '">' . __( 'Delete', 'hi-school-slide-manager' ) . '</button>';
echo '<input type="hidden" name="api_key_index" value="' . esc_attr( $index ) . '" />';
echo '</td>'; echo '</td>';
echo '</tr>'; echo '</tr>';
} }
@@ -534,7 +535,7 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
echo '</div>'; echo '</div>';
// Add JavaScript for copy functionality // Add JavaScript for copy and delete functionality
?> ?>
<script> <script>
jQuery(document).ready(function($) { jQuery(document).ready(function($) {
@@ -544,6 +545,15 @@ Headers: X-HSSM-API-Key: your-api-key</code></pre>
alert("API key copied to clipboard!"); alert("API key copied to clipboard!");
}); });
}); });
$(".hssm-delete-api-key").on("click", function() {
if (! confirm("<?php echo esc_js( __( 'Are you sure you want to delete this API key?', 'hi-school-slide-manager' ) ); ?>")) {
return;
}
var index = $(this).data("index");
$("#hssm-delete-api-key-index").val(index);
$("#hssm-delete-api-key-flag").val("1");
$(this).closest("form").submit();
});
}); });
</script> </script>
<?php <?php
+52 -41
View File
@@ -59,12 +59,6 @@ class HSSM_Frontend {
add_action( 'wp_ajax_hssm_get_notification_settings', array( $this, 'ajax_get_notification_settings' ) ); add_action( 'wp_ajax_hssm_get_notification_settings', array( $this, 'ajax_get_notification_settings' ) );
add_action( 'wp_ajax_hssm_save_notification_settings', array( $this, 'ajax_save_notification_settings' ) ); add_action( 'wp_ajax_hssm_save_notification_settings', array( $this, 'ajax_save_notification_settings' ) );
// Test handler to verify AJAX is working
add_action( 'wp_ajax_hssm_test', array( $this, 'ajax_test' ) );
add_action( 'wp_ajax_nopriv_hssm_test', array( $this, 'ajax_test' ) );
// Debug: Log that handlers are being registered
error_log( 'HSSM: AJAX handlers registered including hssm_get_slides' );
// Frontend assets // Frontend assets
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
@@ -297,8 +291,7 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_upload_image() { public function ajax_upload_image() {
// Verify nonce and permissions if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'upload_files' ) ) {
if ( ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) || ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) ); wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return; return;
} }
@@ -332,8 +325,7 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_preview_slides() { public function ajax_preview_slides() {
// Verify nonce and permissions if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'read' ) ) {
if ( ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) || ! current_user_can( 'read' ) ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) ); wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return; return;
} }
@@ -347,9 +339,21 @@ class HSSM_Frontend {
$meta_query = array( $meta_query = array(
'relation' => 'AND', 'relation' => 'AND',
array( array(
'key' => '_hssm_start_date', 'relation' => 'OR',
'value' => $date, array(
'compare' => '<=', 'key' => '_hssm_start_date',
'value' => '',
'compare' => '=',
),
array(
'key' => '_hssm_start_date',
'value' => $date,
'compare' => '<=',
),
array(
'key' => '_hssm_start_date',
'compare' => 'NOT EXISTS',
),
), ),
array( array(
'relation' => 'OR', 'relation' => 'OR',
@@ -406,9 +410,9 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_save_slide() { public function ajax_save_slide() {
// Verify nonce and permissions if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) {
if ( ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) ); wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
} }
// Sanitize and validate input // Sanitize and validate input
@@ -678,7 +682,7 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_get_slides() { public function ajax_get_slides() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) ) { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) ) {
wp_send_json_error( array( 'message' => 'Nonce verification failed' ) ); wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
return; return;
} }
@@ -855,13 +859,12 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_approve_slide() { public function ajax_approve_slide() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) ) { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) ) {
wp_send_json_error( array( 'message' => 'Nonce verification failed' ) ); wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
return; return;
} }
error_log( 'Approve slide AJAX called' );
if ( empty( $_POST['slide_id'] ) ) { if ( ! isset( $_POST['slide_id'] ) || '' === trim( (string) $_POST['slide_id'] ) ) {
wp_send_json_error( array( 'message' => 'Slide ID is required' ) ); wp_send_json_error( array( 'message' => 'Slide ID is required' ) );
return; return;
} }
@@ -918,28 +921,19 @@ class HSSM_Frontend {
} }
} }
/**
* Test AJAX handler to verify AJAX is working
*
* @since 1.0.0
*/
public function ajax_test() {
error_log( 'AJAX TEST HANDLER CALLED - SUCCESS!' );
wp_send_json_success( array( 'message' => 'AJAX is working!' ) );
}
/** /**
* AJAX handler for deleting slides * AJAX handler for deleting slides
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_delete_slide() { public function ajax_delete_slide() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) ) { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) ) {
wp_send_json_error( array( 'message' => 'Nonce verification failed' ) ); wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
return; return;
} }
if ( empty( $_POST['slide_id'] ) ) { if ( ! isset( $_POST['slide_id'] ) || '' === trim( (string) $_POST['slide_id'] ) ) {
wp_send_json_error( array( 'message' => 'Slide ID is required' ) ); wp_send_json_error( array( 'message' => 'Slide ID is required' ) );
return;
} }
$slide_id = intval( $_POST['slide_id'] ); $slide_id = intval( $_POST['slide_id'] );
@@ -970,12 +964,13 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_update_slide() { public function ajax_update_slide() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) ) { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) ) {
wp_send_json_error( array( 'message' => 'Nonce verification failed' ) ); wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
return; return;
} }
if ( empty( $_POST['slide_id'] ) ) { if ( ! isset( $_POST['slide_id'] ) || '' === trim( (string) $_POST['slide_id'] ) ) {
wp_send_json_error( array( 'message' => 'Slide ID is required' ) ); wp_send_json_error( array( 'message' => 'Slide ID is required' ) );
return;
} }
$slide_id = intval( $_POST['slide_id'] ); $slide_id = intval( $_POST['slide_id'] );
@@ -1281,12 +1276,14 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_duplicate_slide() { public function ajax_duplicate_slide() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) { 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' ) ); wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
} }
if ( empty( $_POST['slide_id'] ) ) { if ( ! isset( $_POST['slide_id'] ) || '' === trim( (string) $_POST['slide_id'] ) ) {
wp_send_json_error( array( 'message' => 'Slide ID is required' ) ); wp_send_json_error( array( 'message' => 'Slide ID is required' ) );
return;
} }
$original_id = intval( $_POST['slide_id'] ); $original_id = intval( $_POST['slide_id'] );
@@ -1328,12 +1325,14 @@ class HSSM_Frontend {
* @since 1.0.0 * @since 1.0.0
*/ */
public function ajax_toggle_slide_status() { public function ajax_toggle_slide_status() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) { 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' ) ); wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
} }
if ( empty( $_POST['slide_id'] ) ) { if ( ! isset( $_POST['slide_id'] ) || '' === trim( (string) $_POST['slide_id'] ) ) {
wp_send_json_error( array( 'message' => 'Slide ID is required' ) ); wp_send_json_error( array( 'message' => 'Slide ID is required' ) );
return;
} }
$slide_id = intval( $_POST['slide_id'] ); $slide_id = intval( $_POST['slide_id'] );
@@ -1454,13 +1453,25 @@ class HSSM_Frontend {
$date_param = isset( $_GET['hssm_date'] ) ? sanitize_text_field( $_GET['hssm_date'] ) : current_time( 'Y-m-d' ); $date_param = isset( $_GET['hssm_date'] ) ? sanitize_text_field( $_GET['hssm_date'] ) : current_time( 'Y-m-d' );
$site_param = isset( $_GET['hssm_site'] ) ? sanitize_text_field( $_GET['hssm_site'] ) : ''; $site_param = isset( $_GET['hssm_site'] ) ? sanitize_text_field( $_GET['hssm_site'] ) : '';
// Apply Meta Query for Date // Apply Meta Query for Date (evergreen: no start/end = always show)
$meta_query = array( $meta_query = array(
'relation' => 'AND', 'relation' => 'AND',
array( array(
'key' => '_hssm_start_date', 'relation' => 'OR',
'value' => $date_param, array(
'compare' => '<=', 'key' => '_hssm_start_date',
'value' => '',
'compare' => '=',
),
array(
'key' => '_hssm_start_date',
'value' => $date_param,
'compare' => '<=',
),
array(
'key' => '_hssm_start_date',
'compare' => 'NOT EXISTS',
),
), ),
array( array(
'relation' => 'OR', 'relation' => 'OR',
+4
View File
@@ -472,6 +472,9 @@ class HSSM_Main {
*/ */
private static function remove_plugin_options() { private static function remove_plugin_options() {
$options = array( $options = array(
'hssm_plugin_mode',
'hssm_api_url',
'hssm_client_site_slug',
'hssm_notification_emails', 'hssm_notification_emails',
'hssm_default_slide_duration', 'hssm_default_slide_duration',
'hssm_cache_duration', 'hssm_cache_duration',
@@ -479,6 +482,7 @@ class HSSM_Main {
'hssm_preview_page_slug', 'hssm_preview_page_slug',
'hssm_manager_page_slug', 'hssm_manager_page_slug',
'hssm_api_keys', 'hssm_api_keys',
'hssm_notification_settings',
); );
foreach ( $options as $option ) { foreach ( $options as $option ) {
+8 -6
View File
@@ -378,18 +378,20 @@ class HSSM_REST_API {
return null; return null;
} }
$sizes = array( 'full', 'large', 'medium', 'thumbnail' );
$sizes_data = array();
foreach ( $sizes as $size ) {
$src = wp_get_attachment_image_src( $thumbnail_id, $size );
$sizes_data[ $size ] = ( $src && isset( $src[0] ) ) ? $src[0] : $image_data[0];
}
return array( return array(
'id' => $thumbnail_id, 'id' => $thumbnail_id,
'url' => $image_data[0], 'url' => $image_data[0],
'width' => $image_data[1], 'width' => $image_data[1],
'height' => $image_data[2], 'height' => $image_data[2],
'alt' => get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ), 'alt' => get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ),
'sizes' => array( 'sizes' => $sizes_data,
'full' => wp_get_attachment_image_src( $thumbnail_id, 'full' )[0],
'large' => wp_get_attachment_image_src( $thumbnail_id, 'large' )[0],
'medium' => wp_get_attachment_image_src( $thumbnail_id, 'medium' )[0],
'thumbnail' => wp_get_attachment_image_src( $thumbnail_id, 'thumbnail' )[0],
),
); );
} }
+2 -2
View File
@@ -109,7 +109,6 @@ class HSSM_Slides_CPT {
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ), 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'show_in_rest' => true, 'show_in_rest' => true,
'rest_base' => 'slides', 'rest_base' => 'slides',
'rest_controller_class' => 'HSSM_REST_Slides_Controller',
); );
register_post_type( self::POST_TYPE, $args ); register_post_type( self::POST_TYPE, $args );
@@ -927,7 +926,8 @@ class HSSM_Slides_CPT {
*/ */
public function ajax_update_slide_order() { public function ajax_update_slide_order() {
// Verify nonce and permissions // Verify nonce and permissions
if ( ! wp_verify_nonce( $_POST['nonce'], 'hssm_update_slide_order' ) || ! current_user_can( 'edit_posts' ) ) { $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'hssm_update_slide_order' ) || ! current_user_can( 'edit_posts' ) ) {
wp_send_json_error( 'Unauthorized' ); wp_send_json_error( 'Unauthorized' );
} }
+15 -3
View File
@@ -245,9 +245,21 @@ function hssm_get_preview_slides_for_date( $date, $site_slug = '' ) {
$meta_query = array( $meta_query = array(
'relation' => 'AND', 'relation' => 'AND',
array( array(
'key' => '_hssm_start_date', 'relation' => 'OR',
'value' => $date, array(
'compare' => '<=', 'key' => '_hssm_start_date',
'value' => '',
'compare' => '=',
),
array(
'key' => '_hssm_start_date',
'value' => $date,
'compare' => '<=',
),
array(
'key' => '_hssm_start_date',
'compare' => 'NOT EXISTS',
),
), ),
array( array(
'relation' => 'OR', 'relation' => 'OR',