PluginProbe
Boxzilla – WordPress Popup Builder / 3.4.8
Boxzilla – WordPress Popup Builder v3.4.8
3.4.11 3.4.10 3.4.9 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 trunk 3.0 3.0.1 3.0.2 3.0.3 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 All 72 releases
boxzilla / src / class-loader.php

class-loader.php in Boxzilla – WordPress Popup Builder 3.4.8, at src/class-loader.php

351 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Boxzilla;
4
5 class BoxLoader
6 {
7 /**
8 * @var Plugin
9 */
10 private $plugin;
11
12 /**
13 * @var array
14 */
15 private $box_ids_to_load = [];
16
17 /**
18 * @var array
19 */
20 protected $options;
21
22 /**
23 * Constructor
24 *
25 * @param Plugin $plugin
26 * @param array $options
27 */
28 public function __construct(Plugin $plugin, array $options)
29 {
30 $this->plugin = $plugin;
31 $this->options = $options;
32 }
33
34 /**
35 * Initializes the plugin, runs on `wp` hook.
36 */
37 public function init()
38 {
39 $this->box_ids_to_load = $this->filter_boxes();
40
41 // Only add other hooks if necessary
42 add_action('wp_head', [$this, 'print_preload_js'], 10, 0);
43 if (count($this->box_ids_to_load) > 0) {
44 add_action('wp_footer', [ $this, 'print_boxes_content' ], 1);
45 add_action('wp_enqueue_scripts', [ $this, 'load_assets' ], 90);
46 }
47 }
48
49 /**
50 * Prints the preload API so that the Boxzilla JS API can be used before Boxzilla itself is loaded
51 * This allows us to defer the Boxzilla script itself.
52 */
53 public function print_preload_js(): void
54 {
55 echo '<script>';
56 include BOXZILLA_DIR . '/assets/js/preload.js';
57 echo '</script>';
58 }
59
60 /**
61 * Get global rules for all boxes
62 *
63 * @return array
64 */
65 protected function get_filter_rules(): array
66 {
67 $rules = get_option('boxzilla_rules', []);
68 return is_array($rules) ? $rules : [];
69 }
70
71
72 /**
73 * Match a string against an array of patterns, glob-style.
74 *
75 * @param string $string
76 * @param array $patterns
77 * @param bool $contains
78 * @return bool
79 */
80 protected function match_patterns($string, array $patterns, $contains = false)
81 {
82 $string = strtolower($string);
83
84 foreach ($patterns as $pattern) {
85 $pattern = rtrim($pattern, '/');
86 $pattern = strtolower($pattern);
87
88 if ($contains) {
89 // contains means we should do a simple occurrence check
90 // does not support wildcards
91 $match = strpos($string, $pattern) !== false;
92 } elseif (function_exists('fnmatch')) {
93 $match = fnmatch($pattern, $string);
94 } else {
95 $match = ( $pattern === $string );
96 }
97
98 if ($match) {
99 return true;
100 }
101 }
102
103 return false;
104 }
105
106 /**
107 * @return string
108 */
109 protected function get_request_url()
110 {
111 return \boxzilla_normalize_relative_url(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/');
112 }
113
114 /**
115 * Check if this rule passes (conditional matches expected value)
116 *
117 * @param string $condition
118 * @param string $value
119 * @param boolean $qualifier
120 *
121 * @return bool
122 */
123 protected function match_rule($condition, $value, $qualifier = true)
124 {
125 $matched = false;
126
127 // cast value to array & trim whitespace or excess comma's
128 $values = array_map('trim', explode(',', rtrim(trim($value), ',')));
129
130 switch ($condition) {
131 case 'everywhere':
132 $matched = true;
133 break;
134
135 case 'is_url':
136 $url = $this->get_request_url();
137 $matched = $this->match_patterns($url, $values, $qualifier === 'contains' || $qualifier === 'not_contains');
138 break;
139
140 case 'is_referer':
141 if (! empty($_SERVER['HTTP_REFERER'])) {
142 $referer = $_SERVER['HTTP_REFERER'];
143 $matched = $this->match_patterns($referer, $values, $qualifier === 'contains' || $qualifier === 'not_contains');
144 }
145 break;
146
147 case 'is_post_type':
148 $post_type = (string) get_post_type();
149 $matched = in_array($post_type, $values, true);
150 break;
151
152 case 'is_single':
153 case 'is_post':
154 // convert to empty string if array with just empty string in it
155 $value = ( $values === [ '' ] ) ? '' : $values;
156 $matched = is_single($value);
157 break;
158
159 case 'is_post_in_category':
160 $matched = is_singular() && has_category($values);
161 break;
162
163 case 'is_page':
164 $matched = is_page($values);
165 break;
166
167 case 'is_post_with_tag':
168 $matched = is_singular() && has_tag($values);
169 break;
170
171 case 'is_user_logged_in':
172 $matched = is_user_logged_in();
173 break;
174 }
175
176 /**
177 * Filters whether a given box rule matches the condition and expected value.
178 *
179 * The dynamic portion of the hook, `$condition`, refers to the condition being matched.
180 *
181 * @param boolean $matched
182 * @param array $values
183 */
184 $matched = apply_filters('boxzilla_box_rule_matches_' . $condition, $matched, $values);
185
186 // if qualifier is set to false, we need to reverse this value here.
187 if (! $qualifier || $qualifier === 'not_contains') {
188 $matched = ! $matched;
189 }
190
191 return $matched;
192 }
193
194 /**
195 * Checks which boxes should be loaded for this request.
196 *
197 * @return array
198 */
199 private function filter_boxes()
200 {
201 $box_ids_to_load = [];
202 $rules = $this->get_filter_rules();
203
204 foreach ($rules as $box_id => $box_rules) {
205 $matched = false;
206 $comparision = isset($box_rules['comparision']) ? $box_rules['comparision'] : 'any';
207
208 // loop through all rules for all boxes
209 foreach ($box_rules as $rule) {
210 // skip faulty values (and comparision rule)
211 if (! is_array($rule) || empty($rule['condition'])) {
212 continue;
213 }
214
215 $qualifier = isset($rule['qualifier']) ? $rule['qualifier'] : true;
216 $matched = $this->match_rule($rule['condition'], $rule['value'], $qualifier);
217
218 // break out of loop if we've already matched
219 if ($comparision === 'any' && $matched) {
220 break;
221 }
222
223 // no need to continue if this rule didn't match
224 if ($comparision === 'all' && ! $matched) {
225 break;
226 }
227 }
228
229 // value of $matched at this point determines whether box should be loaded
230 $load_box = $matched;
231
232 /**
233 * Filters whether a box should be loaded into the page HTML.
234 *
235 * The dynamic portion of the hook, `$box_id`, refers to the ID of the box. Return true if you want to output the box.
236 *
237 * @param boolean $load_box
238 */
239 $load_box = apply_filters('boxzilla_load_box_' . $box_id, $load_box);
240
241 /**
242 * Filters whether a box should be loaded into the page HTML.
243 *
244 * @param boolean $load_box
245 * @param int $box_id
246 */
247 $load_box = apply_filters('boxzilla_load_box', $load_box, $box_id);
248
249 // if matched, box should be loaded on this page
250 if ($load_box) {
251 $box_ids_to_load[] = $box_id;
252 }
253 }
254
255 /**
256 * Filters which boxes should be loaded on this page, expects an array of post ID's.
257 *
258 * @param array $box_ids_to_load
259 */
260 return apply_filters('boxzilla_load_boxes', $box_ids_to_load);
261 }
262
263 /**
264 * Load plugin styles
265 */
266 public function load_assets()
267 {
268 wp_enqueue_style('boxzilla', $this->plugin->url('/assets/css/styles.css'), [], $this->plugin->version());
269 wp_enqueue_script('boxzilla', $this->plugin->url('/assets/js/script.js'), [], $this->plugin->version(), [
270 'strategy' => 'defer',
271 'in_footer' => true,
272 ]);
273
274 // create boxzilla_Global_Options object
275 $plugin_options = $this->options;
276 $boxes = $this->get_matched_boxes();
277
278 $data = [
279 'testMode' => (bool) $plugin_options['test_mode'],
280 'boxes' => (array) array_map(
281 function (Box $box) {
282 return $box->get_client_options();
283 },
284 $boxes
285 ),
286 ];
287
288 wp_localize_script('boxzilla', 'boxzilla_options', $data);
289
290 do_action('boxzilla_load_assets', $this);
291 }
292
293 public function print_boxes_content()
294 {
295 $boxes = $this->get_matched_boxes();
296 if (empty($boxes)) {
297 return;
298 }
299
300 echo '<div style="display: none;">';
301 foreach ($boxes as $box) {
302 echo "<div id=\"boxzilla-box-{$box->ID}-content\">", $box->get_content(), "</div>";
303 }
304 echo '</div>';
305 }
306
307 /**
308 * Get an array of Box objects. These are the boxes that will be loaded for the current request.
309 *
310 * @return Box[]
311 */
312 public function get_matched_boxes()
313 {
314 static $boxes;
315
316 if (is_null($boxes)) {
317 if (count($this->box_ids_to_load) === 0) {
318 return [];
319 }
320
321 // query Box posts
322 $q = new \WP_Query();
323 $posts = $q->query(
324 [
325 'post_type' => 'boxzilla-box',
326 'post_status' => 'publish',
327 'post__in' => $this->box_ids_to_load,
328 'posts_per_page' => count($this->box_ids_to_load),
329 'ignore_sticky_posts' => true,
330 'no_found_rows' => true,
331 ]
332 );
333
334 // create `Box` instances out of \WP_Post instances
335 $boxes = [];
336 foreach ($posts as $key => $post) {
337 $box = new Box($post);
338
339 // skip boxes without any content
340 if ($box->get_content() === '') {
341 continue;
342 }
343
344 $boxes[ $key ] = $box;
345 }
346 }
347
348 return $boxes;
349 }
350 }
351