PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.2.8
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.2.8
3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 All 156 releases
notificationx / includes / Core / Rest / Popup.php

Popup.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.2.8, at includes/Core/Rest/Popup.php

501 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace NotificationX\Core\Rest;
4
5 use NotificationX\GetInstance;
6 use NotificationX\Core\PopupNotification;
7 use NotificationX\Extensions\Popup\PopupNotification as PopupPopupNotification;
8 use NotificationX\NotificationX;
9 use WP_REST_Server;
10
11 /**
12 * @method static Popup get_instance($args = null)
13 */
14 class Popup {
15 /**
16 * Instance of Popup
17 *
18 * @var Popup
19 */
20 use GetInstance;
21 public $namespace;
22 public $rest_base;
23 public $id = 'popup_notification';
24
25 /**
26 * Sources that share the popup-submit form pipeline.
27 * Entries from these sources are listed/managed together in the Feedback Entries screen.
28 */
29 private function form_sources() {
30 return [ 'popup_notification', 'exit_intent_custom' ];
31 }
32
33 /**
34 * Constructor.
35 *
36 * @since 4.7.0
37 *
38 * @param string $post_type Post type.
39 */
40 public function __construct() {
41 $this->namespace = 'notificationx/v1';
42 $this->rest_base = 'popup-submit';
43 add_action('rest_api_init', [$this, 'register_routes']);
44 }
45
46
47 /**
48 * Registers the routes for the objects of the controller.
49 *
50 * @since 3.1.12
51 *
52 * @see register_rest_route()
53 */
54 public function register_routes() {
55 register_rest_route('notificationx/v1', '/popup-submit', [
56 'methods' => 'POST',
57 'callback' => [ $this , 'handle_popup_submission' ],
58 'permission_callback' => '__return_true',
59 'args' => [
60 'nx_id' => [
61 'required' => true,
62 'type' => 'integer',
63 'sanitize_callback' => 'absint',
64 ],
65 'email' => [
66 'type' => 'string',
67 'sanitize_callback' => 'sanitize_email',
68 ],
69 'message' => [
70 'type' => 'string',
71 'sanitize_callback' => 'sanitize_textarea_field',
72 ],
73 'name' => [
74 'type' => 'string',
75 'sanitize_callback' => 'sanitize_textarea_field',
76 ],
77 // `title` and `theme` are persisted into the entry data and later
78 // shown in the admin Feedback Entries screen and CSV export, so
79 // sanitize them on the way in instead of storing raw input.
80 'title' => [
81 'type' => 'string',
82 'sanitize_callback' => 'sanitize_text_field',
83 ],
84 'theme' => [
85 'type' => 'string',
86 'sanitize_callback' => 'sanitize_text_field',
87 ],
88 'timestamp' => [
89 'type' => 'integer',
90 'sanitize_callback' => 'absint',
91 ],
92 ],
93 ]);
94
95 // Feedback entries endpoint
96 register_rest_route('notificationx/v1', '/feedback-entries', [
97 'methods' => 'GET',
98 'callback' => [$this, 'get_feedback_entries'],
99 'permission_callback' => function() {
100 return current_user_can('read_notificationx');
101 },
102 'args' => [
103 'page' => [
104 'default' => 1,
105 'type' => 'integer',
106 'minimum' => 1,
107 ],
108 'per_page' => [
109 'default' => 20,
110 'type' => 'integer',
111 'minimum' => 1,
112 'maximum' => 200,
113 ],
114 's' => [
115 'default' => '',
116 'type' => 'string',
117 'sanitize_callback' => 'sanitize_text_field',
118 ],
119 'notification_id' => [
120 'default' => '',
121 'type' => 'string',
122 'sanitize_callback' => 'sanitize_text_field',
123 ],
124 ],
125 ]);
126
127 // Delete feedback entry endpoint
128 register_rest_route('notificationx/v1', '/feedback-entries/(?P<id>\d+)', [
129 'methods' => 'DELETE',
130 'callback' => [$this, 'delete_feedback_entry'],
131 'permission_callback' => function() {
132 return current_user_can('edit_notificationx');
133 },
134 'args' => [
135 'id' => [
136 'required' => true,
137 'type' => 'integer',
138 ],
139 ],
140 ]);
141
142 // Bulk delete feedback entries endpoint
143 register_rest_route('notificationx/v1', '/feedback-entries/bulk-delete', [
144 'methods' => 'POST',
145 'callback' => [$this, 'bulk_delete_feedback_entries'],
146 'permission_callback' => function() {
147 return current_user_can('edit_notificationx');
148 },
149 'args' => [
150 'ids' => [
151 'required' => true,
152 'type' => 'array',
153 'items' => [
154 'type' => 'integer',
155 ],
156 ],
157 ],
158 ]);
159
160 // Export feedback entries endpoint
161 register_rest_route('notificationx/v1', '/feedback-entries/export', [
162 'methods' => 'POST',
163 'callback' => [$this, 'export_feedback_entries'],
164 'permission_callback' => function() {
165 return current_user_can('read_notificationx');
166 },
167 'args' => [
168 's' => [
169 'required' => false,
170 'type' => 'string',
171 ],
172 'notification_id' => [
173 'required' => false,
174 'type' => 'string',
175 ],
176 ],
177 ]);
178 }
179
180 /**
181 * Handle popup form submission
182 *
183 * @param WP_REST_Request $request
184 * @return WP_REST_Response
185 */
186 public function handle_popup_submission($request) {
187 $popup = PopupPopupNotification::get_instance();
188 return $popup->handle_popup_submission($request);
189 }
190
191
192 /**
193 * Get feedback entries
194 *
195 * @param WP_REST_Request $request
196 * @return WP_REST_Response
197 */
198 public function get_feedback_entries($request) {
199 global $wpdb;
200
201 $table_name = $wpdb->prefix . 'nx_entries';
202
203 // Get pagination parameters
204 $page = $request->get_param('page') ?: 1;
205 $per_page = $request->get_param('per_page') ?: 20;
206 $search = $request->get_param('s') ?: '';
207 $notification_id = $request->get_param('notification_id') ?: '';
208 $offset = ($page - 1) * $per_page;
209
210 // Build WHERE clause — include both popup and exit-intent submissions
211 $sources = $this->form_sources();
212 $src_placeholders = implode(',', array_fill(0, count($sources), '%s'));
213 $where_conditions = ["e.source IN ({$src_placeholders})"];
214 $where_values = $sources;
215
216 // Add notification filter
217 if (!empty($notification_id)) {
218 $where_conditions[] = "e.nx_id = %d";
219 $where_values[] = intval($notification_id);
220 }
221
222 // Add search functionality
223 if (!empty($search)) {
224 $where_conditions[] = "(e.data LIKE %s OR e.created_at LIKE %s)";
225 $search_term = '%' . $wpdb->esc_like($search) . '%';
226 $where_values[] = $search_term;
227 $where_values[] = $search_term;
228 }
229
230 $where_clause = implode(' AND ', $where_conditions);
231
232 // Get total count for pagination
233 $total_query = $wpdb->prepare(
234 "SELECT COUNT(*) FROM {$table_name} e WHERE {$where_clause}",
235 ...$where_values
236 );
237 $total_items = (int) $wpdb->get_var($total_query);
238
239 // Get paginated entries with notification information
240 $posts_table = $wpdb->prefix . 'nx_posts';
241 $entries_query = $wpdb->prepare(
242 "SELECT e.*, p.title as notification_name, p.nx_id as notification_id
243 FROM {$table_name} e
244 LEFT JOIN {$posts_table} p ON e.nx_id = p.nx_id
245 WHERE {$where_clause}
246 ORDER BY e.created_at DESC
247 LIMIT %d OFFSET %d",
248 ...array_merge($where_values, [$per_page, $offset])
249 );
250 $entries = $wpdb->get_results($entries_query, ARRAY_A);
251
252 $formatted_entries = [];
253 foreach ($entries as $entry) {
254 $data = maybe_unserialize($entry['data']);
255 $formatted_entries[] = [
256 'id' => $entry['entry_id'],
257 'date' => $entry['created_at'],
258 'name' => $data['name'] ?? '',
259 'email' => $data['email'] ?? '',
260 'message' => $data['message'] ?? '',
261 'title' => $data['title'] ?? '',
262 'theme' => $data['theme'] ?? '',
263 'ip' => $data['ip'] ?? '',
264 'notification_name' => $entry['notification_name'] ?? '',
265 'notification_id' => $entry['notification_id'] ?? 0,
266 'nx_id' => $entry['nx_id'] ?? 0,
267 ];
268 }
269
270 return new \WP_REST_Response([
271 'entries' => $formatted_entries,
272 'total' => $total_items,
273 'page' => $page,
274 'per_page' => $per_page,
275 'total_pages' => ceil($total_items / $per_page),
276 ], 200);
277 }
278
279 /**
280 * Delete feedback entry
281 *
282 * @param WP_REST_Request $request
283 * @return WP_REST_Response
284 */
285 public function delete_feedback_entry($request) {
286 global $wpdb;
287
288 $entry_id = $request->get_param('id');
289 $table_name = $wpdb->prefix . 'nx_entries';
290
291 $sources = $this->form_sources();
292 $src_placeholders = implode(',', array_fill(0, count($sources), '%s'));
293 $delete_query = $wpdb->prepare(
294 "DELETE FROM {$table_name} WHERE entry_id = %d AND source IN ({$src_placeholders})",
295 array_merge([$entry_id], $sources)
296 );
297 $result = $wpdb->query($delete_query);
298
299 if ($result === false) {
300 return new \WP_REST_Response([
301 'success' => false,
302 'message' => __('Failed to delete entry', 'notificationx'),
303 ], 500);
304 }
305
306 return new \WP_REST_Response([
307 'success' => true,
308 'message' => __('Entry deleted successfully', 'notificationx'),
309 ], 200);
310 }
311
312 /**
313 * Bulk delete feedback entries
314 *
315 * @param WP_REST_Request $request
316 * @return WP_REST_Response
317 */
318 public function bulk_delete_feedback_entries($request) {
319 global $wpdb;
320
321 $entry_ids = $request->get_param('ids');
322 $table_name = $wpdb->prefix . 'nx_entries';
323
324 if (empty($entry_ids) || !is_array($entry_ids)) {
325 return new \WP_REST_Response([
326 'success' => false,
327 'message' => __('No entries selected for deletion', 'notificationx'),
328 ], 400);
329 }
330
331 // Sanitize entry IDs
332 $entry_ids = array_map('absint', $entry_ids);
333 $entry_ids = array_filter($entry_ids); // Remove any zero values
334
335 if (empty($entry_ids)) {
336 return new \WP_REST_Response([
337 'success' => false,
338 'message' => __('Invalid entry IDs provided', 'notificationx'),
339 ], 400);
340 }
341
342 // Create placeholders for the IN clauses
343 $placeholders = implode(',', array_fill(0, count($entry_ids), '%d'));
344 $sources = $this->form_sources();
345 $src_placeholders = implode(',', array_fill(0, count($sources), '%s'));
346
347 // Prepare the query with source filter
348 $query = $wpdb->prepare(
349 "DELETE FROM {$table_name} WHERE entry_id IN ({$placeholders}) AND source IN ({$src_placeholders})",
350 array_merge($entry_ids, $sources)
351 );
352
353 $result = $wpdb->query($query);
354
355 if ($result === false) {
356 return new \WP_REST_Response([
357 'success' => false,
358 'message' => __('Failed to delete entries', 'notificationx'),
359 ], 500);
360 }
361
362 return new \WP_REST_Response([
363 'success' => true,
364 'message' => sprintf(
365 /* translators: %d: Number of entries deleted */
366 _n('%d entry deleted successfully', '%d entries deleted successfully', $result, 'notificationx'),
367 $result
368 ),
369 'deleted_count' => $result,
370 ], 200);
371 }
372
373 /**
374 * Export feedback entries
375 *
376 * @param WP_REST_Request $request
377 * @return WP_REST_Response
378 */
379 public function export_feedback_entries($request) {
380 global $wpdb;
381
382 $table_name = $wpdb->prefix . 'nx_entries';
383 $search = $request->get_param('s') ?: '';
384 $notification_id = $request->get_param('notification_id') ?: '';
385
386 // Build WHERE clause — include both popup and exit-intent submissions
387 $sources = $this->form_sources();
388 $src_placeholders = implode(',', array_fill(0, count($sources), '%s'));
389 $where_conditions = ["e.source IN ({$src_placeholders})"];
390 $where_values = $sources;
391
392 // Add notification filter if provided
393 if (!empty($notification_id)) {
394 $where_conditions[] = "e.nx_id = %d";
395 $where_values[] = intval($notification_id);
396 }
397
398 // Add search functionality if provided
399 if (!empty($search)) {
400 $where_conditions[] = "(e.data LIKE %s OR e.created_at LIKE %s)";
401 $search_term = '%' . $wpdb->esc_like($search) . '%';
402 $where_values[] = $search_term;
403 $where_values[] = $search_term;
404 }
405
406 $where_clause = implode(' AND ', $where_conditions);
407
408 // Get all entries for export (no pagination)
409 $posts_table = $wpdb->prefix . 'nx_posts';
410 $query = $wpdb->prepare(
411 "SELECT e.entry_id, e.nx_id, e.data, e.created_at, p.title as notification_name
412 FROM {$table_name} e
413 LEFT JOIN {$posts_table} p ON e.nx_id = p.nx_id
414 WHERE {$where_clause}
415 ORDER BY e.created_at DESC",
416 ...$where_values
417 );
418
419 $entries = $wpdb->get_results($query, ARRAY_A);
420
421 if (empty($entries)) {
422 return new \WP_REST_Response([
423 'success' => false,
424 'message' => __('No entries found to export', 'notificationx'),
425 ], 404);
426 }
427
428 // Generate CSV content
429 $csv_data = $this->generate_csv_data($entries);
430
431 // Generate filename
432 $filename = 'notificationx-feedback-entries-' . date('Y-m-d-H-i-s') . '.csv';
433
434 return new \WP_REST_Response([
435 'success' => true,
436 'csv_content' => $csv_data,
437 'filename' => $filename,
438 'total_entries' => count($entries),
439 'message' => sprintf(__('Successfully prepared %d entries for export', 'notificationx'), count($entries))
440 ], 200);
441 }
442
443 /**
444 * Generate CSV data from entries
445 *
446 * @param array $entries
447 * @return string
448 */
449 private function generate_csv_data($entries) {
450 $csv_data = [];
451 $is_pro = NotificationX::is_pro();
452
453 // CSV Headers
454 $csv_headers = [
455 __('No', 'notificationx'),
456 __('Date', 'notificationx'),
457 __('NotificationX Title', 'notificationx'),
458 ];
459
460 if ($is_pro) {
461 $csv_headers[] = __('Name', 'notificationx');
462 $csv_headers[] = __('Email Address', 'notificationx');
463 }
464
465 $csv_headers[] = __('Message', 'notificationx');
466
467 $csv_data[] = $csv_headers;
468
469 // Add data rows
470 $counter = 1;
471 foreach ($entries as $entry) {
472 $data = maybe_unserialize($entry['data']);
473 $date = new \DateTime($entry['created_at']);
474
475 $row = [
476 $counter++,
477 $date->format('F j, Y'),
478 $entry['notification_name'] ?: sprintf(__('Notification #%d', 'notificationx'), $entry['nx_id']),
479 ];
480
481 if ($is_pro) {
482 $row[] = $data['name'] ?? '';
483 $row[] = $data['email'] ?? '';
484 }
485
486 $row[] = $data['message'] ?? '';
487
488 $csv_data[] = $row;
489 }
490
491 // Convert array to CSV string
492 $csv_content = '';
493 foreach ($csv_data as $row) {
494 $csv_content .= '"' . implode('","', array_map(function($field) {
495 return str_replace('"', '""', $field); // Escape quotes
496 }, $row)) . '"' . "\n";
497 }
498
499 return $csv_content;
500 }
501 }