PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.5.3
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.5.3
3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.10 All 111 releases
← All changes | includes/Utils/Helper.php +481 -86 3.1.03.5.3 View file →
@@ -1,11 +1,15 @@
1 1 <?php
2 +
2 3 namespace Templately\Utils;
3 4
5 +use Elementor\Plugin;
6 +use Templately\Core\Importer\Utils\Utils;
4 7 use WP_Error;
5 8 use WP_REST_Response;
6 9 use function get_plugins;
7 10 use function is_plugin_active;
11 +
8 12 /**
9 13 * Utility Helper for Templately
10 14 *
11 15 * This class contains some helper functions for easy access.
@@ -12,31 +16,46 @@
12 16 *
13 17 * @since 1.0.0
14 18 */
15 19 class Helper extends Base {
16 - /**
17 - * Get installed WordPress Plugin List
18 - * @return array
19 - */
20 - public static function get_plugins(){
21 - if( ! function_exists( 'get_plugins' ) ) {
22 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
23 - }
24 - return get_plugins();
25 - }
20 + /**
21 + * Check if development API should be used
22 + *
23 + * @return bool True if development API should be used
24 + */
25 + public static function is_dev_api(){
26 + // Only check TEMPLATELY_DEV_API constant - no fallback mechanisms
27 + return defined( 'TEMPLATELY_DEV_API' ) && constant( 'TEMPLATELY_DEV_API' );
28 + }
26 29
27 - /**
28 - * Get installed WordPress Plugin List
29 - * @return boolean
30 - */
31 - public static function is_plugin_active( $plugin ){
32 - if( ! function_exists( 'is_plugin_active' ) ) {
33 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
34 - }
35 - return is_plugin_active( $plugin );
36 - }
30 + /**
31 + * Get installed WordPress Plugin List
32 + * @return array
33 + */
34 + public static function get_plugins() {
35 + if (! function_exists('get_plugins')) {
36 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
37 + }
38 + return get_plugins();
39 + }
40 + public static function is_plugins_installed($plugin_file) {
41 + $_plugins = self::get_plugins();
42 + $is_installed = isset($_plugins[$plugin_file]);
43 + return $is_installed;
44 + }
37 45
38 - /**
46 + /**
47 + * Get installed WordPress Plugin List
48 + * @return boolean
49 + */
50 + public static function is_plugin_active($plugin) {
51 + if (! function_exists('is_plugin_active')) {
52 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
53 + }
54 + return is_plugin_active($plugin);
55 + }
56 +
57 + /**
39 58 * Collect IP from request.
40 59 *
41 60 * @return string
42 61 */
@@ -41,37 +60,149 @@
41 60 * @return string
42 61 */
43 62 public static function get_ip() {
44 63 $ip = '127.0.0.1'; // Local IP
45 - if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
64 + if (! empty($_SERVER['HTTP_CLIENT_IP'])) {
46 65 $ip = $_SERVER['HTTP_CLIENT_IP'];
47 - } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
66 + } elseif (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
48 67 $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
49 68 } else {
50 - $ip = ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : $ip;
69 + $ip = ! empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : $ip;
51 70 }
52 71
53 - return sanitize_text_field( $ip );
72 + return sanitize_text_field($ip);
54 73 }
55 74
56 - /**
57 - * Get views for front-end display
58 - *
59 - * @param string $name it will be file name only from the view's folder.
60 - * @param array $data
61 - * @return void
62 - */
63 - public static function views( $name, $data = [] ){
64 - extract( $data );
65 - $helper = self::class;
66 - $file = TEMPLATELY_PATH . 'views/' . $name . '.php';
75 + /**
76 + * Get views for front-end display
77 + *
78 + * @param string $name it will be file name only from the view's folder.
79 + * @param array $data
80 + * @return void
81 + */
82 + public static function views($name, $data = []) {
83 + extract($data);
84 + $helper = self::class;
85 + $file = TEMPLATELY_PATH . 'views/' . $name . '.php';
67 86
68 - if( is_readable( $file ) ) {
69 - include_once $file;
70 - }
71 - }
87 + if (is_readable($file)) {
88 + include_once $file;
89 + }
90 + }
72 91
73 92 /**
93 + * Get API URL for Templately endpoints
94 + *
95 + * @param string $endpoint API endpoint path (e.g., 'v2/import/pack/123')
96 + * @return string Complete API URL
97 + */
98 + public static function get_api_url($endpoint): string {
99 + $base_url = self::is_dev_api() ? 'https://app.templately.dev' : 'https://app.templately.com';
100 +
101 + /**
102 + * Filter the base URL for development API
103 + *
104 + * @since 3.5.0
105 + * @param string $base_url The default base URL
106 + */
107 + $base_url = apply_filters('templately_dev_api_base_url', $base_url);
108 +
109 + return "{$base_url}/api/{$endpoint}";
110 + }
111 +
112 + /**
113 + * Make a unified API request to Templately API
114 + *
115 + * @param string $method HTTP method (GET or POST)
116 + * @param string $api_url Complete API URL
117 + * @param array $body Request body data (for POST requests)
118 + * @param array $extra_headers Additional headers beyond the standard ones
119 + * @param int $timeout Request timeout in seconds (default: 30)
120 + * @return array|WP_Error Response array or WP_Error on failure
121 + */
122 + private static function make_api_request($method, $api_url, $body = [], $extra_headers = [], $timeout = 30) {
123 + $api_key = Options::get_instance()->get('api_key');
124 +
125 + $headers = [
126 + 'Authorization' => 'Bearer ' . $api_key,
127 + 'x-templately-ip' => self::get_ip(),
128 + 'x-templately-url' => home_url('/'),
129 + 'x-templately-version' => defined( 'TEMPLATELY_VERSION' ) ? constant( 'TEMPLATELY_VERSION' ) : '1.0.0',
130 + ];
131 +
132 + // Add Content-Type for POST requests
133 + if (strtoupper($method) === 'POST') {
134 + $headers['Content-Type'] = 'application/json';
135 + }
136 +
137 + // Merge additional headers
138 + $headers = array_merge($headers, $extra_headers);
139 +
140 + $args = [
141 + 'timeout' => $timeout,
142 + 'headers' => $headers,
143 + ];
144 +
145 + // Apply filter to allow network admin or other functionality to modify request args
146 + $args = apply_filters( 'templately_api_request_params', $args, $method, $api_url );
147 +
148 + // Add body for POST requests
149 + if (strtoupper($method) === 'POST') {
150 + $args['body'] = is_array($body) ? json_encode($body) : $body;
151 + }
152 +
153 + // Make the appropriate request
154 + if (strtoupper($method) === 'POST') {
155 + $response = wp_remote_post($api_url, $args);
156 + } else {
157 + $response = wp_remote_get($api_url, $args);
158 + }
159 +
160 + // Check for verification header in the response
161 + self::check_verification_header($response);
162 +
163 +
164 + // Check for site disconnection in response body
165 + self::check_site_disconnection($response);
166 +
167 + return $response;
168 + }
169 +
170 + /**
171 + * Make a GET request to Templately API
172 + *
173 + * @param string $endpoint API endpoint path (e.g., 'v2/import/pack/123')
174 + * @param array $query_params Query parameters as key-value pairs
175 + * @param array $extra_headers Additional headers beyond the standard ones
176 + * @param int $timeout Request timeout in seconds (default: 30)
177 + * @return array|WP_Error Response array or WP_Error on failure
178 + */
179 + public static function make_api_get_request($endpoint, $query_params = [], $extra_headers = [], $timeout = 30) {
180 + $api_url = self::get_api_url($endpoint);
181 +
182 + // Add query parameters if provided
183 + if (!empty($query_params)) {
184 + $api_url = add_query_arg($query_params, $api_url);
185 + }
186 +
187 + return self::make_api_request('GET', $api_url, [], $extra_headers, $timeout);
188 + }
189 +
190 + /**
191 + * Make a POST request to Templately API
192 + *
193 + * @param string $endpoint API endpoint path (e.g., 'v2/feedback/store')
194 + * @param array $body Request body data
195 + * @param array $extra_headers Additional headers beyond the standard ones
196 + * @param int $timeout Request timeout in seconds (default: 30)
197 + * @return array|WP_Error Response array or WP_Error on failure
198 + */
199 + public static function make_api_post_request($endpoint, $body = [], $extra_headers = [], $timeout = 30) {
200 + $api_url = self::get_api_url($endpoint);
201 + return self::make_api_request('POST', $api_url, $body, $extra_headers, $timeout);
202 + }
203 +
204 + /**
74 205 * Sanitize Helper
75 206 *
76 207 * @param mixed $value
77 208 * @param string $type
@@ -77,15 +208,15 @@
77 208 * @param string $type
78 209 *
79 210 * @return bool|string
80 211 */
81 - public static function sanitize( $value, $type = 'text' ){
82 - switch ( $type ) {
212 + public static function sanitize($value, $type = 'text') {
213 + switch ($type) {
83 214 case 'boolean':
84 - $sanitized_value = rest_sanitize_boolean( $value );
215 + $sanitized_value = rest_sanitize_boolean($value);
85 216 break;
86 217 default:
87 - $sanitized_value = sanitize_text_field( $value );
218 + $sanitized_value = sanitize_text_field($value);
88 219 break;
89 220 }
90 221
91 222 return $sanitized_value;
@@ -91,8 +222,156 @@
91 222 return $sanitized_value;
92 223 }
93 224
94 225 /**
226 + * Check for X-Templately-Verified header and update user verification status
227 + *
228 + * @param array|WP_Error $response The HTTP response array from wp_remote_get/wp_remote_post
229 + * @return void
230 + */
231 + public static function check_verification_header($response) {
232 + // Only process if response is not a WP_Error and contains headers
233 + if (is_wp_error($response)) {
234 + return;
235 + }
236 +
237 + // Retrieve the X-Templately-Verified header
238 + $verification_header = wp_remote_retrieve_header($response, 'X-Templately-Verified');
239 +
240 + // Check if header exists and has a truthy value
241 + if (!empty($verification_header) && filter_var($verification_header, FILTER_VALIDATE_BOOLEAN)) {
242 + try {
243 + // Get current user data
244 + $options = Options::get_instance();
245 + $user = $options->get('user');
246 +
247 + // Only update if user data exists and is not already verified
248 + if (!empty($user) && is_array($user) && empty($user['is_verified'])) {
249 + // Set verification flag
250 + $user['is_verified'] = true;
251 +
252 + // Save updated user data
253 + $options->set('user', $user);
254 +
255 + }
256 +
257 + if (!empty($user['is_verified'])){
258 + if(!headers_sent()){
259 + header( 'X-Templately-Verified: true' );
260 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
261 + self::log('User verification status already updated via X-Templately-Verified header');
262 + }
263 + }
264 +
265 + return true;
266 + }
267 + } catch (\Exception $e) {
268 + // Log error if debug logging is enabled
269 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
270 + self::log('Error updating user verification status: ' . $e->getMessage());
271 + }
272 + }
273 + }
274 +
275 + return false;
276 + }
277 +
278 + /**
279 + * Check for site disconnection status in API response body
280 + *
281 + * Detects SiteNotConnected errors and updates user disconnection status.
282 + * Sends X-Templately-Disconnected header for frontend detection.
283 + *
284 + *
285 + * @param array|WP_Error|mixed $response The response object or body array
286 + * @return bool True if site is disconnected, false otherwise
287 + */
288 + public static function check_site_disconnection($response) {
289 + if (is_wp_error($response)) {
290 + return false;
291 + }
292 +
293 + $response_body = $response;
294 +
295 + // If it's a raw WP response array with body, decode it
296 + if (is_array($response) && isset($response['body']) && is_string($response['body'])) {
297 + $response_body = json_decode(wp_remote_retrieve_body($response), true);
298 + }
299 +
300 + // Check if response body indicates site disconnection
301 + if (!is_array($response_body)) {
302 + return false;
303 + }
304 +
305 + $status = $response_body['status'] ?? null;
306 + $status_text = $response_body['statusText'] ?? null;
307 +
308 + // Check for SiteNotConnected error
309 + if ($status === 'error' && $status_text === 'SiteNotConnected') {
310 + try {
311 + // Get current user data
312 + $options = Options::get_instance();
313 + $user = $options->get('user');
314 +
315 + // Only update if user data exists
316 + if (!empty($user) && is_array($user)) {
317 + // Set disconnection flag
318 + $user['is_disconnected'] = true;
319 +
320 + // Save updated user data
321 + $options->set('user', $user);
322 +
323 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
324 + self::log('Site disconnection detected: SiteNotConnected status');
325 + }
326 + }
327 +
328 + // Send header for frontend detection
329 + if (!headers_sent()) {
330 + header('X-Templately-Disconnected: true');
331 + }
332 +
333 + return true;
334 + } catch (\Exception $e) {
335 + // Log error if debug logging is enabled
336 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
337 + self::log('Error updating site disconnection status: ' . $e->getMessage());
338 + }
339 + }
340 + }
341 +
342 + return false;
343 + }
344 +
345 + /**
346 + * Clear site disconnection status
347 + *
348 + * Called after successful site migration to reset the disconnection flag.
349 + *
350 + * @return void
351 + */
352 + public static function clear_site_disconnection() {
353 + try {
354 + $options = Options::get_instance();
355 + $user = $options->get('user');
356 +
357 + if (!empty($user) && is_array($user)) {
358 + $user['site_url'] = base64_encode( home_url('/') );
359 + $user['is_disconnected'] = false;
360 + $options->set('user', $user);
361 +
362 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
363 + self::log('Site disconnection status cleared and URL updated.');
364 + }
365 + }
366 + } catch (\Exception $e) {
367 + if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
368 + self::log('Error clearing site disconnection status: ' . $e->getMessage());
369 + }
370 + }
371 + }
372 +
373 + /**
95 374 * API Error Formatter
96 375 *
97 376 * @param int $error_code
98 377 * @param mixed $error_message
@@ -100,15 +379,18 @@
100 379 * @param integer $status
101 380 * @param array $additional_data
102 381 * @return WP_Error
103 382 */
104 - public static function error( $error_code, $error_message, $endpoint = '', $status = 500, $additional_data = [] ) {
383 + public static function error($error_code, $error_message, $endpoint = '', $status = 500, $additional_data = []) {
105 384 $additional_data['status'] = $status;
106 - if ( ! empty( $endpoint ) ) {
385 + if (! empty($endpoint)) {
107 386 $additional_data['endpoint'] = $endpoint;
108 387 }
388 + // Add browser padding to avoid browsers not serving small JSON responses
389 + $padding_length = 512;
390 + $additional_data['browser_padding'] = str_repeat(' ', $padding_length);
109 391
110 - return new WP_Error( $error_code, $error_message, $additional_data );
392 + return new WP_Error($error_code, $error_message, $additional_data);
111 393 }
112 394
113 395 /**
114 396 * API Response Formatter
@@ -115,10 +397,10 @@
115 397 *
116 398 * @param mixed $data
117 399 * @return WP_REST_Response
118 400 */
119 - public static function success( $data ) {
120 - return new WP_REST_Response( $data, 200 );
401 + public static function success($data) {
402 + return new WP_REST_Response($data, 200);
121 403 }
122 404
123 405 /**
124 406 * Normalize Favourites Data
@@ -128,42 +410,42 @@
128 410 * @param boolean $undo
129 411 *
130 412 * @return array
131 413 */
132 - public function normalizeFavourites( $favourites, $_favourites = [], $undo = false ){
133 - if( $undo ) {
134 - $_favourites[ $favourites['type'] ] = array_values(array_filter( $_favourites[ $favourites['type'] ], function( $item ) use( $favourites ) {
414 + public function normalizeFavourites($favourites, $_favourites = [], $undo = false) {
415 + if ($undo) {
416 + $_favourites[$favourites['type']] = array_values(array_filter($_favourites[$favourites['type']], function ($item) use ($favourites) {
135 417 return $item != $favourites['id'];
136 - } ));
418 + }));
137 419 return $_favourites;
138 420 }
139 421
140 - array_map( function( $item ) use ( &$_favourites) {
141 - if( ! is_null( $item ) ) {
422 + array_map(function ($item) use (&$_favourites) {
423 + if (! is_null($item)) {
142 424 $item = (array) $item;
143 - if ( isset( $_favourites[ $item['type'] ] ) ){
144 - $_favourites[ $item['type'] ][] = $item['id'];
425 + if (isset($_favourites[$item['type']])) {
426 + $_favourites[$item['type']][] = $item['id'];
145 427 } else {
146 - $_favourites[ $item['type'] ] = [ $item['id'] ];
428 + $_favourites[$item['type']] = [$item['id']];
147 429 }
148 430 }
149 431 return $_favourites;
150 - }, $favourites );
432 + }, $favourites);
151 433
152 434 return $_favourites;
153 435 }
154 436
155 - public function normalizeReviews( $favourites, $_favourites = [], $undo = false ){
156 - array_map( function( $item ) use ( &$_favourites) {
157 - if( ! is_null( $item ) ) {
437 + public function normalizeReviews($favourites, $_favourites = [], $undo = false) {
438 + array_map(function ($item) use (&$_favourites) {
439 + if (! is_null($item)) {
158 440 $item = (array) $item;
159 - if ( !isset( $_favourites[ $item['type'] ] ) ){
160 - $_favourites[ $item['type'] ] = [];
441 + if (!isset($_favourites[$item['type']])) {
442 + $_favourites[$item['type']] = [];
161 443 }
162 - $_favourites[ $item['type'] ][$item['type_id']] = $item['rating'];
444 + $_favourites[$item['type']][$item['type_id']] = $item['rating'];
163 445 }
164 446 return $_favourites;
165 - }, $favourites );
447 + }, $favourites);
166 448
167 449 return $_favourites;
168 450 }
169 451
@@ -172,10 +454,10 @@
172 454 *
173 455 * @param object $triggered_by
174 456 * @return void
175 457 */
176 - public static function trigger_error( $triggered_by, $method = 'get_instance' ){
177 - $class = get_class( $triggered_by );
458 + public static function trigger_error($triggered_by, $method = 'get_instance') {
459 + $class = get_class($triggered_by);
178 460 $trace = debug_backtrace(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
179 461 $file = $trace[0]['file'];
180 462 $line = $trace[0]['line'];
181 463 trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
@@ -183,22 +465,63 @@
183 465
184 466 /**
185 467 * Printing Error Logs in debug.log file.
186 468 *
187 - * @param mixed $log
469 + * @param mixed $log The data to log
470 + * @param string $context Optional context for categorizing log entries
471 + * @param string $level Optional log level (debug, info, warning, error)
188 472 * @return void
189 473 */
190 - public static function log( $log ){
191 - if ( defined('WP_DEBUG_LOG') && WP_DEBUG_LOG === true ) {
192 - if ( is_array( $log ) || is_object( $log ) ) {
193 - error_log( print_r( $log, true ) );
194 - } else {
195 - error_log( $log ?: '' );
196 - }
474 + public static function log($log, $context = '', $level = 'info') {
475 + // Allow complete override of logging behavior
476 + $override_result = apply_filters('templately_log_override', null, $log, $context, $level);
477 + if ($override_result !== null) {
478 + return;
197 479 }
480 +
481 + // Only log if WP_DEBUG_LOG is enabled
482 + if (!defined('WP_DEBUG_LOG') || !WP_DEBUG_LOG) {
483 + return;
484 + }
485 +
486 + // Format the log message
487 + $formatted_message = self::format_log_message($log, $context, $level);
488 +
489 + // Write to error log
490 + error_log($formatted_message);
198 491 }
199 492
200 - public static function should_flush(){
493 + /**
494 + * Format log message with context and level
495 + *
496 + * @param mixed $log The data to log
497 + * @param string $context Context for categorizing log entries
498 + * @param string $level Log level
499 + * @return string Formatted log message
500 + */
501 + private static function format_log_message($log, $context = '', $level = 'info') {
502 + // Convert arrays and objects to readable format
503 + if (is_array($log) || is_object($log)) {
504 + $log_content = print_r($log, true);
505 + } else {
506 + $log_content = (string) ($log ?: '');
507 + }
508 +
509 + // Build the formatted message
510 + $timestamp = current_time('Y-m-d H:i:s');
511 + $level_upper = strtoupper($level);
512 +
513 + if (!empty($context)) {
514 + return "[{$timestamp}] [{$level_upper}] [{$context}] {$log_content}";
515 + } else {
516 + return "[{$timestamp}] [{$level_upper}] {$log_content}";
517 + }
518 + }
519 +
520 + public static function should_flush() {
521 + if (isset($_REQUEST['is_lightspeed']) && $_REQUEST['is_lightspeed'] === 'true') {
522 + return false;
523 + }
201 524 return (!defined('TEMPLATELY_IGNORE_FLUSH_ALL') || !TEMPLATELY_IGNORE_FLUSH_ALL) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') === false;
202 525 }
203 526
204 527 public static function get_block_by_name($blocks, $search) {
@@ -206,9 +529,9 @@
206 529
207 530 while (!empty($queue)) {
208 531 $current_block = array_shift($queue);
209 532
210 - if($search === $current_block['blockName']){
533 + if ($search === $current_block['blockName']) {
211 534 return $current_block;
212 535 }
213 536
214 537 if (isset($current_block['innerBlocks'])) {
@@ -230,13 +553,13 @@
230 553 public static function current_user_can($cap, ...$args) {
231 554 $user = wp_get_current_user();
232 555
233 556 // Multisite super admin has all caps by definition, Unless specifically denied.
234 - if ( is_multisite() && is_super_admin( $user->ID ) ) {
557 + if (is_multisite() && is_super_admin($user->ID)) {
235 558 return true;
236 559 }
237 560
238 - $caps = map_meta_cap( $cap, $user->ID, ...$args );
561 + $caps = map_meta_cap($cap, $user->ID, ...$args);
239 562
240 563 switch ($cap) {
241 564 case 'install_plugins':
242 565 case 'upload_plugins':
@@ -256,24 +579,24 @@
256 579 break;
257 580 }
258 581
259 582 // Maintain BC for the argument passed to the "user_has_cap" filter.
260 - $args = array_merge( array( $cap, $user->ID ), $args );
583 + $args = array_merge(array($cap, $user->ID), $args);
261 584
262 585 /**
263 586 * See WP_User::has_cap() for description.
264 587 */
265 - $capabilities = apply_filters( 'user_has_cap', $user->allcaps, $caps, $args, $user );
588 + $capabilities = apply_filters('user_has_cap', $user->allcaps, $caps, $args, $user);
266 589
267 590 // Everyone is allowed to exist.
268 591 $capabilities['exist'] = true;
269 592
270 593 // Nobody is allowed to do things they are not allowed to do.
271 - unset( $capabilities['do_not_allow'] );
594 + unset($capabilities['do_not_allow']);
272 595
273 596 // Must have ALL requested caps.
274 - foreach ( (array) $caps as $cap ) {
275 - if ( empty( $capabilities[ $cap ] ) ) {
597 + foreach ((array) $caps as $cap) {
598 + if (empty($capabilities[$cap])) {
276 599 return false;
277 600 }
278 601 }
279 602
@@ -279,5 +602,77 @@
279 602
280 603 return true;
281 604 }
282 605
283 -}
606 + /**
607 + * Calculates the elapsed time and checks if it is close to the maximum execution time.
608 + * Returns true if the script should exit to avoid exceeding the limit.
609 + *
610 + * @return bool True if the script should exit, false otherwise.
611 + */
612 + public static function fsi_should_exit() {
613 + if (defined('TEMPLATELY_START_TIME') && ini_get('max_execution_time')) {
614 + $max_time = ini_get('max_execution_time');
615 + $elapsed = microtime(true) - TEMPLATELY_START_TIME;
616 + $delay = max(5, $max_time * 20 / 100);
617 +
618 + // Check if elapsed time is close to max execution time
619 + if ($max_time - $elapsed <= $delay) {
620 + return ['max_time' => $max_time, 'elapsed' => $elapsed, 'delay' => $delay];
621 + }
622 + }
623 + return false;
624 + }
625 +
626 + /**
627 + * Enable Elementor Container
628 + * This function will enable the Elementor Container feature.
629 + * Without this feature, some of the templates may not work properly.
630 + *
631 + * @return boolean
632 + */
633 + public static function enable_elementor_container() {
634 + if (class_exists('Elementor\Plugin')) {
635 + $control_name = Plugin::instance()->experiments->get_feature_option_key('container');
636 + if (get_option($control_name) !== 'active') {
637 + update_option($control_name, 'active');
638 + return true;
639 + }
640 + }
641 + return false;
642 + }
643 +
644 + /**
645 + * Undocumented function
646 + *
647 + * @param [type] $args
648 + * @param [type] $defaults
649 + * @return array
650 + */
651 + public static function recursive_wp_parse_args($args, $defaults) {
652 + $args = (array) $args;
653 + $defaults = (array) $defaults;
654 + $r = $defaults;
655 + foreach ($args as $key => $value) {
656 + if (is_array($value) && isset($r[$key])) {
657 + // also handle numeric array. if both $value and $r[ $key ] are numeric array. wp_is_numeric_array()
658 + if (wp_is_numeric_array($value) && wp_is_numeric_array($r[$key])) {
659 + foreach ($value as $k => $v) {
660 + if (!in_array($v, $r[$key])) {
661 + if (!isset($r[$key][$k])) {
662 + $r[$key][$k] = $v;
663 + } else {
664 + $r[$key][] = $v;
665 + }
666 + }
667 + }
668 + } else {
669 + $r[$key] = self::recursive_wp_parse_args($value, $r[$key]);
670 + }
671 + } else {
672 + $r[$key] = $value;
673 + }
674 + }
675 + return $r;
676 + }
677 +
678 +}