PluginProbe ʕ •ᴥ•ʔ
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More / 3.4.0
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More v3.4.0
4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 trunk 1.0.0 2.0.0 2.0.1 2.0.2 2.0.3 3.0 3.0.1 3.0.2 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.2.0 3.2.1 3.2.2 3.2.4 3.2.5 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1
superb-blocks / src / data / controllers / class-css-controller.php
superb-blocks / src / data / controllers Last commit date
class-cache-controller.php 1 year ago class-css-controller.php 1 year ago class-domainshift-controller.php 1 year ago class-key-controller.php 1 year ago class-log-controller.php 1 year ago class-option-controller.php 1 year ago class-rest-controller.php 1 year ago
class-css-controller.php
429 lines
1 <?php
2
3 namespace SuperbAddons\Data\Controllers;
4
5 defined('ABSPATH') || exit();
6
7 use Exception;
8 use SuperbAddons\Config\Capabilities;
9 use WP_Error;
10 use stdClass;
11
12 class CSSController
13 {
14 const CSS_PATH = '/superb-addons/custom-css/';
15 const CSS_ROUTE = '/css-blocks';
16
17 const BLOCK_NAME_MAX_LENGTH = 145;
18
19 public function __construct()
20 {
21 $this->EnqueueCustomCSSAction();
22 RestController::AddRoute(self::CSS_ROUTE, array(
23 'methods' => 'POST',
24 'permission_callback' => array($this, 'CSSCallbackPermissionCheck'),
25 'callback' => array($this, 'CSSRouteCallback'),
26 ));
27 }
28
29 public function CSSCallbackPermissionCheck()
30 {
31 // Restrict endpoint to only users who have the proper capability.
32 if (!current_user_can(Capabilities::ADMIN)) {
33 return new WP_Error('rest_forbidden', esc_html__('Unauthorized. Please check user permissions.', "superb-blocks"), array('status' => 401));
34 }
35
36 return true;
37 }
38
39 public function CSSRouteCallback($request)
40 {
41 if (!isset($request['action'])) {
42 return new WP_Error('bad_request', 'Bad Request', array('status' => 400));
43 }
44 try {
45 switch ($request['action']) {
46 case 'save-block':
47 return $this->SaveBlock($request);
48 case 'delete-blocks':
49 return $this->DeleteBlocks($request);
50 case 'import-blocks':
51 return $this->ImportBlocks($request);
52 case 'activate-blocks':
53 return $this->ToggleBlocks($request, true);
54 case 'deactivate-blocks':
55 return $this->ToggleBlocks($request, false);
56 default:
57 return new WP_Error('bad_request_plugin', 'Bad Request', array('status' => 400));
58 }
59 } catch (Exception $ex) {
60 LogController::HandleException($ex);
61 return new WP_Error('internal_error_plugin', 'Internal Plugin Error', array('status' => 500));
62 }
63 }
64
65 private function SanitizeBlock($saved_block, $request_block)
66 {
67 $request_block = json_decode($request_block, true);
68 $is_active = isset($request_block['active']) ? !!$request_block['active'] : ($saved_block && isset($saved_block->active) ? !!$saved_block->active : true);
69 $block = new stdClass();
70 $block->name = isset($request_block['name']) && !empty($request_block['name']) ? sanitize_text_field(mb_substr($request_block['name'], 0, self::BLOCK_NAME_MAX_LENGTH)) : 'CSS Block ' . time();
71 $block->selectors = $this->SanitizeSelectors($request_block['selectors']);
72 $block->css = sanitize_textarea_field($request_block['css']);
73 $block->active = $is_active;
74 return $block;
75 }
76
77 private function SanitizeSelectors($selectors_request)
78 {
79 $selectors = [];
80 foreach ($selectors_request as $selector_request) {
81 $selector = new stdClass();
82 $selector->type = sanitize_text_field($selector_request['type']);
83 $selector->value = isset($selector_request['value']) && is_array($selector_request['value']) ? array_map(function ($str) {
84 return sanitize_text_field($str);
85 }, $selector_request['value']) : false;
86 $selectors[] = $selector;
87 }
88 return $selectors;
89 }
90
91 public static function GetBlocks()
92 {
93 return get_option('superb_addons_custom_css_blocks', []);
94 }
95
96 public static function UpdateBlocks($blocks)
97 {
98 $updated = update_option('superb_addons_custom_css_blocks', $blocks, false);
99 if ($updated) {
100 self::GenerateOptimizedCSS();
101 }
102 return $updated;
103 }
104
105 private function GetBlockIDArray($request)
106 {
107 if (!isset($request['block_ids']) || empty($request['block_ids'])) {
108 return false;
109 }
110
111 $block_ids = sanitize_text_field($request['block_ids']);
112 if (strpos($block_ids, ',') !== false) {
113 $block_ids = explode(",", $block_ids);
114 } else {
115 $block_ids = array($block_ids);
116 }
117
118 if (!$block_ids || empty($block_ids)) {
119 return false;
120 }
121
122 return $block_ids;
123 }
124
125 private function DeleteBlocks($request)
126 {
127 $block_ids = $this->GetBlockIDArray($request);
128 if (!$block_ids) {
129 return new WP_Error('bad_request_plugin', 'Bad Request', array('status' => 400));
130 }
131
132 $blocks = self::GetBlocks();
133 foreach ($block_ids as $block_id) {
134 if (!wp_is_uuid($block_id)) {
135 return new WP_Error('bad_request_plugin', 'Bad Request', array('status' => 400));
136 }
137 if (isset($blocks[$block_id])) {
138 unset($blocks[$block_id]);
139 }
140 }
141
142 self::UpdateBlocks($blocks);
143
144 return rest_ensure_response(['success' => true]);
145 }
146
147 private function ToggleBlocks($request, $active)
148 {
149 $block_ids = $this->GetBlockIDArray($request);
150 if (!$block_ids) {
151 return new WP_Error('bad_request_plugin', 'Bad Request', array('status' => 400));
152 }
153
154 $blocks = self::GetBlocks();
155 foreach ($block_ids as $block_id) {
156 if (!wp_is_uuid($block_id)) {
157 return new WP_Error('bad_request_plugin', 'Bad Request', array('status' => 400));
158 }
159 if (isset($blocks[$block_id])) {
160 $blocks[$block_id]->active = $active;
161 }
162 }
163
164 self::UpdateBlocks($blocks);
165
166 return rest_ensure_response(['success' => true]);
167 }
168
169 private function SaveBlock($request)
170 {
171 $blocks = self::GetBlocks();
172 $block_id = isset($request['id']) && wp_is_uuid($request['id']) ? sanitize_text_field($request['id']) : wp_generate_uuid4();
173 $saved_block = isset($blocks[$block_id]) ? $blocks[$block_id] : false;
174 $new_block = $this->SanitizeBlock($saved_block, $request['block']);
175 $blocks[$block_id] = $new_block;
176 self::UpdateBlocks($blocks);
177
178 return rest_ensure_response(['success' => true, 'id' => $block_id]);
179 }
180
181 private function ImportBlocks($request)
182 {
183 $files = $request->get_file_params();
184 if (empty($files) || empty($files['files'])) {
185 return new WP_Error('no_files', 'No files uploaded.', array('status' => 400));
186 }
187 $files = $files['files'];
188 if (!isset($files['tmp_name']) || empty($files['tmp_name'])) {
189 return new WP_Error('no_files', 'No files uploaded.', array('status' => 400));
190 }
191
192 global $wp_filesystem;
193 if (!is_object($wp_filesystem)) {
194 require_once(ABSPATH . 'wp-admin/includes/file.php');
195 WP_Filesystem();
196 }
197
198 $block_amount = 0;
199 $imported_blocks = array();
200 foreach ($files['tmp_name'] as $file) {
201 $file_content = $wp_filesystem->get_contents($file);
202 if (!$file_content) {
203 return rest_ensure_response(['success' => false, "text" => esc_html__("File(s) could not be accessed.", "superb-blocks")]);
204 }
205 $file_blocks = json_decode(base64_decode($file_content));
206 if (empty($file_blocks)) {
207 continue;
208 }
209
210 foreach ($file_blocks as $file_block) {
211 $block = $this->SanitizeBlock(false, json_encode($file_block));
212 $block_id = wp_generate_uuid4();
213 $imported_blocks[$block_id] = $block;
214 $block_amount++;
215 }
216 }
217
218 if (empty($imported_blocks)) {
219 return rest_ensure_response(['success' => false, "text" => esc_html__("Blocks could not be imported. Please verify that the selected files are valid and try again.", "superb-blocks")]);
220 }
221
222 $blocks = self::GetBlocks();
223 $blocks = array_merge($blocks, $imported_blocks);
224 self::UpdateBlocks($blocks);
225
226 return rest_ensure_response(['success' => true, "text" => esc_html(sprintf(__("%d CSS Block(s) imported successfully", "superb-blocks"), $block_amount))]);
227 }
228
229 private static function GenerateOptimizedCSS()
230 {
231 // Delete Previous Files
232 self::RemovePreviousCSSFiles();
233
234 $blocks = self::GetBlocks();
235 if (empty($blocks)) {
236 // No blocks to generate CSS from, remove the optimized CSS option
237 delete_option('superb_addons_optimized_css');
238 return;
239 }
240
241 $valid_types = apply_filters('superb_addons_custom_css_valid_types', array('full', 'front'));
242
243 $css_generations = [];
244 foreach ($blocks as $block) {
245 if (!isset($block->selectors) || !isset($block->css) || !isset($block->active) || !$block->active) {
246 continue;
247 }
248 foreach ($block->selectors as $selector) {
249 if (!isset($selector->type) || !in_array($selector->type, $valid_types)) {
250 continue;
251 }
252 if (!isset($selector->value) || empty($selector->value) || !is_array($selector->value)) {
253 $css_generations[$selector->type]["css"][] = $block->css;
254 if ($selector->type === 'full') {
255 // Full CSS - No need to continue with the rest of the selectors in this block
256 // Continue 2 is used to continue to the next outer loop block
257 continue 2;
258 }
259 continue;
260 }
261 foreach ($selector->value as $selector_value) {
262 $css_generations[$selector->type][$selector_value]["css"][] = $block->css;
263 }
264 }
265 }
266
267 if (version_compare(PHP_VERSION, '7.1', '>=')) {
268 // PHP 7.1 mininumum required for CSSTidy
269 // Include the CSSTidy class if PHP version is 7.1 or greater
270 include(SUPERBADDONS_PLUGIN_DIR . 'src/data/csstidy/class.csstidy.php');
271 $csstidy = new \SuperbAddons\CSSTidy\csstidy();
272
273 // Set some options :
274 $csstidy->set_cfg('optimise_shorthands', 2);
275 $csstidy->set_cfg('template', 'high');
276 } else {
277 $csstidy = false;
278 }
279
280 // Generate CSS Files
281 $optimized = array();
282 foreach ($css_generations as $key => $css_generation) {
283 if (isset($css_generation['css'])) {
284 // No inner key selections
285 $stylesheet = self::GenerateCSSFile($csstidy, $key, join("", $css_generation['css']));
286 if (!$stylesheet) {
287 continue;
288 }
289 $optimized[] = self::GetOptimizedArrayItem($stylesheet, $key);
290 unset($css_generation['css']);
291 }
292
293 if (empty($css_generation)) continue;
294 // Inner key selections
295 foreach ($css_generation as $inner_key => $inner_css_generation) {
296 $stylesheet = self::GenerateCSSFile($csstidy, $key . "-" . $inner_key, join("", $inner_css_generation['css']));
297 if (!$stylesheet) {
298 continue;
299 }
300 $optimized[] = self::GetOptimizedArrayItem($stylesheet, $key, $inner_key);
301 }
302 }
303
304 // Save the optimized CSS to the database
305 update_option('superb_addons_optimized_css', $optimized, true);
306 }
307
308 private static function GetOptimizedArrayItem($stylesheet, $type, $value = false)
309 {
310 return array(
311 'stylesheet' => sanitize_file_name($stylesheet),
312 'type' => sanitize_text_field($type),
313 'value' => sanitize_text_field($value),
314 'created' => time(),
315 );
316 }
317
318 private static function GetCSSDirectory()
319 {
320 $upload_dir = wp_upload_dir();
321 $upload_dir = $upload_dir['basedir'];
322 return $upload_dir . self::CSS_PATH;
323 }
324
325 private static function GetCSSDirectoryURL()
326 {
327 $upload_dir = wp_upload_dir();
328 $upload_dir = $upload_dir['baseurl'];
329 return set_url_scheme($upload_dir . self::CSS_PATH);
330 }
331
332 private static function RemovePreviousCSSFiles()
333 {
334 $upload_dir = self::GetCSSDirectory();
335 if (!is_dir($upload_dir) || strpos($upload_dir, self::CSS_PATH) === false) {
336 return;
337 }
338
339 $files = glob($upload_dir . '*');
340 foreach ($files as $file) {
341 if (file_exists($file)) {
342 wp_delete_file($file);
343 }
344 }
345 }
346
347 private static function GenerateCSSFile($csstidy, $filename, $content)
348 {
349 $filename = sanitize_file_name(sanitize_title($filename) . '.css');
350 // Parse the CSS
351 if ($csstidy) {
352 $csstidy->parse($content);
353 // Get back the optimized CSS Code
354 $css_optimized = $csstidy->print->plain();
355 } else {
356 // Simple fallback with preg_replace
357 $css_optimized = sanitize_text_field($content);
358 }
359
360 $css_optimized = wp_strip_all_tags($css_optimized);
361
362 $upload_dir = self::GetCSSDirectory();
363 if (!is_dir($upload_dir)) {
364 wp_mkdir_p($upload_dir);
365 }
366 $created_bytes = file_put_contents($upload_dir . $filename, $css_optimized);
367 if (!$created_bytes) {
368 return false;
369 }
370
371 return $filename;
372 }
373
374 private function EnqueueCustomCSSAction()
375 {
376 add_action('wp_enqueue_scripts', array($this, 'EnqueueCustomCSS'));
377 }
378
379 public function EnqueueCustomCSS()
380 {
381 try {
382 $optimized_css = get_option('superb_addons_optimized_css', false);
383 if (!$optimized_css || empty($optimized_css)) return;
384
385 $baseurl = self::GetCSSDirectoryURL();
386 $enqueueChecks = apply_filters(
387 'superb_addons_custom_css_enqueue_checks',
388 array(
389 'full' => array("function" => false),
390 'front' => array("function" => 'is_front_page'),
391 )
392 );
393
394 foreach ($optimized_css as $stylesheet) {
395
396 if (!isset($stylesheet['type']) || !isset($enqueueChecks[$stylesheet['type']])) {
397 continue;
398 }
399
400 if (!isset($enqueueChecks[$stylesheet['type']]['function'])) {
401 return;
402 }
403
404 $should_enqueue =
405 (!$enqueueChecks[$stylesheet['type']]['function']
406 || isset($enqueueChecks[$stylesheet['type']]['params']) && call_user_func($enqueueChecks[$stylesheet['type']]['function'], $stylesheet['value']))
407 || (!isset($enqueueChecks[$stylesheet['type']]['params']) && call_user_func($enqueueChecks[$stylesheet['type']]['function']));
408
409 if (!$should_enqueue) continue;
410
411
412
413
414 $this->EnqueueStyle($baseurl, $stylesheet['stylesheet'], $stylesheet['created']);
415 }
416 } catch (Exception $ex) {
417 LogController::HandleException($ex);
418 }
419 }
420
421 private function EnqueueStyle($baseurl, $path, $created)
422 {
423 $path = sanitize_file_name($path);
424 $title = sanitize_title('superb-addons-custom-' . $path);
425 wp_register_style($title, $baseurl . $path, array(), $created);
426 wp_enqueue_style($title);
427 }
428 }
429