PluginProbe ʕ •ᴥ•ʔ
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets / 4.2.2
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets v4.2.2
4.2.5 4.2.4 trunk 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.2 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8 3.8.1 3.8.10 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.8.9.1 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.5.1 4.0.6 4.0.6.1 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2.0 4.2.1 4.2.2 4.2.3
widget-options / includes / extras.php
widget-options / includes Last commit date
admin 3 months ago pagebuilders 3 months ago snippets 3 months ago widgets 3 months ago ajax-functions.php 3 months ago extras.php 3 months ago install.php 3 months ago scripts.php 3 months ago transient.php 3 months ago
extras.php
910 lines
1 <?php
2
3 /**
4 * Extra Functions
5 *
6 * Collections of extra functions to avoid repeatition
7 *
8 * @copyright Copyright (c) 2016, Jeffrey Carandang
9 * @since 4.0
10 */
11
12 function widgetopts_sanitize_array(&$array)
13 {
14 foreach ($array as &$value) {
15 if (!is_array($value)) {
16 // sanitize if value is not an array
17 $value = sanitize_text_field($value);
18 } else {
19 // go inside this function again
20 widgetopts_sanitize_array($value);
21 }
22 }
23
24 return $array;
25 }
26
27 function widgetopts_is_checked($array, $key)
28 {
29 return (isset($array[$key]) && '1' == $array[$key]) ? 'checked="checked"' : '';
30 }
31
32 /*
33 * Check if http or https available on link
34 */
35 function widgetopts_addhttp($url)
36 {
37 if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {
38 $url = "http://" . $url;
39 }
40 return $url;
41 }
42
43
44 /**
45 * Register Global Variables for easier access
46 *
47 *
48 * @since 5.0
49 * @return array
50 */
51 function widgetopts_global_taxonomies()
52 {
53 $taxonomies = get_option('widgetopts_global_taxonomies');
54
55 if (empty($taxonomies)) {
56
57 $tax_args = array(
58 'public' => true
59 );
60 $tax_output = 'objects'; // or objects
61 $tax_operator = 'and'; // 'and' or 'or'
62 $taxonomies = get_taxonomies($tax_args, $tax_output, $tax_operator);
63 unset($taxonomies['post_format']);
64
65 // Let's let devs alter that value coming in
66 $taxonomies = apply_filters('widgetopts_update_global_taxonomies', $taxonomies);
67 update_option('widgetopts_global_taxonomies', $taxonomies);
68 }
69
70 return apply_filters('widgetopts_get_global_taxonomies', $taxonomies);
71 }
72
73 function widgetopts_global_types()
74 {
75 /*$types = get_option( 'widgetopts_global_types' );
76 if( empty( $types ) ) {
77 $types = get_post_types( array(
78 'public' => true,
79 ), 'object' );
80 // Let's let devs alter that value coming in
81 $types = apply_filters( 'widgetopts_update_global_types', $types );
82 update_option( 'widgetopts_global_types', $types );
83 }*/
84
85 $types = get_post_types(array('public' => true), 'object');
86 $types = apply_filters('widgetopts_update_global_types', $types);
87
88 return apply_filters('widgetopts_get_global_types', $types);
89 }
90
91 function widgetopts_global_pages()
92 {
93 $pages = get_option('widgetopts_global_all_pages');
94
95 //old pages object
96 // if( empty( $pages ) ) {
97 // $pages = get_posts( array(
98 // 'post_type' => 'page',
99 // 'post_status' => 'publish',
100 // 'numberposts' => -1,
101 // 'orderby' => 'title',
102 // 'order' => 'ASC',
103 // 'fields' => array('ID', 'name')
104 // ));
105 //
106 // // Let's let devs alter that value coming in
107 // $pages = apply_filters( 'widgetopts_update_global_pages', $pages );
108 // update_option( 'widgetopts_global_pages', $pages );
109 // }
110
111 //create new pages object
112 if (empty($pages)) {
113 global $wpdb;
114
115 $pages = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_type = 'page' AND post_status = 'publish' ORDER BY post_title ASC ");
116
117 // Let's let devs alter that value coming in
118 $pages = apply_filters('widgetopts_update_global_pages', $pages);
119 update_option('widgetopts_global_all_pages', $pages);
120 }
121
122 return apply_filters('widgetopts_get_global_pages', $pages);
123 }
124
125 function widgetopts_global_categories()
126 {
127 $categories = get_option('widgetopts_global_categories');
128
129 if (empty($categories)) {
130 $categories = get_categories(array(
131 'hide_empty' => false
132 ));
133
134 // Let's let devs alter that value coming in
135 $categories = apply_filters('widgetopts_update_global_categories', $categories);
136 update_option('widgetopts_global_categories', $categories);
137 }
138
139 return apply_filters('widgetopts_global_categories', $categories);
140 }
141
142 /*
143 Page Walker Class
144 */
145 if (!class_exists('WidgetOpts_Pages_Checkboxes')) :
146 class WidgetOpts_Pages_Checkboxes extends Walker_Page
147 {
148
149 function start_lvl(&$output, $depth = 0, $args = array())
150 {
151 $output .= "\n<div class='widgetopts-chldrn'>\n";
152 }
153
154 function end_lvl(&$output, $depth = 0, $args = array())
155 {
156 $output .= "</div>\n";
157 }
158
159 function start_el(&$output, $page, $depth = 0, $args = array(), $current_page = 0)
160 {
161 if ($depth) {
162 $indent = str_repeat('&mdash; ', $depth);
163 } else {
164 $indent = '';
165 }
166
167
168
169 if ('' === $page->post_title) {
170 $page->post_title = sprintf(__('#%d (no title)', 'widget-options'), $page->ID);
171 }
172
173 $pages_values = array();
174 if (isset($args['params']['visibility']['pages'])) {
175 $pages_values = $args['params']['visibility']['pages'];
176 }
177
178 if (isset($pages_values[$page->ID]) && $pages_values[$page->ID] == '1') {
179 $checked = 'checked="checked"';
180 } else {
181 $checked = '';
182 }
183
184 $output .= '<p>' . $indent;
185
186 $output .= '<input type="checkbox" name="' . $args['namespace'] . '[extended_widget_opts][visibility][pages][' . $page->ID . ']" id="' . $args['id'] . '-opts-pages-' . $page->ID . '" value="1" ' . $checked . '/>';
187
188 $output .= '<label for="' . $args['id'] . '-opts-pages-' . $page->ID . '">' . $page->post_title . '</label>';
189 }
190
191 function end_el(&$output, $page, $depth = 0, $args = array())
192 {
193 $output .= "</p>\n";
194 }
195 }
196 endif;
197
198 function widgetopts_set_http_headers()
199 {
200 $httpheaders = [];
201 foreach ($_SERVER as $key => $value) {
202 if (substr($key, 0, 5) === 'HTTP_') {
203 $httpheaders[$key] = $value;
204 }
205 }
206 return $httpheaders;
207 }
208
209 /**
210 * Checks if the request is coming from a mobile device based on HTTP headers.
211 *
212 * @return bool True if the request is from a mobile device, false otherwise.
213 */
214 function widgetopts_is_mobile()
215 {
216 global $widgetopts_http_headers;
217
218 // Define mobile headers and their corresponding values
219 $mobile_headers = [
220 'HTTP_ACCEPT' => ['values' => ['application/x-obml2d', 'application/vnd.rim.html', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml']],
221 'HTTP_UA_CPU' => ['values' => ['ARM']],
222 'HTTP_X_WAP_PROFILE' => null,
223 'HTTP_PROFILE' => null,
224 'HTTP_X_HUAWEI_USERID' => null,
225 'HTTP_X_WAP_CLIENTID' => null,
226 'HTTP_WAP_CONNECTION' => null,
227 'HTTP_X_OPERAMINI_PHONE_UA' => null,
228 'HTTP_X_NOKIA_GATEWAY_ID' => null,
229 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
230 'HTTP_X_ORANGE_ID' => null,
231 'HTTP_X_ATT_DEVICEID' => null,
232 'HTTP_UA_OS' => null,
233 'HTTP_X_MOBILE_GATEWAY' => null,
234 ];
235
236 // Check each mobile header for a match
237 foreach ($mobile_headers as $key => $value) {
238 if (isset($widgetopts_http_headers[$key])) {
239 if (isset($value['values']) && is_array($value['values'])) {
240 foreach ($value['values'] as $_match) {
241 if (strpos($widgetopts_http_headers[$key], $_match) !== false) {
242 return true; // Found a match, request is from a mobile device
243 }
244 }
245 } else {
246 return true; // Found a match, request is from a mobile device
247 }
248 }
249 }
250
251 // If no match is found, try another method
252 return widgetopts_is_mobile_or_tablet();
253 }
254
255 function widgetopts_is_mobile_or_tablet($tablet_only = false)
256 {
257 global $widgetopts_user_agent;
258 $user_agent = $widgetopts_user_agent;
259
260 // Check for common mobile and tablet User-Agent patterns
261 $mobile_patterns = array(
262 '/android/i',
263 '/iphone|ipod/i',
264 '/blackberry/i',
265 '/iemobile/i',
266 '/windows phone/i',
267 '/opera mini/i',
268 '/mobile safari/i'
269 );
270
271 $tablet_patterns = array(
272 '/ipad/i',
273 '/android(?!.*mobile)/i',
274 '/kindle|silk/i',
275 '/playbook/i',
276 '/tablet/i'
277 );
278
279 if ($tablet_only === false) {
280 // Check if the User-Agent matches any of the mobile patterns
281 foreach ($mobile_patterns as $pattern) {
282 if (preg_match($pattern, $user_agent)) {
283 return true; // Request is from a mobile device
284 }
285 }
286 }
287
288 // Check if the User-Agent matches any of the tablet patterns
289 foreach ($tablet_patterns as $pattern) {
290 if (preg_match($pattern, $user_agent)) {
291 return true; // Request is from a tablet device
292 }
293 }
294
295 // Additional checks for specific tablet devices or browsers
296 if (stripos($user_agent, 'Macintosh') !== false && stripos($user_agent, 'iPad') !== false) {
297 return true; // Request is from an iPad
298 }
299
300 return false; // Request is neither from a mobile nor tablet device (default to desktop)
301 }
302
303 /**
304 * Check if the device is tablet.
305 * Returns true if any type of tablet device detected
306 * @return bool
307 */
308 function widgetopts_is_tablet()
309 {
310 global $widgetopts_user_agent;
311
312 $tablets = array(
313 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
314 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
315 'GoogleTablet' => 'Android.*Pixel C',
316 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
317 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40',
318 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C',
319 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
320 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
321 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
322 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
323 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
324 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
325 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
326 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
327 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
328 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
329 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X',
330 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
331 'XiaomiTablet' => '21051182G',
332 'iPad' => 'iPad|iPad.*Mobile',
333 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
334 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
335 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
336 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
337 'IRUTablet' => 'M702pro',
338 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
339 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
340 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
341 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
342 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
343 'NokiaLumiaTablet' => 'Lumia 2520',
344 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
345 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
346 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
347 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
348 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
349 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
350 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
351 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
352 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
353 'FlyTablet' => 'IQ310|Fly Vision',
354 'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
355 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03',
356 'NecTablet' => '\bN-06D|\bN-08D',
357 'PantechTablet' => 'Pantech.*P4100',
358 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
359 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
360 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
361 'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900',
362 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
363 'NabiTablet' => 'Android.*\bNabi',
364 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
365 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
366 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
367 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
368 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
369 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
370 'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
371 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
372 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
373 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
374 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
375 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
376 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
377 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026',
378 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
379 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
380 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
381 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
382 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
383 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
384 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
385 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
386 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
387 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
388 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
389 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
390 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
391 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
392 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
393 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
394 'iMobileTablet' => 'i-mobile i-note',
395 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
396 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
397 'AMPETablet' => 'Android.* A78 ',
398 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
399 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
400 'TecnoTablet' => 'TECNO P9|TECNO DP8D',
401 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
402 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
403 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
404 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
405 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
406 'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
407 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
408 'CaptivaTablet' => 'CAPTIVA PAD',
409 'KocasoTablet' => '\b(TB-1207)\b',
410 'HisenseTablet' => '\b(F5281|E2371)\b',
411 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
412 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
413 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
414 'JaytechTablet' => 'TPC-PA762',
415 'TelstraTablet' => 'T-Hub2',
416 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
417 'Hudl' => 'Hudl HT7S3|Hudl 2',
418 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
419 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
420 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
421 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107',
422 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
423 'PocketBookTablet' => 'Pocketbook',
424 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
425 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
426 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
427 'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
428 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
429 'UbislateTablet' => 'UbiSlate[\s]?7C',
430 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
431 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
432 );
433
434 $is_tablet = false;
435 foreach ($tablets as $brand => $pattern) {
436 // Check if the tablet matches the current brand's pattern
437 if (preg_match('#' . $pattern . '#i', $widgetopts_user_agent)) {
438 // Tablet matched, so echo the brand
439 $is_tablet = true;
440 break;
441 }
442 }
443
444 // If no match is found, try another method, check only the most common keyword
445 if (!$is_tablet) {
446 $is_tablet = widgetopts_is_mobile_or_tablet(true);
447 }
448
449 return $is_tablet;
450 }
451
452 /**
453 * Retrieves the user agent string from HTTP headers.
454 *
455 * @return string The concatenated user agent string.
456 */
457 function widgetopts_get_user_agent()
458 {
459 global $widgetopts_http_headers;
460
461 $user_agent = '';
462
463 // Define the user agent headers to check
464 $user_agents = [
465 'HTTP_USER_AGENT',
466 'HTTP_X_ORIGINAL_USER_AGENT',
467 'HTTP_X_OPERAMINI_PHONE_UA',
468 'HTTP_X_BOLT_PHONE_UA',
469 'HTTP_X_DEVICE_USER_AGENT',
470 'HTTP_X_SKYFIRE_PHONE',
471 'HTTP_X_UCBROWSER_DEVICE_UA',
472 'HTTP_DEVICE_STOCK_UA',
473 ];
474
475 // Concatenate user agents from available headers
476 foreach ($user_agents as $ua) {
477 if (isset($widgetopts_http_headers[$ua]) && !empty($widgetopts_http_headers[$ua])) {
478 $user_agent .= $widgetopts_http_headers[$ua] . " ";
479 }
480 }
481
482 return trim($user_agent); // Trim the concatenated user agent string and return
483 }
484
485 /**
486 * Safely evaluates a boolean expression and prevents any output from being printed.
487 *
488 * This function uses eval() to evaluate the provided expression. It performs
489 * basic validation to ensure that the expression consists of valid boolean logic.
490 * Output generated by the eval() is suppressed using output buffering.
491 *
492 * @param string $expression The boolean expression to evaluate.
493 * @return bool Returns true or false based on the evaluated expression, or false on error.
494 */
495 function widgetopts_safe_eval($expression)
496 {
497 if (widgetopts_is_widget_or_post_preview()) {
498 // Always return true for previews unless the user is an administrator
499 if (!current_user_can('administrator')) {
500 return true;
501 }
502 }
503
504 $validation_result = widgetopts_validate_expression($expression);
505
506 if ($validation_result['valid'] === false) {
507 return false;
508 }
509
510 if (stristr($expression, "return") === false) {
511 $expression = "return (" . $expression . ");";
512 }
513
514 ob_start();
515 try {
516 $result = (bool) (@eval($expression));
517 } catch (\Exception $e) {
518 $result = false;
519 } catch (\Error $e) {
520 $result = false;
521 } catch (\ParseError $e) {
522 $result = false;
523 } catch (\Throwable $e) {
524 $result = false;
525 }
526 ob_end_clean();
527
528 return $result;
529 }
530
531 /**
532 * Validate the expression if it contains dangerous and unallowed patterns.
533 */
534 function widgetopts_validate_expression($expression)
535 {
536 // Ensure the expression has no backtick execution
537 if (stripos($expression, '`') !== false) {
538 return ['valid' => false, 'message' => 'Backticks execution is not allowed.'];
539 }
540
541 // List of potentially harmful patterns and their messages
542 $dangerous_patterns = [
543 '/\b(insert|update|delete|replace|select|drop|alter|truncate|grant|revoke)\b/i' => 'Potential SQL injection detected.',
544 '/\b(wp_insert_post|wp_update_post|wp_delete_post|wp_insert_user|wp_update_user|wp_delete_user|add_option|update_option|delete_option|wpdb)\b/i' => 'Unsafe WordPress database functions found.',
545 '/\b(file_put_contents|file_get_contents|fopen|fwrite|unlink|rename|chmod|chown|chgrp|copy|scandir)\b/i' => 'File system manipulation functions are not allowed.',
546 '/\b(wp_remote_get|wp_remote_post|curl_init|curl_exec|curl_setopt|open_basedir|fsockopen|proc_nice|stream_socket_server|stream_socket_client)\b/i' => 'Potential remote execution functions detected.',
547 '/\b(eval|assert|system|exec|shell_exec|passthru|proc_open|popen|pcntl_exec|dl|include|require|include_once|require_once)\b/i' => 'Execution functions are not allowed.',
548 '/\b(base64_decode|hex2bin|mb_decode_mimeheader|str_rot13)\b/i' => 'Encoding/decoding functions that may be used for obfuscation are not allowed.',
549 '/\b(call_user_func|call_user_func_array|create_function|compact|extract|parse_str|ReflectionClass|ReflectionMethod|ReflectionProperty)\b/i' => 'Dynamic function execution is not allowed.',
550 '/\b(str_replace|str_ireplace|preg_replace|preg_replace_callback|preg_replace_callback_array)\b/i' => 'String replacement functions are restricted due to potential obfuscation.',
551 '/`[^`]*`/' => 'Backticks execution is not allowed.',
552 '/\[\s*[\'"]?[a-zA-Z0-9_]+\s*\.\s*[\'"]?[a-zA-Z0-9_]+\s*\]/i' => 'Concatenated function execution is not allowed.',
553 '/\b(str_replace|preg_replace|preg_replace_callback|preg_replace_callback_array)\s*\(\s*[\'"]\s*\.\s*[\'"]/' => 'Potential function name obfuscation detected.',
554 '/\$\w+\s*\[\s*[\'"]?\d+[\'"]?\s*\]\s*\(/' => 'Dynamic function execution using arrays is not allowed.',
555 ];
556
557 $is_valid = true;
558 // Check for dangerous patterns
559 foreach ($dangerous_patterns as $pattern => $message) {
560 if (preg_match($pattern, $expression)) {
561 $is_valid = false;
562 break;
563 }
564 }
565
566 if ($is_valid === false) {
567 return ['valid' => false, 'message' => $message];
568 }
569
570 // Retrieve allowed functions
571 $allowed_functions = array_merge(widgetopts_get_allowed_php_functions(), widgetopts_get_allowed_wp_functions());
572 $allowed_functions = apply_filters('widgetopts_allowed_php_functions', $allowed_functions);
573
574 // Validate using token-based function checking
575 $is_valid = widgetopts_validate_code_with_tokens($expression, $allowed_functions);
576 $is_valid = apply_filters('widgetopts_validate_allowed_commands', $is_valid);
577
578 // Return validation result
579 return $is_valid ? ['valid' => true, 'message' => 'Expression is safe.'] : ['valid' => false, 'message' => 'Expression contains disallowed function calls.'];
580 }
581
582 /**
583 * Returns a list of allowed PHP functions.
584 */
585 function widgetopts_get_allowed_php_functions()
586 {
587 return [
588 // String Manipulation
589 'strlen',
590 'substr',
591 'strtolower',
592 'strtoupper',
593 'trim',
594 'ltrim',
595 'rtrim',
596 'preg_match',
597 'explode',
598 'implode',
599 'htmlspecialchars',
600 'htmlentities',
601 'strip_tags',
602 'str_repeat',
603 'str_split',
604 'strpos',
605 'strrpos',
606 'stripos',
607 'strripos',
608 'substr_replace',
609 'wordwrap',
610
611 // Array Manipulation
612 'in_array',
613 'count',
614 'sizeof',
615
616 // Math Functions
617 'abs',
618 'round',
619 'floor',
620 'ceil',
621 'min',
622 'max',
623 'pow',
624 'sqrt',
625 'rand',
626 'mt_rand',
627 'number_format',
628 'log',
629 'exp',
630 'bcadd',
631 'bcsub',
632 'bcmul',
633 'bcdiv',
634
635 // Date/Time
636 'time',
637 'date',
638 'strtotime',
639 'mktime',
640 'checkdate',
641 'date_default_timezone_get',
642 'gmdate',
643
644 // JSON Functions
645 'json_decode',
646 'json_encode',
647
648 // Variable Handling
649 'isset',
650 'empty',
651 'unset',
652 'is_array',
653 'is_bool',
654 'is_callable',
655 'is_countable',
656 'is_double',
657 'is_float',
658 'is_int',
659 'is_integer',
660 'is_iterable',
661 'is_long',
662 'is_null',
663 'is_numeric',
664 'is_object',
665 'is_real',
666 'is_scalar',
667 'is_string',
668 'is_subclass_of',
669 'is_uploaded_file',
670 'is_writable',
671 'is_readable',
672 'is_dir',
673 'is_file',
674 'is_link',
675 'intval',
676 'strval',
677 'floatval',
678 'doubleval',
679 'boolval',
680 'realpath',
681
682 // File Operations (Read-Only)
683 'filesize',
684 'pathinfo',
685 'basename',
686 'dirname',
687 'file_exists',
688 'readfile',
689 ];
690 }
691
692 /**
693 * Returns a list of allowed WordPress functions.
694 */
695 function widgetopts_get_allowed_wp_functions()
696 {
697 return [
698 // Database Operations
699 'get_option',
700 'get_post',
701 'get_user_meta',
702 'get_bloginfo',
703 'get_theme_mod',
704
705 // Escaping and Sanitizing
706 'esc_url',
707 'esc_html',
708 'esc_attr',
709 'esc_js',
710 'sanitize_text_field',
711 'sanitize_email',
712 'sanitize_title',
713 'sanitize_key',
714
715 // URL and Links
716 'get_permalink',
717 'get_home_url',
718 'get_site_url',
719 'get_admin_url',
720 'wp_parse_url',
721 'urlencode',
722 'urldecode',
723 'rawurlencode',
724 'rawurldecode',
725 'parse_url',
726 'http_build_query',
727
728 // Security and Authentication
729 'wp_verify_nonce',
730 'check_admin_referer',
731 'current_user_can',
732 'is_user_logged_in',
733 'wp_nonce_field',
734 'wp_nonce_url',
735
736 // Formatting
737 'wp_kses',
738 'wp_kses_post',
739 'wp_trim_words',
740 'wpautop',
741
742 // Retrieval of Metadata and Post Details
743 'get_post_meta',
744 'get_page_template_slug',
745 'get_the_author_meta',
746 'get_the_terms',
747 'get_comments',
748 'get_category',
749 'get_terms',
750 'get_nav_menu_locations',
751 'get_posts',
752 'get_post_status',
753 'get_comment_meta',
754 'get_taxonomy',
755 'get_object_taxonomies',
756 'get_post_type',
757 'get_the_ID',
758 'get_the_title',
759 'get_edit_post_link',
760
761 // Plugins and Themes
762 'is_plugin_active',
763 'get_plugins',
764 'get_theme_support',
765
766 // Utilities
767 'wp_generate_uuid4',
768
769 // User and Authentication
770 'get_userdata',
771 'get_user_by',
772 'get_users',
773 'get_author_posts_url',
774 'get_user_roles',
775 'get_users_by_role',
776 'wp_get_current_user',
777
778 // Media and Files
779 'wp_get_attachment_image_src',
780 'wp_get_attachment_url',
781 'wp_get_attachment_metadata',
782 'wp_get_attachment_image',
783 'wp_get_attachment_thumb_url',
784
785 // File Handling
786 'wp_get_upload_dir',
787 'wp_handle_upload',
788 'wp_check_filetype_and_ext',
789 'wp_get_file_upload_url',
790
791 // Taxonomies
792 'get_term_meta',
793 'get_the_category_list',
794 'get_the_tag_list',
795 'get_the_term_list',
796
797 // Checking Functions
798 'is_admin',
799 'is_network_admin',
800 'is_main_query',
801 'is_single',
802 'is_singular',
803 'is_category',
804 'is_tag',
805 'is_page',
806 'is_front_page',
807 'is_home',
808 'is_attachment',
809 'is_archive',
810 'is_date',
811 'is_year',
812 'is_month',
813 'is_day',
814 'is_author',
815 'is_search',
816 'is_404',
817 'is_multisite',
818 'is_customize_preview',
819 ];
820 }
821
822 /**
823 * Validate PHP code against allowed functions and detect obfuscated calls.
824 *
825 * @param string $code The PHP code to validate.
826 * @param array $allowed_functions List of explicitly allowed functions.
827 * @return bool Returns true if the code is considered safe, false otherwise.
828 */
829 function widgetopts_validate_code_with_tokens($code, $allowed_functions)
830 {
831 $code = stripslashes($code);
832 // Ensure correct PHP opening tag before tokenizing
833 if (strpos(trim($code), '<?php') !== 0) {
834 $code = "<?php " . $code;
835 }
836
837 $tokens = token_get_all($code);
838 $is_safe = true;
839 $last_token = null;
840
841 foreach ($tokens as $index => $token) {
842 if (is_array($token)) {
843 $token_type = $token[0];
844 $token_value = $token[1];
845
846 // **Fix: Properly detect function calls inside conditions**
847 if ($token_type === T_STRING) {
848 $function_name = strtolower($token_value);
849 $next_token = $tokens[$index + 1] ?? null;
850
851 if ($next_token === '(' || (is_array($next_token) && $next_token[0] === T_WHITESPACE && ($tokens[$index + 2] ?? null) === '(')) {
852 if (!in_array($function_name, array_map('strtolower', $allowed_functions))) {
853 $is_safe = false;
854 break;
855 }
856 }
857 }
858
859 // **Fix: Detect if T_ENCAPSED_AND_WHITESPACE is being treated as code**
860 if ($token_type === T_ENCAPSED_AND_WHITESPACE) {
861 $is_safe = false;
862 break;
863 }
864
865 // **Fix: Dynamic Function Execution (`$func()` or `['test']()`)**
866 if ($token_type === T_VARIABLE || $token_type === T_CONSTANT_ENCAPSED_STRING) {
867 $next_token = $tokens[$index + 1] ?? null;
868 if ($next_token === '(') {
869 $is_safe = false;
870 break;
871 }
872 }
873
874 $last_token = $token;
875 }
876 }
877
878 return $is_safe;
879 }
880
881 function widgetopts_is_widget_or_post_preview()
882 {
883 // Check if it's a block editor preview
884 if (defined('REST_REQUEST') && REST_REQUEST && isset($_GET['context']) && $_GET['context'] === 'edit') {
885 return true;
886 }
887
888 // Check if it's a post/page preview
889 if (is_preview() || (isset($_GET['preview']) && $_GET['preview'] == 'true')) {
890 return true;
891 }
892
893 // Check if it's a Customizer preview
894 if (is_customize_preview()) {
895 return true;
896 }
897
898 // Check if Beaver Builder is in edit mode
899 if (class_exists('FLBuilderModel') && FLBuilderModel::is_builder_active()) {
900 return true;
901 }
902
903 // Check if Elementor is in edit mode
904 if (class_exists('\Elementor\Plugin') && \Elementor\Plugin::$instance->editor->is_edit_mode()) {
905 return true;
906 }
907
908 return false;
909 }
910