PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.3
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.3
3.3.3 3.3.2 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 All 158 releases
notificationx / includes / Core / Rest / Posts.php

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

357 lines 13.0 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 FluentForm\Framework\Database\Query\Expression;
6 use NotificationX\Core\Database;
7 use NotificationX\Core\PostType;
8 use NotificationX\Core\REST;
9 use NotificationX\Extensions\ExtensionFactory;
10 use NotificationX\Extensions\GlobalFields;
11 use NotificationX\GetInstance;
12 use NotificationX\NotificationX;
13 use WP_REST_Controller;
14 use WP_REST_Response;
15 use WP_REST_Server;
16 use WP_Error;
17
18 /**
19 * @method static Posts get_instance($args = null)
20 */
21 class Posts extends WP_REST_Controller {
22 /**
23 * Instance of NotificationX
24 *
25 * @var NotificationX
26 */
27 use GetInstance;
28
29 /**
30 * Post type.
31 *
32 * @since 4.7.0
33 * @var string
34 */
35 protected $post_type;
36
37 /**
38 * Constructor.
39 *
40 * @since 4.7.0
41 *
42 * @param string $post_type Post type.
43 */
44 public function __construct() {
45 $this->namespace = 'notificationx/v1';
46 $this->rest_base = 'nx';
47 add_action('rest_api_init', [$this, 'register_routes']);
48 }
49
50 /**
51 * Registers the routes for the objects of the controller.
52 *
53 * @since 4.7.0
54 *
55 * @see register_rest_route()
56 */
57 public function register_routes() {
58 register_rest_route(
59 $this->namespace,
60 '/' . $this->rest_base,
61 array(
62 array(
63 'methods' => WP_REST_Server::READABLE,
64 'callback' => array($this, 'get_items'),
65 'permission_callback' => array($this, 'get_items_permissions_check'),
66 // 'args' => $this->get_collection_params(),
67 ),
68 array(
69 'methods' => WP_REST_Server::CREATABLE,
70 'callback' => array($this, 'create_item'),
71 'permission_callback' => array($this, 'create_item_permissions_check'),
72 // 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
73 ),
74 // 'schema' => array($this, 'get_public_item_schema'),
75 )
76 );
77
78 // $schema = $this->get_item_schema();
79 $get_item_args = array(
80 'context' => $this->get_context_param(array('default' => 'view')),
81 );
82
83 register_rest_route(
84 $this->namespace,
85 '/' . $this->rest_base . '/(?P<id>[\d]+)',
86 array(
87 'args' => array(
88 'id' => array(
89 'description' => __('Unique identifier for the object.', 'notificationx'),
90 'type' => 'integer',
91 ),
92 ),
93 array(
94 'methods' => WP_REST_Server::READABLE,
95 'callback' => array($this, 'get_item'),
96 'permission_callback' => array($this, 'get_item_permissions_check'),
97 // 'args' => $get_item_args,
98 ),
99 array(
100 'methods' => WP_REST_Server::EDITABLE,
101 'callback' => array($this, 'update_item'),
102 'permission_callback' => array($this, 'update_item_permissions_check'),
103 // 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
104 ),
105 array(
106 'methods' => WP_REST_Server::DELETABLE,
107 'callback' => array($this, 'delete_item'),
108 'permission_callback' => array($this, 'delete_item_permissions_check'),
109 'args' => array(
110 'force' => array(
111 'type' => 'boolean',
112 'default' => false,
113 'description' => __('Whether to bypass Trash and force deletion.', 'notificationx'),
114 ),
115 ),
116 ),
117 // 'schema' => array($this, 'get_public_item_schema'),
118 )
119 );
120 }
121
122 /**
123 * Checks if a given request has access to read posts.
124 *
125 * @since 4.7.0
126 *
127 * @param WP_REST_Request $request Full details about the request.
128 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
129 */
130 public function get_items_permissions_check($request) {
131 return current_user_can('read_notificationx');
132 }
133
134 /**
135 * Checks if a given request has access to read post.
136 *
137 * @since 4.7.0
138 *
139 * @param WP_REST_Request $request Full details about the request.
140 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
141 */
142 public function get_item_permissions_check($request) {
143 $params = $request->get_params();
144 if( !empty( $params['source'] ) ) {
145 return current_user_can('read_notificationx');
146 }
147 return current_user_can('read_notificationx');
148 }
149
150
151 public function get_items($request) {
152 $params = $request->get_params();
153 $status = !empty($params['status']) ? $params['status'] : "all";
154 $page = !empty($params['page']) ? intval( $params['page'] ) : 1;
155 $per_page = !empty($params['per_page']) ? intval( $params['per_page'] ) : 20;
156 $search_keyword = !empty($params['s']) ? sanitize_text_field($params['s']) : '';
157 $start_from = ($page - 1) * $per_page;
158 $query = Database::get_instance()->query()
159 ->from('nx_posts a')
160 ->join('nx_stats b', 'b.nx_id', '=', 'a.nx_id')
161 ->group_by('a.nx_id')
162 ->order_by('a.updated_at', 'DESC')
163 ->select('a.*, SUM(b.clicks) clicks, SUM(b.views) views');
164 if ($status !== 'all') {
165 $query->where('enabled', $status == 'enabled' ? true : false);
166 }
167 if( $search_keyword ) {
168 global $wpdb;
169 // esc_like() so `%` and `_` typed into the search box match themselves
170 // instead of acting as wildcards; the pattern is bound as a value by the
171 // query builder.
172 $like = '%' . $wpdb->esc_like( $search_keyword ) . '%';
173 $query->where(function($query) use ($like) {
174 $query->where('title', 'LIKE', $like)
175 ->orWhere( 'a.nx_id', 'LIKE', $like );
176 });
177 }
178
179 $query->offset($start_from)
180 ->limit($per_page);
181 $posts = $query->get();
182 $posts = PostType::get_instance()->__get_posts( $posts, '*' );
183
184 // Add entries count for popup notifications
185 global $wpdb;
186 $entries_table = $wpdb->prefix . 'nx_entries';
187 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- False positive: the query is prepared via $this->wpdb->prepare(), which this sniff does not recognise, and only $wpdb->prefix table names are interpolated. Audited 2026-07-16.
188 $entries_counts = $wpdb->get_results(
189 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- False positive: the query is prepared via $this->wpdb->prepare(), which this sniff does not recognise, and only $wpdb->prefix table names are interpolated. Audited 2026-07-16.
190 "SELECT nx_id, COUNT(*) as entries_count
191 FROM {$entries_table}
192 WHERE source IN ('popup_notification', 'exit_intent_custom')
193 GROUP BY nx_id",
194 ARRAY_A
195 );
196 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
197
198 // Create a lookup array for entries counts
199 $entries_lookup = [];
200 foreach ($entries_counts as $entry) {
201 $entries_lookup[$entry['nx_id']] = $entry['entries_count'];
202 }
203
204 // Add entries count to posts
205 foreach ($posts as $key => $post) {
206 if ($post['source'] === 'popup_notification') {
207 $posts[$key]['entries'] = isset($entries_lookup[$post['nx_id']]) ? $entries_lookup[$post['nx_id']] : 0;
208 }
209 }
210
211 $total_posts = Database::get_instance()->get_post(Database::$table_posts, [], 'count(*) AS total');
212 $enabled = Database::get_instance()->get_post(Database::$table_posts, ['enabled' => true], 'count(*) AS total');
213 $disabled = Database::get_instance()->get_post(Database::$table_posts, ['enabled' => false], 'count(*) AS total');
214
215 return [
216 'total' => $total_posts['total'],
217 'enabled' => $enabled['total'],
218 'disabled' => $disabled['total'],
219 'search_keyword' => $search_keyword,
220 'posts' => $posts,
221 ];
222
223 }
224
225 /**
226 * Retrieves a single post.
227 *
228 * @since 4.7.0
229 *
230 * @param WP_REST_Request $request Full details about the request.
231 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
232 */
233 public function get_item($request) {
234 PostType::get_instance()->set_context( 'edit' );
235 $post = PostType::get_instance()->get_post( absint( $request['id'] ) );
236 if ( empty( $post ) ) {
237 return new WP_Error(
238 'rest_post_invalid_id',
239 __( 'Invalid notification ID.', 'notificationx' ),
240 array( 'status' => 404 )
241 );
242 }
243 return $post;
244 }
245
246 /**
247 * Checks if a given request has access to create a post.
248 *
249 * @since 4.7.0
250 *
251 * @param WP_REST_Request $request Full details about the request.
252 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
253 */
254 public function create_item_permissions_check($request) {
255 if (!empty($request['id'])) {
256 return new WP_Error(
257 'rest_post_exists',
258 __('Cannot create existing post.', 'notificationx'),
259 array('status' => 400)
260 );
261 }
262
263 return current_user_can('edit_notificationx');
264 }
265
266 /**
267 * Creates a single post.
268 *
269 * @since 4.7.0
270 *
271 * @param WP_REST_Request $request Full details about the request.
272 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
273 */
274 public function create_item($request) {
275 if (!empty($request['nx_id'])) {
276 return new WP_Error(
277 'rest_post_exists',
278 __('Cannot create existing post.', 'notificationx'),
279 array('status' => 400)
280 );
281 }
282
283 // $prepared_post = $this->prepare_item_for_database($request);
284
285 // if (is_wp_error($prepared_post)) {
286 // return $prepared_post;
287 // }
288
289 $params = $request->get_params();
290 return PostType::get_instance()->save_post($params);
291 }
292
293 /**
294 * Checks if a given request has access to update a post.
295 *
296 * @since 4.7.0
297 *
298 * @param WP_REST_Request $request Full details about the request.
299 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
300 */
301 public function update_item_permissions_check($request) {
302 $params = $request->get_params();
303 if( !empty( $params['source'] ) ) {
304 return current_user_can('edit_notificationx');
305 }
306 return current_user_can('edit_notificationx');
307 }
308
309 /**
310 * Updates a single post.
311 *
312 * @since 4.7.0
313 *
314 * @param WP_REST_Request $request Full details about the request.
315 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
316 */
317 public function update_item($request) {
318 $params = $request->get_params();
319 return PostType::get_instance()->save_post($params);
320 }
321
322 /**
323 * Checks if a given request has access to delete a post.
324 *
325 * @since 4.7.0
326 *
327 * @param WP_REST_Request $request Full details about the request.
328 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
329 */
330 public function delete_item_permissions_check($request) {
331 // if ($post && !$this->check_delete_permission($post)) {
332 // return new WP_Error(
333 // 'rest_cannot_delete',
334 // __('Sorry, you are not allowed to delete this post.'),
335 // array('status' => rest_authorization_required_code())
336 // );
337 // }
338 return current_user_can('edit_notificationx');
339 }
340
341 /**
342 * Deletes a single post.
343 *
344 * @since 4.7.0
345 *
346 * @param WP_REST_Request $request Full details about the request.
347 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
348 */
349 public function delete_item($request) {
350 if(PostType::get_instance()->delete_post($request['id'])){
351 wp_send_json_success();
352 }
353 wp_send_json_error();
354 }
355
356 }
357