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/Admin/Entries.php +167 -0 0.2.5.6trunk View file →
@@ -1,0 +1,167 @@
1 +<?php
2 +
3 +/**
4 + * Extension Factory
5 + *
6 + * @package NotificationX\Extensions
7 + */
8 +
9 +namespace NotificationX\Admin;
10 +
11 +use NotificationX\Core\Database;
12 +use NotificationX\Core\Helper;
13 +use NotificationX\GetInstance;
14 +
15 +/**
16 + * @method static Entries get_instance($args = null)
17 + */
18 +class Entries {
19 + /**
20 + * Instance of Entries
21 + *
22 + * @var Entries
23 + */
24 + use GetInstance;
25 +
26 + protected $wpdb;
27 + protected $count = [];
28 + public $format = [
29 + 'entry_id' => '%d',
30 + 'nx_id' => '%d',
31 + 'source' => '%s',
32 + 'entry_key' => '%s',
33 + 'data' => '%s',
34 + 'created_at' => '%s',
35 + 'updated_at' => '%s',
36 + ];
37 +
38 + /**
39 + * Initially Invoked when initialized.
40 + * @hook init
41 + */
42 + public function __construct() {
43 + global $wpdb;
44 + $this->wpdb = $wpdb;
45 + }
46 +
47 + public function count($source, $col = 'source'){
48 + if(empty($this->count)){
49 + $this->count = Database::get_instance()->get_source_count(Database::$table_entries, $col, [$col => $source]);
50 + }
51 + if(!empty($this->count[$source])){
52 + return $this->count[$source];
53 + }
54 + elseif(!empty($source)){
55 + return 0;
56 + }
57 + return $this->count;
58 + }
59 +
60 +
61 + /**
62 + * Recursively sanitize the user-controlled `data` payload of an entry
63 + * before it is stored. Entry data frequently originates from low-trust,
64 + * even unauthenticated sources — public form submissions (Gravity Forms,
65 + * CF7, FluentForm, WPForms, Ninja Forms, WeForms, Formidable, …) and the
66 + * Zapier / IFTTT REST ingest — and is later rendered on the frontend. The
67 + * JS renderer only escapes string-typed values, so an array-wrapped value
68 + * (e.g. `name => ['<img src=x onerror=alert(1)>']`) is coerced back to its
69 + * raw string and injected via `dangerouslySetInnerHTML` — a stored XSS
70 + * (Patchstack, NotificationX Pro <= 3.1.3). insert_entry()/insert_entries()
71 + * are the single storage chokepoint every extension (Free and Pro) funnels
72 + * through, so sanitizing every string leaf here closes that class of issue
73 + * regardless of ingest path. Non-string scalars (ints, bools, timestamps)
74 + * are left untouched so downstream typing is preserved; only strings can
75 + * carry markup.
76 + *
77 + * @param mixed $data
78 + * @return mixed
79 + */
80 + public function sanitize_entry_data($data) {
81 + if (is_array($data)) {
82 + array_walk_recursive($data, function (&$val) {
83 + if (is_string($val)) {
84 + $val = sanitize_text_field($val);
85 + }
86 + });
87 + } elseif (is_string($data)) {
88 + $data = sanitize_text_field($data);
89 + }
90 + return $data;
91 + }
92 +
93 + public function insert_entry($entry) {
94 + if(empty($entry['data'])){
95 + return false;
96 + }
97 + $entry['data'] = $this->sanitize_entry_data($entry['data']);
98 + $timestamp = !empty($entry['data']['timestamp']) ? $entry['data']['timestamp'] : time();
99 + if(empty($entry['created_at'])){
100 + $entry['created_at'] = Helper::mysql_time($timestamp);
101 + }
102 + if(empty($entry['updated_at'])){
103 + $entry['updated_at'] = Helper::mysql_time($timestamp);
104 + }
105 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
106 + $entry = apply_filters('nx_insert_entry', $entry);
107 + $result = Database::get_instance()->insert_post(Database::$table_entries, $entry, $this->format);
108 + if ( $result ) {
109 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
110 + do_action( 'nx_after_entry_inserted', $entry );
111 + }
112 + return $result;
113 + }
114 +
115 + public function insert_entries($entries) {
116 + foreach ($entries as $key => $entry) {
117 + if(empty($entry['data'])){
118 + unset($entries[$key]);
119 + continue;
120 + }
121 + $entry['data'] = $this->sanitize_entry_data($entry['data']);
122 + $timestamp = !empty($entry['data']['timestamp']) ? $entry['data']['timestamp'] : time();
123 + if(empty($entry['created_at'])){
124 + $entry['created_at'] = Helper::mysql_time($timestamp);
125 + }
126 + if(empty($entry['updated_at'])){
127 + $entry['updated_at'] = Helper::mysql_time($timestamp);
128 + }
129 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
130 + $entries[$key] = apply_filters('nx_insert_entry', $entry);
131 + }
132 + return Database::get_instance()->insert_posts(Database::$table_entries, $entries, $this->format);
133 + }
134 +
135 + public function get_entries($where__or_nx_id = [], $select = "*", $join_table = '', $group_by_col = '', $data_in_entry = false) {
136 + if (is_int($where__or_nx_id)) {
137 + $where__or_nx_id = ['nx_id' => $where__or_nx_id];
138 + }
139 + $entries = Database::get_instance()->get_posts(Database::$table_entries, $select, $where__or_nx_id, $join_table, $group_by_col, '', 'ORDER BY `created_at` DESC');
140 + if ($data_in_entry) {
141 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
142 + $entries = apply_filters('nx_get_entries', $entries);
143 + return $entries;
144 + }
145 + foreach ($entries as $key => $value) {
146 + if (!empty($value['data'])) {
147 + $value = array_merge($value['data'], $value);
148 + unset($value['data']);
149 + }
150 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
151 + $entries[$key] = apply_filters('nx_get_entry', $value);
152 + }
153 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
154 + $entries = apply_filters('nx_get_entries', $entries);
155 + return $entries;
156 + }
157 +
158 + public function delete_entries($where__or_nx_id, $limit = 0) {
159 + if (!is_array($where__or_nx_id)) {
160 + $where__or_nx_id = ['nx_id' => $where__or_nx_id];
161 + }
162 + $results = Database::get_instance()->delete_posts(Database::$table_entries, $where__or_nx_id, $limit);
163 + // @todo add action.
164 + return $results;
165 + }
166 +
167 +}