PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.5.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.5.0
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
templately / includes / Utils / Helper.php

Helper.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.5.0, at includes/Utils/Helper.php

580 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Utils;
4
5 use Elementor\Plugin;
6 use Templately\Core\Importer\Utils\Utils;
7 use WP_Error;
8 use WP_REST_Response;
9 use function get_plugins;
10 use function is_plugin_active;
11
12 /**
13 * Utility Helper for Templately
14 *
15 * This class contains some helper functions for easy access.
16 *
17 * @since 1.0.0
18 */
19 class Helper extends Base {
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 }
29
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 }
45
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 /**
58 * Collect IP from request.
59 *
60 * @return string
61 */
62 public static function get_ip() {
63 $ip = '127.0.0.1'; // Local IP
64 if (! empty($_SERVER['HTTP_CLIENT_IP'])) {
65 $ip = $_SERVER['HTTP_CLIENT_IP'];
66 } elseif (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
67 $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
68 } else {
69 $ip = ! empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : $ip;
70 }
71
72 return sanitize_text_field($ip);
73 }
74
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';
86
87 if (is_readable($file)) {
88 include_once $file;
89 }
90 }
91
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 return $response;
164 }
165
166 /**
167 * Make a GET request to Templately API
168 *
169 * @param string $endpoint API endpoint path (e.g., 'v2/import/pack/123')
170 * @param array $query_params Query parameters as key-value pairs
171 * @param array $extra_headers Additional headers beyond the standard ones
172 * @param int $timeout Request timeout in seconds (default: 30)
173 * @return array|WP_Error Response array or WP_Error on failure
174 */
175 public static function make_api_get_request($endpoint, $query_params = [], $extra_headers = [], $timeout = 30) {
176 $api_url = self::get_api_url($endpoint);
177
178 // Add query parameters if provided
179 if (!empty($query_params)) {
180 $api_url = add_query_arg($query_params, $api_url);
181 }
182
183 return self::make_api_request('GET', $api_url, [], $extra_headers, $timeout);
184 }
185
186 /**
187 * Make a POST request to Templately API
188 *
189 * @param string $endpoint API endpoint path (e.g., 'v2/feedback/store')
190 * @param array $body Request body data
191 * @param array $extra_headers Additional headers beyond the standard ones
192 * @param int $timeout Request timeout in seconds (default: 30)
193 * @return array|WP_Error Response array or WP_Error on failure
194 */
195 public static function make_api_post_request($endpoint, $body = [], $extra_headers = [], $timeout = 30) {
196 $api_url = self::get_api_url($endpoint);
197 return self::make_api_request('POST', $api_url, $body, $extra_headers, $timeout);
198 }
199
200 /**
201 * Sanitize Helper
202 *
203 * @param mixed $value
204 * @param string $type
205 *
206 * @return bool|string
207 */
208 public static function sanitize($value, $type = 'text') {
209 switch ($type) {
210 case 'boolean':
211 $sanitized_value = rest_sanitize_boolean($value);
212 break;
213 default:
214 $sanitized_value = sanitize_text_field($value);
215 break;
216 }
217
218 return $sanitized_value;
219 }
220
221 /**
222 * Check for X-Templately-Verified header and update user verification status
223 *
224 * @param array|WP_Error $response The HTTP response array from wp_remote_get/wp_remote_post
225 * @return void
226 */
227 public static function check_verification_header($response) {
228 // Only process if response is not a WP_Error and contains headers
229 if (is_wp_error($response)) {
230 return;
231 }
232
233 // Retrieve the X-Templately-Verified header
234 $verification_header = wp_remote_retrieve_header($response, 'X-Templately-Verified');
235
236 // Check if header exists and has a truthy value
237 if (!empty($verification_header) && filter_var($verification_header, FILTER_VALIDATE_BOOLEAN)) {
238 try {
239 // Get current user data
240 $options = Options::get_instance();
241 $user = $options->get('user');
242
243 // Only update if user data exists and is not already verified
244 if (!empty($user) && is_array($user) && empty($user['is_verified'])) {
245 // Set verification flag
246 $user['is_verified'] = true;
247
248 // Save updated user data
249 $options->set('user', $user);
250
251 }
252
253 if (!empty($user['is_verified'])){
254 if(!headers_sent()){
255 header( 'X-Templately-Verified: true' );
256 if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
257 self::log('User verification status already updated via X-Templately-Verified header');
258 }
259 }
260
261 return true;
262 }
263 } catch (\Exception $e) {
264 // Log error if debug logging is enabled
265 if (defined('TEMPLATELY_DEBUG_LOG') && constant('TEMPLATELY_DEBUG_LOG')) {
266 self::log('Error updating user verification status: ' . $e->getMessage());
267 }
268 }
269 }
270
271 return false;
272 }
273
274 /**
275 * API Error Formatter
276 *
277 * @param int $error_code
278 * @param mixed $error_message
279 * @param string $endpoint
280 * @param integer $status
281 * @param array $additional_data
282 * @return WP_Error
283 */
284 public static function error($error_code, $error_message, $endpoint = '', $status = 500, $additional_data = []) {
285 $additional_data['status'] = $status;
286 if (! empty($endpoint)) {
287 $additional_data['endpoint'] = $endpoint;
288 }
289 // Add browser padding to avoid browsers not serving small JSON responses
290 $padding_length = 512;
291 $additional_data['browser_padding'] = str_repeat(' ', $padding_length);
292
293 return new WP_Error($error_code, $error_message, $additional_data);
294 }
295
296 /**
297 * API Response Formatter
298 *
299 * @param mixed $data
300 * @return WP_REST_Response
301 */
302 public static function success($data) {
303 return new WP_REST_Response($data, 200);
304 }
305
306 /**
307 * Normalize Favourites Data
308 *
309 * @param array $favourites
310 * @param array $_favourites
311 * @param boolean $undo
312 *
313 * @return array
314 */
315 public function normalizeFavourites($favourites, $_favourites = [], $undo = false) {
316 if ($undo) {
317 $_favourites[$favourites['type']] = array_values(array_filter($_favourites[$favourites['type']], function ($item) use ($favourites) {
318 return $item != $favourites['id'];
319 }));
320 return $_favourites;
321 }
322
323 array_map(function ($item) use (&$_favourites) {
324 if (! is_null($item)) {
325 $item = (array) $item;
326 if (isset($_favourites[$item['type']])) {
327 $_favourites[$item['type']][] = $item['id'];
328 } else {
329 $_favourites[$item['type']] = [$item['id']];
330 }
331 }
332 return $_favourites;
333 }, $favourites);
334
335 return $_favourites;
336 }
337
338 public function normalizeReviews($favourites, $_favourites = [], $undo = false) {
339 array_map(function ($item) use (&$_favourites) {
340 if (! is_null($item)) {
341 $item = (array) $item;
342 if (!isset($_favourites[$item['type']])) {
343 $_favourites[$item['type']] = [];
344 }
345 $_favourites[$item['type']][$item['type_id']] = $item['rating'];
346 }
347 return $_favourites;
348 }, $favourites);
349
350 return $_favourites;
351 }
352
353 /**
354 * Trigger Error
355 *
356 * @param object $triggered_by
357 * @return void
358 */
359 public static function trigger_error($triggered_by, $method = 'get_instance') {
360 $class = get_class($triggered_by);
361 $trace = debug_backtrace(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
362 $file = $trace[0]['file'];
363 $line = $trace[0]['line'];
364 trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
365 }
366
367 /**
368 * Printing Error Logs in debug.log file.
369 *
370 * @param mixed $log The data to log
371 * @param string $context Optional context for categorizing log entries
372 * @param string $level Optional log level (debug, info, warning, error)
373 * @return void
374 */
375 public static function log($log, $context = '', $level = 'info') {
376 // Allow complete override of logging behavior
377 $override_result = apply_filters('templately_log_override', null, $log, $context, $level);
378 if ($override_result !== null) {
379 return;
380 }
381
382 // Only log if WP_DEBUG_LOG is enabled
383 if (!defined('WP_DEBUG_LOG') || !WP_DEBUG_LOG) {
384 return;
385 }
386
387 // Format the log message
388 $formatted_message = self::format_log_message($log, $context, $level);
389
390 // Write to error log
391 error_log($formatted_message);
392 }
393
394 /**
395 * Format log message with context and level
396 *
397 * @param mixed $log The data to log
398 * @param string $context Context for categorizing log entries
399 * @param string $level Log level
400 * @return string Formatted log message
401 */
402 private static function format_log_message($log, $context = '', $level = 'info') {
403 // Convert arrays and objects to readable format
404 if (is_array($log) || is_object($log)) {
405 $log_content = print_r($log, true);
406 } else {
407 $log_content = (string) ($log ?: '');
408 }
409
410 // Build the formatted message
411 $timestamp = current_time('Y-m-d H:i:s');
412 $level_upper = strtoupper($level);
413
414 if (!empty($context)) {
415 return "[{$timestamp}] [{$level_upper}] [{$context}] {$log_content}";
416 } else {
417 return "[{$timestamp}] [{$level_upper}] {$log_content}";
418 }
419 }
420
421 public static function should_flush() {
422 if (isset($_REQUEST['is_lightspeed']) && $_REQUEST['is_lightspeed'] === 'true') {
423 return false;
424 }
425 return (!defined('TEMPLATELY_IGNORE_FLUSH_ALL') || !TEMPLATELY_IGNORE_FLUSH_ALL) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') === false;
426 }
427
428 public static function get_block_by_name($blocks, $search) {
429 $queue = $blocks;
430
431 while (!empty($queue)) {
432 $current_block = array_shift($queue);
433
434 if ($search === $current_block['blockName']) {
435 return $current_block;
436 }
437
438 if (isset($current_block['innerBlocks'])) {
439 // Add nested blocks to the end of the queue for processing
440 $queue = array_merge($queue, $current_block['innerBlocks']);
441 }
442 }
443
444 return false;
445 }
446
447 /**
448 * Only checks if user can install/activate plugins
449 *
450 * @param [type] $cap
451 * @param [type] ...$args
452 * @return void
453 */
454 public static function current_user_can($cap, ...$args) {
455 $user = wp_get_current_user();
456
457 // Multisite super admin has all caps by definition, Unless specifically denied.
458 if (is_multisite() && is_super_admin($user->ID)) {
459 return true;
460 }
461
462 $caps = map_meta_cap($cap, $user->ID, ...$args);
463
464 switch ($cap) {
465 case 'install_plugins':
466 case 'upload_plugins':
467 $caps = ['install_plugins'];
468 break;
469 case 'install_themes':
470 case 'upload_themes':
471 $caps = ['install_themes'];
472 break;
473 case 'activate_plugins':
474 case 'deactivate_plugins':
475 case 'activate_plugin':
476 case 'deactivate_plugin':
477 $caps = ['activate_plugins'];
478 break;
479 default:
480 break;
481 }
482
483 // Maintain BC for the argument passed to the "user_has_cap" filter.
484 $args = array_merge(array($cap, $user->ID), $args);
485
486 /**
487 * See WP_User::has_cap() for description.
488 */
489 $capabilities = apply_filters('user_has_cap', $user->allcaps, $caps, $args, $user);
490
491 // Everyone is allowed to exist.
492 $capabilities['exist'] = true;
493
494 // Nobody is allowed to do things they are not allowed to do.
495 unset($capabilities['do_not_allow']);
496
497 // Must have ALL requested caps.
498 foreach ((array) $caps as $cap) {
499 if (empty($capabilities[$cap])) {
500 return false;
501 }
502 }
503
504 return true;
505 }
506
507 /**
508 * Calculates the elapsed time and checks if it is close to the maximum execution time.
509 * Returns true if the script should exit to avoid exceeding the limit.
510 *
511 * @return bool True if the script should exit, false otherwise.
512 */
513 public static function fsi_should_exit() {
514 if (defined('TEMPLATELY_START_TIME') && ini_get('max_execution_time')) {
515 $max_time = ini_get('max_execution_time');
516 $elapsed = microtime(true) - TEMPLATELY_START_TIME;
517 $delay = max(5, $max_time * 20 / 100);
518
519 // Check if elapsed time is close to max execution time
520 if ($max_time - $elapsed <= $delay) {
521 return ['max_time' => $max_time, 'elapsed' => $elapsed, 'delay' => $delay];
522 }
523 }
524 return false;
525 }
526
527 /**
528 * Enable Elementor Container
529 * This function will enable the Elementor Container feature.
530 * Without this feature, some of the templates may not work properly.
531 *
532 * @return boolean
533 */
534 public static function enable_elementor_container() {
535 if (class_exists('Elementor\Plugin')) {
536 $control_name = Plugin::instance()->experiments->get_feature_option_key('container');
537 if (get_option($control_name) !== 'active') {
538 update_option($control_name, 'active');
539 return true;
540 }
541 }
542 return false;
543 }
544
545 /**
546 * Undocumented function
547 *
548 * @param [type] $args
549 * @param [type] $defaults
550 * @return array
551 */
552 public static function recursive_wp_parse_args($args, $defaults) {
553 $args = (array) $args;
554 $defaults = (array) $defaults;
555 $r = $defaults;
556 foreach ($args as $key => $value) {
557 if (is_array($value) && isset($r[$key])) {
558 // also handle numeric array. if both $value and $r[ $key ] are numeric array. wp_is_numeric_array()
559 if (wp_is_numeric_array($value) && wp_is_numeric_array($r[$key])) {
560 foreach ($value as $k => $v) {
561 if (!in_array($v, $r[$key])) {
562 if (!isset($r[$key][$k])) {
563 $r[$key][$k] = $v;
564 } else {
565 $r[$key][] = $v;
566 }
567 }
568 }
569 } else {
570 $r[$key] = self::recursive_wp_parse_args($value, $r[$key]);
571 }
572 } else {
573 $r[$key] = $value;
574 }
575 }
576 return $r;
577 }
578
579 }
580