PluginProbe ʕ •ᴥ•ʔ
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets / 4.2.5
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets v4.2.5
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 1 month ago pagebuilders 1 month ago snippets 1 month ago widgets 1 month ago ajax-functions.php 1 month ago extras.php 1 month ago install.php 1 month ago scripts.php 1 month ago transient.php 1 month ago
extras.php
1189 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 // Trust-flag for widgetopts_safe_eval(): wrap callsites whose expression
496 // provably comes from a DB-backed, admin-controlled source so non-admin
497 // viewers can still execute it. Missing wrapper → eval short-circuits to true.
498 function widgetopts_eval_trust_begin()
499 {
500 if (!isset($GLOBALS['_widgetopts_trust_depth'])) {
501 $GLOBALS['_widgetopts_trust_depth'] = 0;
502 }
503 $GLOBALS['_widgetopts_trust_depth']++;
504 }
505
506 function widgetopts_eval_trust_end()
507 {
508 if (!empty($GLOBALS['_widgetopts_trust_depth'])) {
509 $GLOBALS['_widgetopts_trust_depth']--;
510 }
511 }
512
513 function widgetopts_eval_is_trusted()
514 {
515 return !empty($GLOBALS['_widgetopts_trust_depth']);
516 }
517
518 function widgetopts_safe_eval_trusted($expression)
519 {
520 widgetopts_eval_trust_begin();
521 try {
522 return widgetopts_safe_eval($expression);
523 } catch (\Throwable $e) {
524 return false;
525 } finally {
526 widgetopts_eval_trust_end();
527 }
528 }
529
530 function widgetopts_blocks_collect_logic_hashes(array $blocks, array &$out)
531 {
532 foreach ($blocks as $block) {
533 if (isset($block['attrs']['extended_widget_opts']['class']['logic'])
534 && is_string($block['attrs']['extended_widget_opts']['class']['logic'])
535 && $block['attrs']['extended_widget_opts']['class']['logic'] !== '') {
536 $out[hash('sha256', $block['attrs']['extended_widget_opts']['class']['logic'])] = true;
537 }
538 if (isset($block['attrs']['extended_widget_opts_block']['class']['logic'])
539 && is_string($block['attrs']['extended_widget_opts_block']['class']['logic'])
540 && $block['attrs']['extended_widget_opts_block']['class']['logic'] !== '') {
541 $out[hash('sha256', $block['attrs']['extended_widget_opts_block']['class']['logic'])] = true;
542 }
543 if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
544 widgetopts_blocks_collect_logic_hashes($block['innerBlocks'], $out);
545 }
546 }
547 }
548
549 // Per-request, per-post sha256 allowlist of class.logic values actually
550 // stored in this post's post_content. Cached because render_block_data fires
551 // per block, and parse_blocks() is the expensive part.
552 function widgetopts_get_post_logic_allowlist($post_id)
553 {
554 static $cache = array();
555
556 $post_id = (int) $post_id;
557 if ($post_id <= 0) {
558 return array();
559 }
560 if (isset($cache[$post_id])) {
561 return $cache[$post_id];
562 }
563
564 $content = get_post_field('post_content', $post_id);
565 if (!is_string($content) || $content === ''
566 || strpos($content, 'extended_widget_opts') === false) {
567 return $cache[$post_id] = array();
568 }
569
570 $blocks = parse_blocks($content);
571 $hashes = array();
572 if (is_array($blocks)) {
573 widgetopts_blocks_collect_logic_hashes($blocks, $hashes);
574 }
575
576 return $cache[$post_id] = $hashes;
577 }
578
579 // Recursively neutralise legacy display-logic shapes inside a REST payload.
580 // Recognises: extended_widget_opts[_block].class.logic, widgetopts_logic /
581 // widgetopts_settings_logic keys, block/freeform strings.
582 function widgetopts_rest_scrub_legacy_logic(&$value, &$modified)
583 {
584 if (is_string($value)) {
585 // Pre-screen by substring to avoid parse_blocks() on unrelated content.
586 if (strpos($value, 'extended_widget_opts') !== false
587 || strpos($value, 'start_widgetopts') !== false) {
588 $blocks = parse_blocks($value);
589 if (is_array($blocks) && !empty($blocks)) {
590 $b_changed = false;
591 widgetopts_strip_logic_from_blocks($blocks, $b_changed);
592 if ($b_changed) {
593 $value = serialize_blocks($blocks);
594 $modified = true;
595 }
596 }
597 }
598 return;
599 }
600
601 if (!is_array($value) && !is_object($value)) {
602 return;
603 }
604
605 $is_object = is_object($value);
606 $items = $is_object ? get_object_vars($value) : $value;
607
608 foreach ($items as $k => $v) {
609 $key = (string) $k;
610
611 // Page-builder-specific inline keys. Both names are owned by this
612 // plugin's own UI — no third-party REST consumer would legitimately
613 // ship a setting under these exact identifiers.
614 if (is_string($v) && $v !== ''
615 && ($key === 'widgetopts_settings_logic' || $key === 'widgetopts_logic')) {
616 $v = '';
617 $modified = true;
618 }
619
620 // Scoped to plugin-owned containers — never touch class.logic under
621 // unrelated parents (third-party REST shapes commonly use them).
622 if (($key === 'extended_widget_opts' || $key === 'extended_widget_opts_block')
623 && (is_array($v) || is_object($v))) {
624 widgetopts_rest_scrub_extended_widget_opts($v, $modified);
625 }
626
627 if (is_array($v) || is_object($v) || is_string($v)) {
628 widgetopts_rest_scrub_legacy_logic($v, $modified);
629 }
630
631 if ($is_object) {
632 $value->$k = $v;
633 } else {
634 $value[$k] = $v;
635 }
636 }
637 }
638
639 // Inside a plugin-owned container, find and clear class.logic at any depth.
640 function widgetopts_rest_scrub_extended_widget_opts(&$container, &$modified)
641 {
642 if (!is_array($container) && !is_object($container)) {
643 return;
644 }
645
646 $is_object = is_object($container);
647 $items = $is_object ? get_object_vars($container) : $container;
648
649 foreach ($items as $k => $v) {
650 if ((string) $k === 'class' && (is_array($v) || is_object($v))) {
651 $cls_is_obj = is_object($v);
652 $cls_items = $cls_is_obj ? get_object_vars($v) : $v;
653 if (isset($cls_items['logic'])
654 && is_string($cls_items['logic'])
655 && $cls_items['logic'] !== '') {
656 if ($cls_is_obj) {
657 $v->logic = '';
658 } else {
659 $v['logic'] = '';
660 }
661 $modified = true;
662 }
663 }
664
665 if (is_array($v) || is_object($v)) {
666 widgetopts_rest_scrub_extended_widget_opts($v, $modified);
667 }
668
669 if ($is_object) {
670 $container->$k = $v;
671 } else {
672 $container[$k] = $v;
673 }
674 }
675 }
676
677 // Belt-and-braces net for REST routes without their own save-time gate.
678 // Non-admin write requests only; GET/HEAD/OPTIONS untouched.
679 add_filter('rest_request_before_callbacks', function ($response, $handler, $request) {
680 if (!($request instanceof WP_REST_Request)) {
681 return $response;
682 }
683 if (current_user_can('manage_options')) {
684 return $response;
685 }
686 $method = strtoupper((string) $request->get_method());
687 if ($method === 'GET' || $method === 'HEAD' || $method === 'OPTIONS') {
688 return $response;
689 }
690
691 $params = $request->get_params();
692 if (!is_array($params) || empty($params)) {
693 return $response;
694 }
695
696 foreach ($params as $key => $value) {
697 $changed = false;
698 widgetopts_rest_scrub_legacy_logic($value, $changed);
699 if ($changed) {
700 $request->set_param($key, $value);
701 }
702 }
703
704 return $response;
705 }, 10, 3);
706
707 function widgetopts_safe_eval($expression)
708 {
709 // Closed default: non-admin without trust flag → "show" without eval.
710 if (!current_user_can('manage_options') && !widgetopts_eval_is_trusted()) {
711 return true;
712 }
713
714 if (widgetopts_is_widget_or_post_preview()) {
715 if (!current_user_can('manage_options')) {
716 return true;
717 }
718 }
719
720 $validation_result = widgetopts_validate_expression($expression);
721
722 if ($validation_result['valid'] === false) {
723 return false;
724 }
725
726 if (stristr($expression, "return") === false) {
727 $expression = "return (" . $expression . ");";
728 }
729
730 ob_start();
731 try {
732 $result = (bool) (@eval($expression));
733 } catch (\Exception $e) {
734 $result = false;
735 } catch (\Error $e) {
736 $result = false;
737 } catch (\ParseError $e) {
738 $result = false;
739 } catch (\Throwable $e) {
740 $result = false;
741 }
742 ob_end_clean();
743
744 return $result;
745 }
746
747 /**
748 * Validate the expression if it contains dangerous and unallowed patterns.
749 */
750 function widgetopts_validate_expression($expression)
751 {
752 // Ensure the expression has no backtick execution
753 if (stripos($expression, '`') !== false) {
754 return ['valid' => false, 'message' => 'Backticks execution is not allowed.'];
755 }
756
757 // List of potentially harmful patterns and their messages
758 $dangerous_patterns = [
759 '/\b(insert|update|delete|replace|select|drop|alter|truncate|grant|revoke)\b/i' => 'Potential SQL injection detected.',
760 '/\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.',
761 '/\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.',
762 '/\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.',
763 '/\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.',
764 '/\b(base64_decode|hex2bin|mb_decode_mimeheader|str_rot13)\b/i' => 'Encoding/decoding functions that may be used for obfuscation are not allowed.',
765 '/\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.',
766 '/\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.',
767 '/`[^`]*`/' => 'Backticks execution is not allowed.',
768 '/\[\s*[\'"]?[a-zA-Z0-9_]+\s*\.\s*[\'"]?[a-zA-Z0-9_]+\s*\]/i' => 'Concatenated function execution is not allowed.',
769 '/\b(str_replace|preg_replace|preg_replace_callback|preg_replace_callback_array)\s*\(\s*[\'"]\s*\.\s*[\'"]/' => 'Potential function name obfuscation detected.',
770 '/\$\w+\s*\[\s*[\'"]?\d+[\'"]?\s*\]\s*\(/' => 'Dynamic function execution using arrays is not allowed.',
771 ];
772
773 $is_valid = true;
774 // Check for dangerous patterns
775 foreach ($dangerous_patterns as $pattern => $message) {
776 if (preg_match($pattern, $expression)) {
777 $is_valid = false;
778 break;
779 }
780 }
781
782 if ($is_valid === false) {
783 return ['valid' => false, 'message' => $message];
784 }
785
786 // Retrieve allowed functions
787 $allowed_functions = array_merge(widgetopts_get_allowed_php_functions(), widgetopts_get_allowed_wp_functions());
788 $allowed_functions = apply_filters('widgetopts_allowed_php_functions', $allowed_functions);
789
790 // Validate using token-based function checking
791 $is_valid = widgetopts_validate_code_with_tokens($expression, $allowed_functions);
792 $is_valid = apply_filters('widgetopts_validate_allowed_commands', $is_valid);
793
794 // Return validation result
795 return $is_valid ? ['valid' => true, 'message' => 'Expression is safe.'] : ['valid' => false, 'message' => 'Expression contains disallowed function calls.'];
796 }
797
798 /**
799 * Returns a list of allowed PHP functions.
800 */
801 function widgetopts_get_allowed_php_functions()
802 {
803 return [
804 // String Manipulation
805 'strlen',
806 'substr',
807 'strtolower',
808 'strtoupper',
809 'trim',
810 'ltrim',
811 'rtrim',
812 'preg_match',
813 'explode',
814 'implode',
815 'htmlspecialchars',
816 'htmlentities',
817 'strip_tags',
818 'str_repeat',
819 'str_split',
820 'strpos',
821 'strrpos',
822 'stripos',
823 'strripos',
824 'substr_replace',
825 'wordwrap',
826
827 // Array Manipulation
828 'array_merge',
829 'array_diff',
830 'array_keys',
831 'array_values',
832 'in_array',
833 'count',
834 'sizeof',
835 'array_slice',
836 'array_push',
837 'array_pop',
838 'array_intersect',
839 'array_unique',
840 'array_column',
841 'array_reverse',
842
843 // Math Functions
844 'abs',
845 'round',
846 'floor',
847 'ceil',
848 'min',
849 'max',
850 'pow',
851 'sqrt',
852 'rand',
853 'mt_rand',
854 'number_format',
855 'log',
856 'exp',
857 'bcadd',
858 'bcsub',
859 'bcmul',
860 'bcdiv',
861
862 // Date/Time
863 'time',
864 'date',
865 'strtotime',
866 'mktime',
867 'checkdate',
868 'date_default_timezone_get',
869 'gmdate',
870
871 // JSON Functions
872 'json_decode',
873 'json_encode',
874
875 // Variable Handling
876 'isset',
877 'empty',
878 'unset',
879 'is_array',
880 'is_bool',
881 'is_callable',
882 'is_countable',
883 'is_double',
884 'is_float',
885 'is_int',
886 'is_integer',
887 'is_iterable',
888 'is_long',
889 'is_null',
890 'is_numeric',
891 'is_object',
892 'is_real',
893 'is_scalar',
894 'is_string',
895 'is_subclass_of',
896 'is_uploaded_file',
897 'is_writable',
898 'is_readable',
899 'is_dir',
900 'is_file',
901 'is_link',
902 'intval',
903 'strval',
904 'floatval',
905 'doubleval',
906 'boolval',
907 'realpath',
908
909 // File Operations (Read-Only)
910 'filesize',
911 'pathinfo',
912 'basename',
913 'dirname',
914 'file_exists'
915 ];
916 }
917
918 /**
919 * Returns a list of allowed WordPress functions.
920 */
921 function widgetopts_get_allowed_wp_functions()
922 {
923 return [
924 // Database Operations
925 'get_option',
926 'get_post',
927 'get_user_meta',
928 'get_bloginfo',
929 'get_theme_mod',
930
931 // Escaping and Sanitizing
932 'esc_url',
933 'esc_html',
934 'esc_attr',
935 'esc_js',
936 'sanitize_text_field',
937 'sanitize_email',
938 'sanitize_title',
939 'sanitize_key',
940
941 // URL and Links
942 'get_permalink',
943 'get_home_url',
944 'get_site_url',
945 'get_admin_url',
946 'wp_parse_url',
947 'urlencode',
948 'urldecode',
949 'rawurlencode',
950 'rawurldecode',
951 'parse_url',
952 'http_build_query',
953
954 // Security and Authentication
955 'wp_verify_nonce',
956 'check_admin_referer',
957 'current_user_can',
958 'is_user_logged_in',
959 'wp_nonce_field',
960 'wp_nonce_url',
961
962 // Formatting
963 'wp_kses',
964 'wp_kses_post',
965 'wp_trim_words',
966 'wpautop',
967
968 // Retrieval of Metadata and Post Details
969 'get_post_meta',
970 'get_page_template_slug',
971 'get_the_author_meta',
972 'get_the_terms',
973 'get_comments',
974 'get_category',
975 'get_terms',
976 'get_nav_menu_locations',
977 'get_posts',
978 'get_post_status',
979 'get_comment_meta',
980 'get_taxonomy',
981 'get_object_taxonomies',
982 'get_post_type',
983 'get_the_ID',
984 'get_the_title',
985 'get_edit_post_link',
986
987 // Plugins and Themes
988 'is_plugin_active',
989 'get_plugins',
990 'get_theme_support',
991
992 // Utilities
993 'wp_generate_uuid4',
994
995 // User and Authentication
996 'get_userdata',
997 'get_user_by',
998 'get_users',
999 'get_author_posts_url',
1000 'get_user_roles',
1001 'get_users_by_role',
1002 'wp_get_current_user',
1003
1004 // Media and Files
1005 'wp_get_attachment_image_src',
1006 'wp_get_attachment_url',
1007 'wp_get_attachment_metadata',
1008 'wp_get_attachment_image',
1009 'wp_get_attachment_thumb_url',
1010
1011 // File Handling
1012 'wp_get_upload_dir',
1013 'wp_handle_upload',
1014 'wp_check_filetype_and_ext',
1015 'wp_get_file_upload_url',
1016
1017 // Taxonomies
1018 'get_term_meta',
1019 'get_the_category_list',
1020 'get_the_tag_list',
1021 'get_the_term_list',
1022
1023 // Checking Functions
1024 'is_admin',
1025 'is_network_admin',
1026 'is_main_query',
1027 'is_single',
1028 'is_singular',
1029 'is_category',
1030 'is_tag',
1031 'is_page',
1032 'is_front_page',
1033 'is_home',
1034 'is_attachment',
1035 'is_archive',
1036 'is_date',
1037 'is_year',
1038 'is_month',
1039 'is_day',
1040 'is_author',
1041 'is_search',
1042 'is_404',
1043 'is_multisite',
1044 'is_customize_preview',
1045 ];
1046 }
1047
1048 /**
1049 * Return the nearest significant token relative to $index, skipping whitespace and comments.
1050 *
1051 * @param array $tokens Token array from token_get_all().
1052 * @param int $index Starting position.
1053 * @param int $dir 1 = look forward (next), -1 = look backward (prev).
1054 * @return array|string|null The token, or null if none found.
1055 */
1056 function widgetopts_adjacent_significant_token(array $tokens, int $index, int $dir)
1057 {
1058 $skip = [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT];
1059 $count = count($tokens);
1060 for ($i = $index + $dir; $dir === 1 ? $i < $count : $i >= 0; $i += $dir) {
1061 $t = $tokens[$i];
1062 if (is_array($t) && in_array($t[0], $skip, true)) {
1063 continue;
1064 }
1065 return $t;
1066 }
1067 return null;
1068 }
1069
1070 /**
1071 * Validate PHP code against allowed functions and detect obfuscated calls.
1072 *
1073 * @param string $code The PHP code to validate.
1074 * @param array $allowed_functions List of explicitly allowed functions.
1075 * @return bool Returns true if the code is considered safe, false otherwise.
1076 */
1077 function widgetopts_validate_code_with_tokens($code, $allowed_functions)
1078 {
1079 $code = stripslashes($code);
1080 // Ensure correct PHP opening tag before tokenizing
1081 if (strpos(trim($code), '<?php') !== 0) {
1082 $code = "<?php " . $code;
1083 }
1084
1085 $tokens = token_get_all($code);
1086 $is_safe = true;
1087
1088 // Constructs that aren't T_STRING — allowlist loop would skip them.
1089 // T_FUNCTION / T_FN block closure-define + immediate invoke.
1090 $forbidden_constructs = [T_EVAL, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_EXIT, T_GOTO, T_FUNCTION];
1091 if (defined('T_FN')) {
1092 $forbidden_constructs[] = T_FN; // PHP 7.4+
1093 }
1094
1095 foreach ($tokens as $index => $token) {
1096 if (is_array($token)) {
1097 $token_type = $token[0];
1098 $token_value = $token[1];
1099
1100 // Block language constructs (eval, include, require, exit, goto).
1101 // These produce dedicated token types, not T_STRING, so the allowlist
1102 // check below would silently pass them.
1103 if (in_array($token_type, $forbidden_constructs, true)) {
1104 $is_safe = false;
1105 break;
1106 }
1107
1108 // Detect function calls — skip non-significant tokens before '('
1109 if ($token_type === T_STRING) {
1110 $next = widgetopts_adjacent_significant_token($tokens, $index, 1);
1111 if ($next === '(') {
1112 // Block method/static/nullsafe calls: $obj->func(), Class::func(), $obj?->func()
1113 // The identifier looks like an allowed function name but is actually a method —
1114 // the allowlist covers only direct (free) function calls.
1115 $prev = widgetopts_adjacent_significant_token($tokens, $index, -1);
1116 $method_ops = [T_OBJECT_OPERATOR, T_DOUBLE_COLON];
1117 if (defined('T_NULLSAFE_OBJECT_OPERATOR')) {
1118 $method_ops[] = T_NULLSAFE_OBJECT_OPERATOR; // PHP 8.0+ (?->)
1119 }
1120 if (is_array($prev) && in_array($prev[0], $method_ops, true)) {
1121 $is_safe = false;
1122 break;
1123 }
1124 $function_name = strtolower($token_value);
1125 if (!in_array($function_name, array_map('strtolower', $allowed_functions))) {
1126 $is_safe = false;
1127 break;
1128 }
1129 }
1130 }
1131
1132 // Detect if T_ENCAPSED_AND_WHITESPACE is being treated as code
1133 if ($token_type === T_ENCAPSED_AND_WHITESPACE) {
1134 $is_safe = false;
1135 break;
1136 }
1137
1138 // Dynamic call via variable or string literal: `$fn()` / `'func'()`
1139 // Skip non-significant tokens between the token and '('
1140 if ($token_type === T_VARIABLE || $token_type === T_CONSTANT_ENCAPSED_STRING) {
1141 $next = widgetopts_adjacent_significant_token($tokens, $index, 1);
1142 if ($next === '(') {
1143 $is_safe = false;
1144 break;
1145 }
1146 }
1147 } elseif ($token === ']' || $token === ')' || $token === '}') {
1148 // `$arr[k]()`, `(expr)()`, `${$fn}()` / `Foo::{'m'}()`.
1149 $next = widgetopts_adjacent_significant_token($tokens, $index, 1);
1150 if ($next === '(') {
1151 $is_safe = false;
1152 break;
1153 }
1154 }
1155 }
1156
1157 return $is_safe;
1158 }
1159
1160 function widgetopts_is_widget_or_post_preview()
1161 {
1162 // Check if it's a block editor preview
1163 if (defined('REST_REQUEST') && REST_REQUEST && isset($_GET['context']) && $_GET['context'] === 'edit') {
1164 return true;
1165 }
1166
1167 // Check if it's a post/page preview
1168 if (is_preview() || (isset($_GET['preview']) && $_GET['preview'] == 'true')) {
1169 return true;
1170 }
1171
1172 // Check if it's a Customizer preview
1173 if (is_customize_preview()) {
1174 return true;
1175 }
1176
1177 // Check if Beaver Builder is in edit mode
1178 if (class_exists('FLBuilderModel') && FLBuilderModel::is_builder_active()) {
1179 return true;
1180 }
1181
1182 // Check if Elementor is in edit mode
1183 if (class_exists('\Elementor\Plugin') && \Elementor\Plugin::$instance->editor->is_edit_mode()) {
1184 return true;
1185 }
1186
1187 return false;
1188 }
1189