diff --git a/cleverpush-api.php b/cleverpush-api.php
index 5236cab..488d35e 100644
--- a/cleverpush-api.php
+++ b/cleverpush-api.php
@@ -85,6 +85,33 @@ public static function send_notification($title, $body, $url, $options = array()
return CleverPush_Api::request('/notification/send', $params);
}
+ public static function delete_notification($notification_id)
+ {
+ $channel_id = get_option('cleverpush_channel_id');
+ $api_key_private = get_option('cleverpush_apikey_private');
+
+ if (empty($channel_id) || empty($notification_id) || empty($api_key_private)) {
+ return false;
+ }
+
+ $response = wp_remote_request(
+ CLEVERPUSH_API_ENDPOINT . '/channel/' . rawurlencode($channel_id) . '/notification/' . rawurlencode($notification_id),
+ array(
+ 'method' => 'DELETE',
+ 'timeout' => 20, // phpcs:ignore
+ 'headers' => array(
+ 'authorization' => $api_key_private
+ )
+ )
+ );
+
+ if (is_wp_error($response)) {
+ return false;
+ }
+
+ return wp_remote_retrieve_response_code($response) >= 200 && wp_remote_retrieve_response_code($response) < 300;
+ }
+
public static function update_channel($channel_id, $params = array())
{
if (empty($channel_id)) {
diff --git a/cleverpush.php b/cleverpush.php
index f7ee2da..e31fe89 100644
--- a/cleverpush.php
+++ b/cleverpush.php
@@ -4,7 +4,7 @@
Plugin URI: https://cleverpush.com
Description: Send push notifications to your users right through your website. Visit CleverPush for more details.
Author: CleverPush
-Version: 1.9.8
+Version: 1.9.9
Author URI: https://cleverpush.com
Text Domain: cleverpush
Domain Path: /languages
@@ -43,6 +43,8 @@ public function __construct()
add_action('admin_notices', array($this, 'warn_nosettings'));
add_action('add_meta_boxes', array($this, 'create_metabox'));
add_action('save_post', array($this, 'save_post'), 10, 2);
+ add_action('transition_post_status', array($this, 'cleanup_draft_notification_for_status_change'), 10, 3);
+ add_action('before_delete_post', array($this, 'delete_draft_notification'));
add_action('admin_notices', array($this, 'notices'));
add_action('publish_post', array($this, 'publish_post'), 10, 1);
@@ -414,6 +416,17 @@ function ajax_load_options()
$hidden_notification_settings = get_option('cleverpush_channel_hidden_notification_settings');
if (!empty($api_key_private) && !empty($selected_channel_id)) {
+ $post_id = isset($_POST['post_id']) ? absint(wp_unslash($_POST['post_id'])) : 0;
+ $saved_targeting = array();
+ if ($post_id && current_user_can('edit_post', $post_id)) {
+ $saved_targeting = get_post_meta($post_id, 'cleverpush_notification_targeting', true);
+ if (!is_array($saved_targeting)) {
+ $saved_targeting = array();
+ }
+ }
+ $saved_topics = isset($saved_targeting['topics']) && is_array($saved_targeting['topics']) ? array_map('strval', $saved_targeting['topics']) : array();
+ $saved_tags = isset($saved_targeting['tags']) && is_array($saved_targeting['tags']) ? array_map('strval', $saved_targeting['tags']) : array();
+ $saved_segments = isset($saved_targeting['segments']) && is_array($saved_targeting['segments']) ? array_map('strval', $saved_targeting['segments']) : array();
$cleverpush_segments = array();
if (empty($hidden_notification_settings) || strpos($hidden_notification_settings, 'segments') === false) {
@@ -540,24 +553,23 @@ function ajax_load_options()
+ style="display: ; margin-left: 30px;" data-required="">
new Date()) {
+ cpScheduledAtPicker.value = getLocalDateString(new Date(cpScheduledAtInput.value));
+ }
+ cpScheduledAtPicker.addEventListener('input', syncScheduledAt);
+ cpScheduledAtPicker.addEventListener('change', syncScheduledAt);
+ var postForm = document.getElementById('post');
+ if (postForm) {
+ postForm.addEventListener('submit', syncScheduledAt);
+ }
+ }
+
var initCleverPush = function () {
if (typeof wp !== 'undefined' && wp.data && wp.data.subscribe && wp.data.select) {
var hasNotice = false;
@@ -790,6 +835,10 @@ public function metabox($post)
var isPreviewingPost = coreEditor.isPreviewingPost();
var postStatus = coreEditor.getEditedPostAttribute('status');
+ if (isSavingPost && !wasSavingPost) {
+ syncScheduledAt();
+ }
+
// Save metaboxes on save completion, except for autosaves that are not a post preview.
var shouldTriggerTemplateNotice = (
(wasSavingPost && !isSavingPost && !wasAutosavingPost) ||
@@ -971,29 +1020,12 @@ public function metabox($post)
}
}
- if (cpScheduledAtInput && cpScheduledAtPicker) {
- var getLocalDateString = function(date) {
- return date.getFullYear() + '-' + ((date.getMonth() + 1) + '').padStart(2, '0') + '-' + (date.getDate() + '') + "T" + (date.getHours() + '').padStart(2, '0') + ":" + (date.getMinutes() + '').padStart(2, '0')
- };
- var date = new Date();
- cpScheduledAtPicker.min = getLocalDateString(date);
- if (cpScheduledAtInput.value && new Date(cpScheduledAtInput.value) > new Date()) {
- cpScheduledAtPicker.value = getLocalDateString(new Date(cpScheduledAtInput.value));
- }
- cpScheduledAtPicker.addEventListener('change', function() {
- if (!cpScheduledAtPicker.value) {
- cpScheduledAtInput.value = '';
- return;
- }
- cpScheduledAtInput.value = new Date(cpScheduledAtPicker.value).toISOString();
- });
- }
}
}
};
request.open('POST', ajaxurl, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
- request.send('action=cleverpush_send_options');
+ request.send('action=cleverpush_send_options&post_id=ID; ?>');
};
if (document.readyState === 'complete') {
@@ -1126,16 +1158,18 @@ public function publish_post($post_id)
return;
}
- if (!current_user_can('cleverpush_send')) {
+ $saved_send = get_post_meta($post_id, 'cleverpush_send_notification', true);
+ $publishing_via_cron = (function_exists('wp_doing_cron') && wp_doing_cron()) || (defined('DOING_CRON') && DOING_CRON);
+ if (!current_user_can('cleverpush_send') && !($publishing_via_cron && $saved_send)) {
return;
}
- if (isset($_POST['cleverpush_metabox_form_data_available']) ? !isset($_POST['cleverpush_send_notification']) : !get_post_meta($post_id, 'cleverpush_send_notification', true)) {
+ if (isset($_POST['cleverpush_metabox_form_data_available']) ? !isset($_POST['cleverpush_send_notification']) : !$saved_send) {
return;
}
if (get_post_meta($post_id, 'cleverpush_notification_sent', true)) {
- $notification_sent_at = get_post_meta(get_the_ID(), 'cleverpush_notification_sent_at', true);
+ $notification_sent_at = get_post_meta($post_id, 'cleverpush_notification_sent_at', true);
if (!empty($notification_sent_at) && (time() - $notification_sent_at) < 60) {
return;
}
@@ -1145,15 +1179,28 @@ public function publish_post($post_id)
if (get_option('cleverpush_notification_title_required') == 'on') {
$title = null;
}
- $text = !empty(get_the_excerpt()) ? html_entity_decode(get_the_excerpt()) : '';
+ $text = get_the_excerpt($post_id);
+ $text = !empty($text) ? html_entity_decode($text) : '';
$url = get_permalink($post_id);
+ $metabox_submitted = isset($_POST['cleverpush_metabox_form_data_available']);
if (!empty($_POST['cleverpush_title'])) {
$title = sanitize_text_field(wp_unslash($_POST['cleverpush_title']));
$text = '';
+ } elseif (!$metabox_submitted) {
+ $saved_title = get_post_meta($post_id, 'cleverpush_title', true);
+ if (!empty($saved_title)) {
+ $title = $saved_title;
+ $text = '';
+ }
}
if (!empty($_POST['cleverpush_text'])) {
$text = sanitize_text_field(wp_unslash($_POST['cleverpush_text']));
+ } elseif (!$metabox_submitted) {
+ $saved_text = get_post_meta($post_id, 'cleverpush_text', true);
+ if (!empty($saved_text)) {
+ $text = $saved_text;
+ }
}
if (empty($title)) {
@@ -1161,22 +1208,20 @@ public function publish_post($post_id)
}
$options = array();
- if (isset($_POST['cleverpush_use_segments']) && $_POST['cleverpush_use_segments'] == '1' && !empty($_POST['cleverpush_segments'])) {
- $options['segments'] = array_map('sanitize_text_field', $_POST['cleverpush_segments']);
- }
- if (isset($_POST['cleverpush_use_topics']) && $_POST['cleverpush_use_topics'] == '1' && !empty($_POST['cleverpush_topics'])) {
- $options['topics'] = array_map('sanitize_text_field', $_POST['cleverpush_topics']);
- }
- if (isset($_POST['cleverpush_use_tags']) && $_POST['cleverpush_use_tags'] == '1' && !empty($_POST['cleverpush_tags'])) {
- $options['tags'] = array_map('sanitize_text_field', $_POST['cleverpush_tags']);
- }
- $thumbnail_url = get_the_post_thumbnail_url();
+ $options = array_merge($options, $this->get_notification_targeting($post_id, $metabox_submitted));
+ $thumbnail_url = get_the_post_thumbnail_url($post_id);
if (!empty($thumbnail_url)) {
$options['mediaUrl'] = $thumbnail_url;
}
if (!empty($_POST['cleverpush_scheduled_at'])) {
$options['scheduledAt'] = sanitize_text_field(wp_unslash($_POST['cleverpush_scheduled_at']));
+ } elseif (!$metabox_submitted) {
+ $saved_scheduled_at = get_post_meta($post_id, 'cleverpush_scheduled_at', true);
+ $saved_timestamp = !empty($saved_scheduled_at) ? strtotime($saved_scheduled_at) : false;
+ if ($saved_timestamp && $saved_timestamp > time()) {
+ $options['scheduledAt'] = $saved_scheduled_at;
+ }
}
delete_post_meta($post_id, 'cleverpush_send_notification');
@@ -1187,6 +1232,7 @@ public function publish_post($post_id)
update_option('cleverpush_notification_error', null);
update_post_meta($post_id, 'cleverpush_notification_sent', true);
update_post_meta($post_id, 'cleverpush_notification_sent_at', time());
+ $this->delete_draft_notification($post_id);
} catch (Exception $ex) {
update_option('cleverpush_notification_result', array('status' => 'error', 'message' => $ex->getMessage() ));
@@ -1215,10 +1261,15 @@ public function save_post($post_id, $post)
if (isset($_POST['cleverpush_text'])) {
update_post_meta($post_id, 'cleverpush_text', sanitize_text_field($_POST['cleverpush_text']));
}
- if (isset($_POST['cleverpush_scheduled_at'])) {
- update_post_meta($post_id, 'cleverpush_scheduled_at', sanitize_text_field($_POST['cleverpush_scheduled_at']));
+ if (isset($_POST['cleverpush_metabox_form_data_available']) && isset($_POST['cleverpush_scheduled_at'])) {
+ update_post_meta($post_id, 'cleverpush_scheduled_at', sanitize_text_field(wp_unslash($_POST['cleverpush_scheduled_at'])));
+ }
+ if (isset($_POST['cleverpush_metabox_form_data_available']) && $this->notification_targeting_submitted()) {
+ update_post_meta($post_id, 'cleverpush_notification_targeting', $this->get_notification_targeting($post_id, true));
}
+ $this->sync_draft_notification($post_id);
+
if (!empty($_POST['cleverpush_story_id'])) {
if (isset($_POST['clear_cache']) && !empty($_POST['cleverpush_story_id'])) {
delete_transient('cleverpush_story_' . sanitize_text_field($_POST['cleverpush_story_id']) . '_content');
@@ -1245,6 +1296,185 @@ public function save_post($post_id, $post)
}
}
+ public function sync_draft_notification($post_id)
+ {
+ if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
+ return;
+ }
+
+ $status = get_post_status($post_id);
+ if (!in_array($status, array('draft', 'future', 'pending'), true)) {
+ $this->delete_draft_notification($post_id);
+ return;
+ }
+
+ if (!isset($_POST['cleverpush_metabox_form_data_available'])) {
+ return;
+ }
+
+ if (!isset($_POST['cleverpush_send_notification'])) {
+ $this->delete_draft_notification($post_id);
+ return;
+ }
+
+ $title = html_entity_decode(get_the_title($post_id));
+ if (get_option('cleverpush_notification_title_required') == 'on') {
+ $title = null;
+ }
+ if (!empty($_POST['cleverpush_title'])) {
+ $title = sanitize_text_field(wp_unslash($_POST['cleverpush_title']));
+ }
+ $text = '';
+ if (!empty($_POST['cleverpush_text'])) {
+ $text = sanitize_text_field(wp_unslash($_POST['cleverpush_text']));
+ }
+ if (empty($title)) {
+ $this->delete_draft_notification($post_id);
+ return;
+ }
+
+ $scheduled_at = get_post_meta($post_id, 'cleverpush_scheduled_at', true);
+ $scheduled_timestamp = !empty($scheduled_at) ? strtotime($scheduled_at) : false;
+ if (!$scheduled_timestamp || $scheduled_timestamp <= time()) {
+ $scheduled_at = '';
+ }
+
+ $url = get_permalink($post_id);
+ $targeting = $this->get_notification_targeting($post_id, true);
+ $fingerprint = md5($title . '|' . $text . '|' . $url . '|' . $scheduled_at . '|' . wp_json_encode($targeting));
+ $existing_id = get_post_meta($post_id, 'cleverpush_draft_notification_id', true);
+ $existing_fingerprint = get_post_meta($post_id, 'cleverpush_draft_notification_hash', true);
+ if (!empty($existing_id) && $existing_fingerprint === $fingerprint) {
+ $this->retry_pending_draft_deletions($post_id);
+ return;
+ }
+
+ $options = array_merge(array('status' => 'draft'), $targeting);
+ if (!empty($scheduled_at)) {
+ $options['scheduledAt'] = $scheduled_at;
+ }
+
+ try {
+ $response = CleverPush_Api::send_notification($title, $text, $url, $options);
+ if (empty($response) || is_wp_error($response)) {
+ return;
+ }
+ $body = json_decode(wp_remote_retrieve_body($response));
+ if (empty($body->notificationId)) {
+ return;
+ }
+ $new_notification_id = sanitize_text_field($body->notificationId);
+ update_post_meta($post_id, 'cleverpush_draft_notification_id', $new_notification_id);
+ update_post_meta($post_id, 'cleverpush_draft_notification_hash', $fingerprint);
+ if (!empty($existing_id) && $existing_id !== $new_notification_id) {
+ $this->delete_draft_notification_by_id($post_id, $existing_id);
+ }
+ $this->retry_pending_draft_deletions($post_id);
+ } catch (Exception $ex) {
+ update_option('cleverpush_notification_error', $ex->getMessage());
+ }
+ }
+
+ public function delete_draft_notification($post_id)
+ {
+ $notification_id = get_post_meta($post_id, 'cleverpush_draft_notification_id', true);
+ if (!empty($notification_id)) {
+ if (!CleverPush_Api::delete_notification($notification_id)) {
+ $this->queue_draft_notification_deletion($post_id, $notification_id);
+ return false;
+ }
+ }
+ delete_post_meta($post_id, 'cleverpush_draft_notification_id');
+ delete_post_meta($post_id, 'cleverpush_draft_notification_hash');
+ $this->retry_pending_draft_deletions($post_id);
+ return true;
+ }
+
+ public function delete_draft_notification_by_id($post_id, $notification_id)
+ {
+ if (!CleverPush_Api::delete_notification($notification_id)) {
+ $this->queue_draft_notification_deletion($post_id, $notification_id);
+ return false;
+ }
+ return true;
+ }
+
+ public function queue_draft_notification_deletion($post_id, $notification_id)
+ {
+ $pending_ids = get_post_meta($post_id, 'cleverpush_draft_notification_ids_to_delete', true);
+ $pending_ids = is_array($pending_ids) ? $pending_ids : array();
+ if (!in_array($notification_id, $pending_ids, true)) {
+ $pending_ids[] = $notification_id;
+ update_post_meta($post_id, 'cleverpush_draft_notification_ids_to_delete', $pending_ids);
+ }
+ }
+
+ public function retry_pending_draft_deletions($post_id)
+ {
+ $pending_ids = get_post_meta($post_id, 'cleverpush_draft_notification_ids_to_delete', true);
+ if (!is_array($pending_ids) || empty($pending_ids)) {
+ return;
+ }
+
+ $remaining_ids = array();
+ foreach ($pending_ids as $notification_id) {
+ if (!CleverPush_Api::delete_notification($notification_id)) {
+ $remaining_ids[] = $notification_id;
+ }
+ }
+ if (empty($remaining_ids)) {
+ delete_post_meta($post_id, 'cleverpush_draft_notification_ids_to_delete');
+ } else {
+ update_post_meta($post_id, 'cleverpush_draft_notification_ids_to_delete', $remaining_ids);
+ }
+ }
+
+ public function cleanup_draft_notification_for_status_change($new_status, $old_status, $post)
+ {
+ if (in_array($new_status, array('draft', 'future', 'pending'), true)) {
+ return;
+ }
+ $this->delete_draft_notification($post->ID);
+ }
+
+ public function notification_targeting_submitted()
+ {
+ return isset($_POST['cleverpush_use_topics'])
+ || isset($_POST['cleverpush_use_segments'])
+ || isset($_POST['cleverpush_use_tags']);
+ }
+
+ public function get_notification_targeting($post_id, $from_request = false)
+ {
+ $targeting = get_post_meta($post_id, 'cleverpush_notification_targeting', true);
+ if (!is_array($targeting)) {
+ $targeting = array();
+ }
+ if (!$from_request) {
+ return $targeting;
+ }
+
+ $targeting = $this->replace_submitted_targeting($targeting, 'topics', 'cleverpush_use_topics', 'cleverpush_topics');
+ $targeting = $this->replace_submitted_targeting($targeting, 'segments', 'cleverpush_use_segments', 'cleverpush_segments');
+ $targeting = $this->replace_submitted_targeting($targeting, 'tags', 'cleverpush_use_tags', 'cleverpush_tags');
+
+ return $targeting;
+ }
+
+ public function replace_submitted_targeting($targeting, $key, $use_field, $value_field)
+ {
+ if (!isset($_POST[$use_field])) {
+ return $targeting;
+ }
+
+ unset($targeting[$key]);
+ if ($_POST[$use_field] == '1' && !empty($_POST[$value_field]) && is_array($_POST[$value_field])) {
+ $targeting[$key] = array_map('sanitize_text_field', wp_unslash($_POST[$value_field]));
+ }
+
+ return $targeting;
+ }
+
public function show_public_preview( $query )
{
if ($query->is_main_query()
diff --git a/readme.txt b/readme.txt
index e6fc754..22b7fd8 100644
--- a/readme.txt
+++ b/readme.txt
@@ -5,7 +5,7 @@ Plugin URI: https://cleverpush.com
Tags: push notifications, web push, browser notifications, woocommerce
Requires at least: 2.7
Tested up to: 6.7.2
-Stable tag: 1.9.8
+Stable tag: 1.9.9
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -34,6 +34,11 @@ If you are using any cache plugin, also be sure, to empty your cache.
== ChangeLog ==
+= 1.9.9 =
+* Keep the optional notification schedule after saving a post
+* Send that scheduled push when WordPress publishes the post, including a scheduled post
+* Show an unpublished scheduled post as a draft in CleverPush
+
= 1.9.8 =
* Fixed issue with capabilities_version