PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.0
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.0
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 / Posts.php

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

349 lines 12.8 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 return PostType::get_instance()->get_post( absint( $request['id'] ) );
236 }
237
238 /**
239 * Checks if a given request has access to create a post.
240 *
241 * @since 4.7.0
242 *
243 * @param WP_REST_Request $request Full details about the request.
244 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
245 */
246 public function create_item_permissions_check($request) {
247 if (!empty($request['id'])) {
248 return new WP_Error(
249 'rest_post_exists',
250 __('Cannot create existing post.', 'notificationx'),
251 array('status' => 400)
252 );
253 }
254
255 return current_user_can('edit_notificationx');
256 }
257
258 /**
259 * Creates a single post.
260 *
261 * @since 4.7.0
262 *
263 * @param WP_REST_Request $request Full details about the request.
264 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
265 */
266 public function create_item($request) {
267 if (!empty($request['nx_id'])) {
268 return new WP_Error(
269 'rest_post_exists',
270 __('Cannot create existing post.', 'notificationx'),
271 array('status' => 400)
272 );
273 }
274
275 // $prepared_post = $this->prepare_item_for_database($request);
276
277 // if (is_wp_error($prepared_post)) {
278 // return $prepared_post;
279 // }
280
281 $params = $request->get_params();
282 return PostType::get_instance()->save_post($params);
283 }
284
285 /**
286 * Checks if a given request has access to update a post.
287 *
288 * @since 4.7.0
289 *
290 * @param WP_REST_Request $request Full details about the request.
291 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
292 */
293 public function update_item_permissions_check($request) {
294 $params = $request->get_params();
295 if( !empty( $params['source'] ) ) {
296 return current_user_can('edit_notificationx');
297 }
298 return current_user_can('edit_notificationx');
299 }
300
301 /**
302 * Updates a single post.
303 *
304 * @since 4.7.0
305 *
306 * @param WP_REST_Request $request Full details about the request.
307 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
308 */
309 public function update_item($request) {
310 $params = $request->get_params();
311 return PostType::get_instance()->save_post($params);
312 }
313
314 /**
315 * Checks if a given request has access to delete a post.
316 *
317 * @since 4.7.0
318 *
319 * @param WP_REST_Request $request Full details about the request.
320 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
321 */
322 public function delete_item_permissions_check($request) {
323 // if ($post && !$this->check_delete_permission($post)) {
324 // return new WP_Error(
325 // 'rest_cannot_delete',
326 // __('Sorry, you are not allowed to delete this post.'),
327 // array('status' => rest_authorization_required_code())
328 // );
329 // }
330 return current_user_can('edit_notificationx');
331 }
332
333 /**
334 * Deletes a single post.
335 *
336 * @since 4.7.0
337 *
338 * @param WP_REST_Request $request Full details about the request.
339 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
340 */
341 public function delete_item($request) {
342 if(PostType::get_instance()->delete_post($request['id'])){
343 wp_send_json_success();
344 }
345 wp_send_json_error();
346 }
347
348 }
349