PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / trunk
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar vtrunk
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
← All changes | includes/Core/Rest/Posts.php +348 -0 0.2.5.6trunk View file →
@@ -1,0 +1,348 @@
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 +}