PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 4.6.0
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v4.6.0
4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / EmbedPress / Includes / Classes / Helper.php
embedpress / EmbedPress / Includes / Classes Last commit date
Analytics 2 weeks ago Database 2 months ago DynamicFieldResolver.php 1 month ago Elementor_Enhancer.php 6 months ago EmbedPress_Core_Installer.php 6 years ago EmbedPress_Notice.php 3 months ago EmbedPress_Plugin_Usage_Tracker.php 1 month ago Extend_CustomPlayer_Controls.php 2 months ago Extend_Elementor_Controls.php 1 year ago FeatureNoticeManager.php 5 days ago FeatureNotices.php 5 days ago FeaturePreviewModal.php 5 days ago Feature_Enhancer.php 1 month ago GoogleReviewsAdminPage.php 5 days ago GoogleReviewsApify.php 5 days ago GoogleReviewsManaged.php 5 days ago GoogleReviewsRenderer.php 5 days ago GoogleReviewsRestController.php 5 days ago GoogleReviewsStore.php 5 days ago Helper.php 5 days ago Pdf_Thumbnail_Handler.php 2 months ago PermalinkHelper.php 10 months ago View_Count_Display.php 2 weeks ago
Helper.php
1879 lines
1 <?php
2
3 namespace EmbedPress\Includes\Classes;
4
5 use EmbedPress\Providers\TemplateLayouts\YoutubeLayout;
6 use EmbedPress\Shortcode;
7
8 use Elementor\Plugin;
9
10
11 if (!defined('ABSPATH')) {
12 exit;
13 } // Exit if accessed directly
14
15 class Helper
16 {
17
18 /**
19 * Parse a query string into an associative array.
20 *
21 * If multiple values are found for the same key, the value of that key
22 * value pair will become an array. This function does not parse nested
23 * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
24 * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
25 *
26 * @param string $str Query string to parse
27 * @param int|bool $urlEncoding How the query string is encoded
28 *
29 * @return array
30 */
31
32 public function __construct()
33 {
34 add_action('wp_ajax_lock_content_form_handler', [$this, 'lock_content_form_handler']);
35 add_action('wp_ajax_nopriv_lock_content_form_handler', [$this, 'lock_content_form_handler']);
36
37 add_action('wp_ajax_embedpress_gutenberg_password_check', [$this, 'gutenberg_password_check']);
38 add_action('wp_ajax_nopriv_embedpress_gutenberg_password_check', [$this, 'gutenberg_password_check']);
39
40
41 add_action('wp_ajax_loadmore_data_handler', [$this, 'loadmore_data_handler']);
42 add_action('wp_ajax_nopriv_loadmore_data_handler', [$this, 'loadmore_data_handler']);
43
44
45 add_action('wp_ajax_fetch_video_description', [$this, 'ajax_video_popup_description']);
46 add_action('wp_ajax_nopriv_fetch_video_description', [$this, 'ajax_video_popup_description']);
47 }
48
49 public function ajax_video_popup_description()
50 {
51 if (isset($_POST['vid'])) {
52 $api_key = self::get_api_key();
53 $vid = sanitize_text_field($_POST['vid']);
54
55 $video_data = Helper::get_youtube_video_data($api_key, $vid);
56
57 if ($video_data) {
58 ob_start();
59 ?>
60 <div class="video-description">
61 <?php echo YoutubeLayout::generate_youtube_video_description($video_data); ?>
62 </div>
63 <?php
64 $description_html = ob_get_clean();
65 wp_send_json_success(['description' => $description_html]);
66 } else {
67 wp_send_json_error(['error' => 'Failed to fetch video data.']);
68 }
69 } else {
70 wp_send_json_error(['error' => 'Invalid video ID.']);
71 }
72 }
73
74 public static function get_api_key()
75 {
76 $settings = (array) get_option(EMBEDPRESS_PLG_NAME . ':youtube', []);
77 return !empty($settings['api_key']) ? $settings['api_key'] : '';
78 }
79
80 public static function parse_query($str, $urlEncoding = true)
81 {
82 $result = [];
83
84 if ($str === '') {
85 return $result;
86 }
87
88 if ($urlEncoding === true) {
89 $decoder = function ($value) {
90 return rawurldecode(str_replace('+', ' ', $value));
91 };
92 } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
93 $decoder = 'rawurldecode';
94 } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
95 $decoder = 'urldecode';
96 } else {
97 $decoder = function ($str) {
98 return $str;
99 };
100 }
101
102 foreach (explode('&', $str) as $kvp) {
103 $parts = explode('=', $kvp, 2);
104 $key = $decoder($parts[0]);
105 $value = isset($parts[1]) ? $decoder($parts[1]) : null;
106 if (!isset($result[$key])) {
107 $result[$key] = $value;
108 } else {
109 if (!is_array($result[$key])) {
110 $result[$key] = [$result[$key]];
111 }
112 $result[$key][] = $value;
113 }
114 }
115
116 return $result;
117 }
118 public static function get_pdf_renderer()
119 {
120 // $renderer = EMBEDPRESS_URL_ASSETS. 'pdf/web/viewer.html';
121
122 $renderer = admin_url('admin-ajax.php?action=get_viewer');
123
124 // @TODO; apply settings query args here
125 return $renderer;
126 }
127
128 public static function get_flipbook_renderer()
129 {
130 $renderer = admin_url('admin-ajax.php?action=get_flipbook_viewer');
131 return $renderer;
132 }
133
134 public static function get_extension_from_file_url($url)
135 {
136 $path = parse_url((string) $url, PHP_URL_PATH);
137 if (empty($path)) {
138 $path = (string) $url;
139 }
140 $urlSplit = explode(".", $path);
141 $ext = end($urlSplit);
142 return strtolower($ext);
143 }
144
145
146 public static function is_instagram_feed($url)
147 {
148 return (bool) preg_match('~(?:https?://)?(?:www\.)?instagram\.com/(?!p/|reel/|tv/|stories/)[^/]+/?$~i', (string) $url);
149 }
150
151 public static function is_file_url($url)
152 {
153 $pattern = '/\.([0-9a-z]+)(?=[?#])|(\.)(?:[\w]+)$/i';
154 return preg_match($pattern, $url) === 1;
155 }
156
157 public static function is_opensea($url)
158 {
159 return strpos($url, "opensea.io") !== false;
160 }
161 public static function is_youtube_channel($url)
162 {
163 return (bool) (preg_match('~(?:https?:\/\/)?(?:www\.)?(?:youtube.com\/)(?:channel\/|c\/|user\/|@)(\w+)~i', (string) $url));
164 }
165
166 public static function is_youtube_playlist($url)
167 {
168 // /playlist?list=PL… OR /watch?…&list=… (Mix radio, video-in-playlist).
169 $u = str_replace(['&#038;', '&amp;'], '&', (string) $url);
170 return (bool) (preg_match('~^https?://(?:www\.)?youtube\.com/(?:playlist|watch)\?(?:[^#]*&)?list=([\w-]+)~i', $u));
171 }
172
173 public static function is_youtube_channel_or_playlist($url)
174 {
175 return self::is_youtube_channel($url) || self::is_youtube_playlist($url);
176 }
177
178 public static function is_youtube($url)
179 {
180 return (bool) (preg_match('~(?:https?://)?(?:www\.)?(?:youtube\.com|youtu\.be)/watch\?v=([^&]+)~i', (string) $url));
181 }
182
183 // Saved sources data temporary in wp_options table
184 public static function get_source_data($blockid, $source_url, $source_option_name, $source_temp_option_name)
185 {
186 if (self::is_youtube_channel($source_url)) {
187 $source_name = 'YoutubeChannel';
188 } else if (self::is_youtube($source_url)) {
189 $source_name = 'Youtube';
190 } else if (!empty(self::is_file_url($source_url))) {
191 $source_name = 'document_' . self::get_extension_from_file_url($source_url);
192 } else if (self::is_opensea($source_url)) {
193 $source_name = 'OpenSea';
194 } else if (self::is_instagram_feed($source_url)) {
195 $source_name = 'InstagramFeed';
196 } else {
197 Shortcode::get_embera_instance();
198 $collectios = Shortcode::get_collection();
199 $provider = $collectios->findProviders($source_url);
200
201 if (!empty($provider[$source_url])) {
202 $source_name = $provider[$source_url]->getProviderName();
203 } else {
204 $host = parse_url($source_url, PHP_URL_HOST);
205 if ($host) {
206 $parts = explode('.', $host);
207 if (count($parts) > 1) {
208 $source_name = $parts[1];
209 } else {
210 // Handle the case where the host doesn't have at least two parts
211 $source_name = $host;
212 }
213 } else {
214 // Handle the case where parse_url fails
215 $source_name = 'unknown';
216 }
217 }
218 }
219
220 if (!empty($blockid) && $blockid != 'undefined') {
221 $sources = json_decode(get_option($source_temp_option_name), true);
222
223 if (!$sources) {
224 $sources = array();
225 }
226 $exists = false;
227
228 foreach ($sources as $i => $source) {
229 if ($source['id'] === $blockid) {
230 $sources[$i]['source']['name'] = $source_name;
231 $sources[$i]['source']['url'] = $source_url;
232 $exists = true;
233 break;
234 }
235 }
236
237 if (!$exists) {
238 $sources[] = array('id' => $blockid, 'source' => array('name' => $source_name, 'url' => $source_url, 'count' => 1));
239 }
240
241 update_option($source_temp_option_name, json_encode($sources));
242 }
243 }
244
245 /**
246 * One-time scan of existing post content to seed the source-data options.
247 *
248 * Uses two SQL-prefiltered queries (post_content + _elementor_data postmeta)
249 * so a site with thousands of posts only loads the rows that actually
250 * mention "embedpress" — avoiding the N+1 round-trips of get_posts() +
251 * per-post get_post_field() / get_post_meta().
252 */
253 public static function seed_source_data_from_existing_content()
254 {
255 if (get_option('embedpress_source_data_seeded')) {
256 return;
257 }
258
259 global $wpdb;
260
261 $limit = (int) apply_filters('embedpress_seed_source_data_limit', 2000);
262 $like = '%' . $wpdb->esc_like('embedpress') . '%';
263
264 // Posts whose content mentions embedpress (blocks or shortcodes).
265 $content_rows = $wpdb->get_results($wpdb->prepare(
266 "SELECT ID, post_content
267 FROM {$wpdb->posts}
268 WHERE post_status IN ('publish','draft','private','future','pending')
269 AND post_type NOT IN ('revision','attachment','nav_menu_item','customize_changeset','oembed_cache')
270 AND post_content LIKE %s
271 LIMIT %d",
272 $like,
273 $limit
274 ));
275
276 if ($content_rows) {
277 foreach ($content_rows as $row) {
278 $post_id = (int) $row->ID;
279 $content = (string) $row->post_content;
280
281 // Gutenberg: <!-- wp:embedpress/* {"url":"..."} -->
282 if (preg_match_all('/wp:embedpress[^>]*?"url"\s*:\s*"([^"]+)"/', $content, $m)) {
283 foreach ($m[1] as $i => $url) {
284 self::get_source_data(
285 md5("seed-{$post_id}-g-{$i}"),
286 wp_unslash($url),
287 'gutenberg_source_data',
288 'gutenberg_temp_source_data'
289 );
290 }
291 }
292
293 // Shortcode: [embedpress ...]URL[/embedpress]
294 if (preg_match_all('/\[embedpress[^\]]*\](.*?)\[\/embedpress\]/s', $content, $m)) {
295 foreach ($m[1] as $i => $url) {
296 $url = trim(wp_unslash($url));
297 if ($url === '') continue;
298 self::get_source_data(
299 md5("seed-{$post_id}-s-{$i}"),
300 $url,
301 'gutenberg_source_data',
302 'gutenberg_temp_source_data'
303 );
304 }
305 }
306 }
307 }
308
309 // Elementor widgets — postmeta-level prefilter.
310 $meta_rows = $wpdb->get_results($wpdb->prepare(
311 "SELECT post_id, meta_value
312 FROM {$wpdb->postmeta}
313 WHERE meta_key = %s
314 AND meta_value LIKE %s
315 LIMIT %d",
316 '_elementor_data',
317 $like,
318 $limit
319 ));
320
321 if ($meta_rows) {
322 foreach ($meta_rows as $row) {
323 $post_id = (int) $row->post_id;
324 $el = (string) $row->meta_value;
325 if (preg_match_all('/"widgetType":"embedpress[^"]*".*?"(?:embedpress_pro_embeded_link|embed_url|url|pdf_source|src)":"([^"]+)"/', $el, $m)) {
326 foreach ($m[1] as $i => $url) {
327 self::get_source_data(
328 md5("seed-{$post_id}-e-{$i}"),
329 stripslashes($url),
330 'elementor_source_data',
331 'elementor_temp_source_data'
332 );
333 }
334 }
335 }
336 }
337
338 // Promote temp → real without needing save_post.
339 self::get_save_source_data_on_post_update('gutenberg_source_data', 'gutenberg_temp_source_data');
340 self::get_save_source_data_on_post_update('elementor_source_data', 'elementor_temp_source_data');
341
342 update_option('embedpress_source_data_seeded', time(), false);
343 }
344
345 // Saved source data when post updated
346 public static function get_save_source_data_on_post_update($source_option_name, $source_temp_option_name)
347 {
348
349 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
350 return;
351 }
352 $temp_data = json_decode(get_option($source_temp_option_name), true);
353 $source_data = json_decode(get_option($source_option_name), true);
354 if (!$temp_data) {
355 $temp_data = array();
356 }
357 if (!$source_data) {
358 $source_data = array();
359 }
360
361 $sources = array_merge($temp_data, $source_data);
362
363 $unique_sources = array();
364 foreach ($sources as $source) {
365 $unique_sources[$source['id']] = $source;
366 }
367
368 $unique_sources = array_values($unique_sources);
369
370 delete_option($source_temp_option_name);
371
372 update_option($source_option_name, json_encode($unique_sources));
373 }
374
375 //Delete source temporary data when reload without update or publish
376 public static function get_delete_source_temp_data_on_reload($source_temp_option_name)
377 {
378 $source_temp_data = json_decode(get_option($source_temp_option_name), true);
379 if ($source_temp_data) {
380 delete_option($source_temp_option_name);
381 }
382 }
383
384 public static function get_file_title($url)
385 {
386 return get_the_title(attachment_url_to_postid($url));
387 }
388
389 public static function get_hash()
390 {
391 $hash_key = get_option(EMBEDPRESS_PLG_NAME . '_hash_key');
392 if (!$hash_key) {
393 $hash_key = wp_hash_password(wp_generate_password(30));
394 update_option(EMBEDPRESS_PLG_NAME . '_hash_key', $hash_key);
395 }
396 return $hash_key;
397 }
398
399 public function lock_content_form_handler() {
400 // print_r($embedHTML);
401
402 $client_id = isset($_POST['client_id']) ? sanitize_text_field($_POST['client_id']) : '';
403 $password = isset($_POST['password']) ? sanitize_text_field($_POST['password']) : '';
404 $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
405
406 $epbase64 = get_post_meta( $post_id, 'ep_base_' .$client_id, true );
407 $hash_key = get_post_meta( $post_id, 'hash_key_' .$client_id, true );
408
409 // Set the decryption key and initialization vector (IV)
410 $key = Helper::get_hash();
411
412 // Decode the base64 encoded cipher
413 $cipher = base64_decode($epbase64);
414 // Decrypt the cipher using AES-128-CBC encryption
415
416 $wp_pass_key = hash('sha256', wp_salt(32) . md5($password));
417 $iv = substr($wp_pass_key, 0, 16);
418
419 if ($wp_pass_key === $hash_key) {
420
421 $embed = openssl_decrypt($cipher, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv) . '<script>
422 var now = new Date();
423 var time = now.getTime();
424 var expireTime = time + 1000 * 60 * 60 * 24 * 30;
425 now.setTime(expireTime);
426 document.cookie = "password_correct_'.esc_js($client_id).'='.esc_js($hash_key).'; expires=" + now.toUTCString() + "; path=/";
427 </script>';
428
429 }
430 else{
431 $embed = 0;
432 }
433
434 // Process the form data and return a response
435 $response = array(
436 'success' => true,
437 'password' => $password,
438 'embedHtml' => $embed,
439 'post_id' => $post_id
440 );
441
442 wp_send_json($response);
443
444 }
445
446 public function gutenberg_password_check()
447 {
448 $client_id = isset($_POST['client_id']) ? sanitize_text_field($_POST['client_id']) : '';
449 $password = isset($_POST['password']) ? sanitize_text_field($_POST['password']) : '';
450 $content_password = isset($_POST['content_password']) ? sanitize_text_field($_POST['content_password']) : '';
451
452 // Verify password
453 if ($password === $content_password) {
454 // Set cookie for authentication
455 $hash_pass = hash('sha256', wp_salt(32) . md5($password));
456 setcookie("password_correct_" . $client_id, $hash_pass, time() + 3600, '/');
457
458 $response = array(
459 'success' => true,
460 'message' => 'Password correct'
461 );
462 } else {
463 $response = array(
464 'success' => false,
465 'message' => 'Incorrect password'
466 );
467 }
468
469 wp_send_json($response);
470 }
471
472 public static function display_password_form($client_id = '', $embedHtml = '', $pass_hash_key = '', $attributes = [])
473 {
474 $lock_heading = !empty($attributes['lockHeading']) ? sanitize_text_field($attributes['lockHeading']) : '';
475 $lock_subheading = !empty($attributes['lockSubHeading']) ? sanitize_text_field($attributes['lockSubHeading']) : '';
476 $lock_error_message = !empty($attributes['lockErrorMessage']) ? sanitize_text_field($attributes['lockErrorMessage']) : '';
477 $footer_message = !empty($attributes['footerMessage']) ? sanitize_text_field($attributes['footerMessage']) : '';
478 $password_placeholder = !empty($attributes['passwordPlaceholder']) ? sanitize_text_field($attributes['passwordPlaceholder']) : '';
479 $button_text = !empty($attributes['submitButtonText']) ? sanitize_text_field($attributes['submitButtonText']) : '';
480 $unlocking_text = !empty($attributes['submitUnlockingText']) ? sanitize_text_field($attributes['submitUnlockingText']) : '';
481 $enable_footer_message = !empty($attributes['enableFooterMessage']) ? sanitize_text_field($attributes['enableFooterMessage']) : '';
482
483
484 // Set the encryption key and initialization vector (IV)
485 $key = self::get_hash();
486
487 $salt = wp_salt(32);
488 $wp_hash_key = hash('sha256', $salt . $pass_hash_key);
489 $iv = substr($wp_hash_key, 0, 16);
490
491
492 // Encrypt the plaintext using AES-128-CBC encryption
493 $cipher = openssl_encrypt($embedHtml, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
494
495 // Base64 encode the encrypted cipher
496 $encrypted_data = base64_encode($cipher);
497
498 update_post_meta(get_the_ID(), 'ep_base_' . $client_id, $encrypted_data);
499 update_post_meta(get_the_ID(), 'hash_key_' . $client_id, $wp_hash_key);
500
501 $lock_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g fill="#6354a5" class="color134563 svgShape"><path d="M46.3 28.7h-3v-6.4C43.3 16.1 38.2 11 32 11c-6.2 0-11.3 5.1-11.3 11.3v6.4h-3v-6.4C17.7 14.4 24.1 8 32 8s14.3 6.4 14.3 14.3v6.4" fill="#6354a5" class="color000000 svgShape"></path><path d="M44.8 55.9H19.2c-2.6 0-4.8-2.2-4.8-4.8V31.9c0-2.6 2.2-4.8 4.8-4.8h25.6c2.6 0 4.8 2.2 4.8 4.8v19.2c0 2.7-2.2 4.8-4.8 4.8zM19.2 30.3c-.9 0-1.6.7-1.6 1.6v19.2c0 .9.7 1.6 1.6 1.6h25.6c.9 0 1.6-.7 1.6-1.6V31.9c0-.9-.7-1.6-1.6-1.6H19.2z" fill="#6354a5" class="color000000 svgShape"></path><path d="M35.2 36.7c0 1.8-1.4 3.2-3.2 3.2s-3.2-1.4-3.2-3.2 1.4-3.2 3.2-3.2 3.2 1.5 3.2 3.2" fill="#6354a5" class="color000000 svgShape"></path><path d="M32.8 36.7h-1.6l-1.6 9.6h4.8l-1.6-9.6" fill="#6354a5" class="color000000 svgShape"></path></g></svg>';
502
503 echo '
504 <div class="password-form-container sd">
505 <h2>' . esc_html($lock_heading) . '</h2>
506 <p>' . esc_html($lock_subheading) . ' </p>
507 <form class="password-form" method="post" class="password-form" data-unlocking-text="' . esc_attr($unlocking_text) . '">
508
509 <div class="password-field">
510 <span class="lock-icon">' . $lock_icon . '</span>
511 <input type="password" name="pass_' . esc_attr($client_id) . '" placeholder="' . esc_attr($password_placeholder) . '" required>
512 </div>
513 <input type="hidden" name="ep_client_id" value="' . esc_attr($client_id) . '">
514 <input type="hidden" name="post_id" value="' . esc_attr(get_the_ID()) . '">
515
516 <input type="submit" name="password_submit" value="' . esc_attr($button_text) . '">
517 <div class="error-message hidden">' . esc_html($lock_error_message) . '</div>
518 </form>
519 ' . (!empty($enable_footer_message) ? '<p class="need-access-message">' . esc_html($footer_message) . '</p>' : '') . '
520 </div>
521 ';
522 }
523
524 // Check if the user has already entered the correct password
525 public static function is_password_correct($client_id)
526 {
527 if (isset($_COOKIE['password_correct_' . $client_id])) {
528 return $_COOKIE['password_correct_' . $client_id];
529 } else {
530 return false;
531 }
532 }
533
534 public static function customLogo($embedHTML, $atts)
535 {
536 $x = !empty($atts['logoX']) ? $atts['logoX'] : 0;
537 $y = !empty($atts['logoY']) ? $atts['logoY'] : 0;
538 $uniqid = !empty($atts['url']) ? '.ose-uid-' . md5($atts['url']) : '';
539
540 $brandUrl = !empty($atts['customlogoUrl']) ? $atts['customlogoUrl'] : '';
541 $opacity = !empty($atts['logoOpacity']) ? $atts['logoOpacity'] : '';
542
543 $cssClass = !empty($atts['url']) ? '.ose-uid-' . md5($atts['url']) : '.ose-youtube';
544
545
546
547 ob_start(); ?>
548 <style type="text/css">
549 <?php echo esc_html($cssClass); ?> {
550 position: relative;
551 }
552
553 <?php echo esc_html($cssClass); ?>.watermark {
554 border: 0;
555 position: absolute;
556 bottom: <?php echo esc_html($y); ?>%;
557 right: <?php echo esc_html($x); ?>%;
558 max-width: 150px;
559 max-height: 75px;
560 opacity: 0.25;
561 z-index: 5;
562 -o-transition: opacity 0.5s ease-in-out;
563 -moz-transition: opacity 0.5s ease-in-out;
564 -webkit-transition: opacity 0.5s ease-in-out;
565 transition: opacity 0.5s ease-in-out;
566 opacity: <?php echo esc_html($opacity); ?>;
567 }
568
569 <?php echo esc_html($cssClass); ?>.watermark:hover {
570 opacity: 1;
571 }
572 </style>
573 <?php
574
575
576 $style = ob_get_clean();
577
578 if (!class_exists('\simple_html_dom')) {
579 include_once EMBEDPRESS_PATH_CORE . 'simple_html_dom.php';
580 }
581
582 $cta = '';
583 $img = '';
584
585 if (!empty($atts['customlogo'])) {
586 $img = '<img src="' . esc_url($atts['customlogo']) . '"/>';
587
588 $imgDom = str_get_html($img);
589 $imgDom = $imgDom->find('img', 0);
590 $imgDom->setAttribute('class', 'watermark ep-custom-logo');
591 $imgDom->removeAttribute('style');
592 $imgDom->setAttribute('width', 'auto');
593 $imgDom->setAttribute('height', 'auto');
594 ob_start();
595 echo $imgDom;
596
597 $cta .= ob_get_clean();
598
599 $imgDom->clear();
600 unset($img, $imgDom);
601
602 if (!empty($brandUrl)) {
603 $cta = '<a href="' . esc_url($brandUrl) . '" target="_blank">' . $cta . '</a>';
604 }
605 $dom = str_get_html($embedHTML);
606
607 $wrapDiv = $dom->find($uniqid, 0);
608
609 if (!empty($wrapDiv) && is_object($wrapDiv)) {
610 $wrapDiv->innertext .= $cta;
611 }
612
613 ob_start();
614 echo $wrapDiv;
615
616 $markup = ob_get_clean();
617
618 $dom->clear();
619 unset($dom, $wrapDiv);
620
621 $embedHTML = $style . $markup;
622 }
623
624 return $embedHTML;
625 }
626
627
628 public static function embed_content_share($content_id = '', $attributes = [])
629 {
630 $share_position = !empty($attributes['sharePosition']) ? $attributes['sharePosition'] : 'right';
631 $custom_thumnail = !empty($attributes['customThumbnail']) ? $attributes['customThumbnail'] : '';
632 $custom_title = !empty($attributes['customTitle']) ? $attributes['customTitle'] : '';
633 $custom_description = !empty($attributes['customDescription']) ? $attributes['customDescription'] : '';
634
635 // Create a unique hash based on content attributes
636 $content_hash = md5($custom_thumnail . $custom_title . $custom_description . $content_id);
637
638 // Encode for URL usage
639 $custom_thumnail = urlencode($custom_thumnail);
640 $custom_title = urlencode($custom_title);
641 $custom_description = urlencode($custom_description);
642
643 // Get social share options with defaults
644 $facebook_enabled = isset($attributes['shareFacebook']) ? $attributes['shareFacebook'] !== false : true;
645 $twitter_enabled = isset($attributes['shareTwitter']) ? $attributes['shareTwitter'] !== false : true;
646 $pinterest_enabled = isset($attributes['sharePinterest']) ? $attributes['sharePinterest'] !== false : true;
647 $linkedin_enabled = isset($attributes['shareLinkedin']) ? $attributes['shareLinkedin'] !== false : true;
648 $style = isset($attributes['width']) ? 'style="max-width: ' . esc_attr($attributes['width']) . 'px;"' : '';
649
650
651 $page_url = urlencode(get_permalink() . '?hash=' . $content_id . '&unique=' . $content_hash);
652
653 $social_icons = '<div class="ep-social-share-wraper"' . $style . ' ><div class="ep-social-share share-position-' . esc_attr($share_position) . '">';
654
655
656 // Facebook
657 if ($facebook_enabled) {
658 $social_icons .= '<a href="https://www.facebook.com/sharer/sharer.php?u=' . $page_url . '" class="ep-social-icon facebook" target="_blank">
659 <svg width="64px" height="64px" fill="#000000" viewBox="0 -6 512 512" xmlns="http://www.w3.org/2000/svg">
660 <path d="M0 0h512v500H0z" fill="#475a96"/>
661 <path d="m375.72 112.55h-237.43c-8.137 0-14.73 6.594-14.73 14.73v237.43c0 8.135 6.594 14.73 14.73 14.73h127.83v-103.36h-34.781v-40.28h34.781v-29.705c0-34.473 21.055-53.244 51.807-53.244 14.73 0 27.391 1.097 31.08 1.587v36.026l-21.328 0.01c-16.725 0-19.963 7.947-19.963 19.609v25.717h39.887l-5.193 40.28h-34.693v103.36h68.012c8.135 0 14.73-6.596 14.73-14.73v-237.43c-1e-3 -8.137-6.596-14.73-14.731-14.73z" fill="#fff"/>
662 </svg>
663 </a>';
664 }
665
666 // Twitter
667 if ($twitter_enabled) {
668 $social_icons .= '<a href="https://twitter.com/intent/tweet?url=' . $page_url . '&text=' . $custom_title . '" class="ep-social-icon twitter" target="_blank">
669 <svg viewBox="0 0 24 24" aria-hidden="true" fill="#fff" class="r-4qtqp9 r-yyyyoo r-dnmrzs r-bnwqim r-lrvibr r-m6rgpd r-lrsllp r-1nao33i r-16y2uox r-8kz0gk"><g><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path></g></svg>
670 </a>';
671 }
672
673 // Pinterest
674 if ($pinterest_enabled) {
675 $social_icons .= '<a href="http://pinterest.com/pin/create/button/?url=' . $page_url . '&media=' . $custom_thumnail . '&description=' . $custom_description . '" class="ep-social-icon pinterest" target="_blank">
676
677 <svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" viewBox="-36.42015 -60.8 315.6413 364.8"><path d="M121.5 0C54.4 0 0 54.4 0 121.5 0 173 32 217 77.2 234.7c-1.1-9.6-2-24.4.4-34.9 2.2-9.5 14.2-60.4 14.2-60.4s-3.6-7.3-3.6-18c0-16.9 9.8-29.5 22-29.5 10.4 0 15.4 7.8 15.4 17.1 0 10.4-6.6 26-10.1 40.5-2.9 12.1 6.1 22 18 22 21.6 0 38.2-22.8 38.2-55.6 0-29.1-20.9-49.4-50.8-49.4-34.6 0-54.9 25.9-54.9 52.7 0 10.4 4 21.6 9 27.7 1 1.2 1.1 2.3.8 3.5-.9 3.8-3 12.1-3.4 13.8-.5 2.2-1.8 2.7-4.1 1.6-15.2-7.1-24.7-29.2-24.7-47.1 0-38.3 27.8-73.5 80.3-73.5 42.1 0 74.9 30 74.9 70.2 0 41.9-26.4 75.6-63 75.6-12.3 0-23.9-6.4-27.8-14 0 0-6.1 23.2-7.6 28.9-2.7 10.6-10.1 23.8-15.1 31.9 11.4 3.5 23.4 5.4 36 5.4 67.1 0 121.5-54.4 121.5-121.5C243 54.4 188.6 0 121.5 0z" fill="#fff"/></svg>
678
679 </a>';
680 }
681
682 // LinkedIn
683 if ($linkedin_enabled) {
684 $social_icons .= '<a href="https://www.linkedin.com/shareArticle?mini=true&url=' . $page_url . '" class="ep-social-icon linkedin" target="_blank">
685
686 <svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
687 viewBox="0 0 310 310" xml:space="preserve">
688 <g id="XMLID_801_">
689 <path id="XMLID_802_" d="M72.16,99.73H9.927c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5H72.16c2.762,0,5-2.238,5-5V104.73
690 C77.16,101.969,74.922,99.73,72.16,99.73z"/>
691 <path id="XMLID_803_" d="M41.066,0.341C18.422,0.341,0,18.743,0,41.362C0,63.991,18.422,82.4,41.066,82.4
692 c22.626,0,41.033-18.41,41.033-41.038C82.1,18.743,63.692,0.341,41.066,0.341z"/>
693 <path id="XMLID_804_" d="M230.454,94.761c-24.995,0-43.472,10.745-54.679,22.954V104.73c0-2.761-2.238-5-5-5h-59.599
694 c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5h62.097c2.762,0,5-2.238,5-5v-98.918c0-33.333,9.054-46.319,32.29-46.319
695 c25.306,0,27.317,20.818,27.317,48.034v97.204c0,2.762,2.238,5,5,5H305c2.762,0,5-2.238,5-5V194.995
696 C310,145.43,300.549,94.761,230.454,94.761z"/>
697 </g>
698 </svg>
699 </a>';
700 }
701
702 $social_icons .= '</div></div>';
703
704 return $social_icons;
705 }
706
707 public static function ep_get_elementor_widget_settings($page_settings = '', $id = '', $widgetType = '')
708 {
709 if (empty($page_settings)) {
710 return [];
711 }
712
713 // Handle both string and array cases for elementor data
714 if (is_string($page_settings)) {
715 $data = json_decode($page_settings, true);
716 } else {
717 // Data might already be an array in older versions
718 $data = $page_settings;
719 }
720 $element_setting = null;
721
722 Plugin::$instance->db->iterate_data($data, function ($element) use (&$element_setting, $widgetType, $id) {
723
724 if ($element['id'] == $id && $element['elType'] == 'widget' && $element['widgetType'] == $widgetType) {
725 $element_setting[] = $element['settings'];
726 }
727 });
728
729 return $element_setting;
730 }
731
732
733
734 public static function ep_get_popup_icon()
735 {
736 $svg = '<div class="ep-doc-popup-icon" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" xml:space="preserve"><path fill="#fff" d="M5 3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6l-2-2v8H5V5h8l-2-2H5zm9 0 2.7 2.7-7.5 7.5 1.7 1.7 7.5-7.5L21 10V3h-7z"/><path style="fill:none" d="M0 0h24v24H0z"/></svg></div>';
737
738 return $svg;
739 }
740 public static function ep_get_download_icon()
741 {
742 $svg = '<div class="ep-doc-download-icon" ><svg width="25" height="25" viewBox="0 0 0.6 0.6" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" fill-rule="evenodd" d="M.525.4A.025.025 0 0 1 .55.422v.053A.075.075 0 0 1 .479.55H.125A.075.075 0 0 1 .05.479V.425A.025.025 0 0 1 .1.422v.053A.025.025 0 0 0 .122.5h.353A.025.025 0 0 0 .5.478V.425A.025.025 0 0 1 .525.4ZM.3.05a.025.025 0 0 1 .025.025v.24L.357.283A.025.025 0 0 1 .39.281l.002.002a.025.025 0 0 1 .002.033L.392.318.317.393.316.394.314.395.311.397.308.398.305.399.301.4H.295L.292.399.289.398.287.397.285.395A.025.025 0 0 1 .283.393L.208.318A.025.025 0 0 1 .241.281l.002.002.032.032v-.24A.025.025 0 0 1 .3.05Z"/></svg></div>';
743
744 return $svg;
745 }
746
747 public static function ep_get_print_icon()
748 {
749 $svg = '<div class="ep-doc-print-icon" ><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24">
750 <path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z" fill="#fff"/>
751 </svg></div>';
752
753 return $svg;
754 }
755
756 public static function ep_get_fullscreen_icon()
757 {
758 $svg = '<div class="ep-doc-fullscreen-icon"><svg width="25" height="25" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
759 <path d="m3 15 .117.007a1 1 0 0 1 .876.876L4 16v4h4l.117.007a1 1 0 0 1 0 1.986L8 22H3l-.117-.007a1 1 0 0 1-.876-.876L2 21v-5l.007-.117a1 1 0 0 1 .876-.876L3 15Zm18 0a1 1 0 0 1 .993.883L22 16v5a1 1 0 0 1-.883.993L21 22h-5a1 1 0 0 1-.117-1.993L16 20h4v-4a1 1 0 0 1 .883-.993L21 15ZM8 2a1 1 0 0 1 .117 1.993L8 4H4v4a1 1 0 0 1-.883.993L3 9a1 1 0 0 1-.993-.883L2 8V3a1 1 0 0 1 .883-.993L3 2h5Zm13 0 .117.007a1 1 0 0 1 .876.876L22 3v5l-.007.117a1 1 0 0 1-.876.876L21 9l-.117-.007a1 1 0 0 1-.876-.876L20 8V4h-4l-.117-.007a1 1 0 0 1 0-1.986L16 2h5Z" fill="#fff"/>
760 </svg></div>';
761
762 return $svg;
763 }
764 public static function ep_get_minimize_icon()
765 {
766 $svg = '<div class="ep-doc-minimize-icon" style="display:none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" style="enable-background:new 0 0 385.331 385.331" xml:space="preserve" width="20" height="20"><path fill="#fff" d="M13.751 8.131h5.62c0.355 0 0.619 -0.28 0.619 -0.634 0 -0.355 -0.265 -0.615 -0.619 -0.614h-4.995V1.878c0 -0.355 -0.27 -0.624 -0.624 -0.624s-0.624 0.27 -0.624 0.624v5.62c0 0.002 0.001 0.003 0.001 0.004 0 0.002 -0.001 0.003 -0.001 0.005 0 0.348 0.276 0.625 0.624 0.624zM6.244 1.259c-0.354 0 -0.614 0.265 -0.614 0.619v4.995H0.624c-0.355 0 -0.624 0.27 -0.624 0.624 0 0.355 0.27 0.624 0.624 0.624h5.62c0.002 0 0.003 -0.001 0.004 -0.001 0.002 0 0.003 0.001 0.005 0.001 0.348 0 0.624 -0.276 0.624 -0.624V1.878c0 -0.354 -0.28 -0.619 -0.634 -0.619zm0.005 10.61H0.629c-0.355 0.001 -0.619 0.28 -0.619 0.634 0 0.355 0.265 0.615 0.619 0.614h4.995v5.005c0 0.355 0.27 0.624 0.624 0.624 0.355 0 0.624 -0.27 0.624 -0.624V12.502c0 -0.002 -0.001 -0.003 -0.001 -0.004 0 -0.002 0.001 -0.003 0.001 -0.005 0 -0.348 -0.276 -0.624 -0.624 -0.624zm13.127 0H13.756c-0.002 0 -0.003 0.001 -0.004 0.001 -0.002 0 -0.003 -0.001 -0.005 -0.001 -0.348 0 -0.624 0.276 -0.624 0.624v5.62c0 0.355 0.28 0.619 0.634 0.619 0.354 0.001 0.614 -0.265 0.614 -0.619v-4.995H19.376c0.355 0 0.624 -0.27 0.624 -0.624s-0.27 -0.624 -0.624 -0.625z"/><g/><g/><g/><g/><g/><g/></svg></div>';
767
768 return $svg;
769 }
770 public static function ep_get_draw_icon()
771 {
772 $svg = '<div class="ep-doc-draw-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m15 7.5 2.5 2.5m-10 10L19.25 8.25c0.69 -0.69 0.69 -1.81 0 -2.5v0c-0.69 -0.69 -1.81 -0.69 -2.5 0L5 17.5V20h2.5Zm0 0h8.379C17.05 20 18 19.05 18 17.879v0c0 -0.563 -0.224 -1.103 -0.621 -1.5L17 16M4.5 5c2 -2 5.5 -1 5.5 1 0 2.5 -6 2.5 -6 5 0 0.876 0.533 1.526 1.226 2" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
773
774 return $svg;
775 }
776
777 public static function get_insta_video_icon()
778 {
779 $svg = '<svg class="insta-video-icon" aria-label="Clip" class="x1lliihq x1n2onr6" color="#FFF" fill="#FFF" height="20" viewBox="1.111 1.111 24.444 24.447" width="20">
780 <path d="m14.248 1.111 3.304 5.558h-6.2L8.408 1.146c.229-.014.466-.024.713-.03l.379-.005Zm2.586 0h.331c3.4 0 4.964.838 6.267 2.097a6.674 6.674 0 0 1 1.773 3.133l.078.328H20.14l-3.307-5.558ZM6.093 1.53l2.74 5.139H1.382a6.678 6.678 0 0 1 4.38-5.033ZM16.91 15.79l-5.05-2.916a1.01 1.01 0 0 0-1.507.742l-.009.133v5.831a1.011 1.011 0 0 0 1.394.933l.121-.059 5.05-2.916a1.01 1.01 0 0 0 .111-1.674l-.111-.076-5.05-2.916ZM1.132 8.891h24.404l.017.4.003.21v7.666c0 3.401-.839 4.966-2.098 6.267-1.279 1.238-2.778 2.062-5.922 2.121l-.371.003H9.501c-3.4 0-4.963-.839-6.267-2.099-1.238-1.278-2.06-2.776-2.12-5.922l-.003-.37V9.501l.003-.21Z" fill-rule="evenodd" /></svg>';
781
782 return $svg;
783 }
784
785 public static function get_insta_image_carousel_icon()
786 {
787 $svg = '<svg aria-label="Carousel" class="x1lliihq x1n2onr6" color="#FFF" fill="#FFF" height="25" viewBox="0 0 43.636 43.636" width="25">
788 <path d="M31.636 27V10a4.695 4.695 0 0 0-4.727-4.727H10A4.695 4.695 0 0 0 5.273 10v17A4.695 4.695 0 0 0 10 31.727h17c2.545-.091 4.636-2.182 4.636-4.727zm4-13.364v14.636c0 4.091-3.364 7.455-7.455 7.455H13.545c-.545 0-.818.636-.455 1 .909 1 2.182 1.636 3.727 1.636h12.182a9.35 9.35 0 0 0 9.364-9.364V16.818a5.076 5.076 0 0 0-1.636-3.727c-.455-.364-1.091 0-1.091.545z" /></svg>';
789
790 return $svg;
791 }
792
793 public static function get_insta_image_icon()
794 {
795 $svg = '<svg width="22" height="22" viewBox="0 0 0.6 0.6" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M.175.05A.125.125 0 0 0 .05.175v.25A.125.125 0 0 0 .175.55h.25A.125.125 0 0 0 .55.425v-.25A.125.125 0 0 0 .425.05h-.25ZM.2.225a.025.025 0 1 1 .05 0 .025.025 0 0 1-.05 0ZM.225.15a.075.075 0 1 0 0 .15.075.075 0 0 0 0-.15Zm.138.205A.025.025 0 0 1 .398.351l.048.042A.025.025 0 1 0 .479.355L.43.312a.075.075 0 0 0-.107.011l-.04.05a.024.024 0 0 1-.032.005.074.074 0 0 0-.099.015L.118.432a.025.025 0 0 0 .038.033l.035-.04A.024.024 0 0 1 .223.42.074.074 0 0 0 .322.405l.04-.05Z" fill="#fff"/></svg>';
796
797 return $svg;
798 }
799
800 public static function get_insta_like_icon()
801 {
802 $svg = '<svg version="1.1" id="Uploaded to svgrepo.com" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 0.8 0.8" xml:space="preserve"><path d="M.225.25C.225.264.214.275.2.275S.175.264.175.25.186.225.2.225.225.236.225.25zM.75.3C.75.453.589.582.485.65a1.06 1.06 0 0 1-.073.044.025.025 0 0 1-.024 0A1.049 1.049 0 0 1 .315.65C.211.582.05.453.05.3a.2.2 0 0 1 .2-.2.199.199 0 0 1 .15.068A.199.199 0 0 1 .55.1a.2.2 0 0 1 .2.2zM.25.25a.05.05 0 1 0-.1 0 .05.05 0 0 0 .1 0z" style="fill:#fff"/></svg>';
803
804 return $svg;
805 }
806 public static function get_insta_comment_icon()
807 {
808 $svg = '<svg fill="#fff" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 2.5 2.5" xml:space="preserve"><path d="M2.374.446a.063.063 0 0 0-.061-.057H.991a.063.063 0 0 0-.063.057H.927v.328h.559c.029 0 .053.022.056.051h.001v.731h.275l.162.162a.063.063 0 0 0 .116-.035v-.127h.217a.063.063 0 0 0 .06-.051h.002V.446h-.001z"/><path d="M1.361.899H.18A.056.056 0 0 0 .125.95v.946h.001a.057.057 0 0 0 .054.045h.194v.113a.057.057 0 0 0 .104.032l.145-.145h.738c.027 0 .05-.02.056-.045h.001V.95h-.001a.056.056 0 0 0-.056-.051z"/></svg>';
809
810 return $svg;
811 }
812 public static function get_instagram_icon()
813 {
814 $svg = '<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xml:space="preserve" width="1285" height="400"><style>.st0{fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}</style><path class="st0" d="M14.375 19.375h-8.75c-2.75 0-5-2.25-5-5v-8.75c0-2.75 2.25-5 5-5h8.75c2.75 0 5 2.25 5 5v8.75c0 2.75-2.25 5-5 5z"/><path class="st0" d="M14.375 10A4.375 4.375 0 0 1 10 14.375 4.375 4.375 0 0 1 5.625 10a4.375 4.375 0 0 1 8.75 0zm1.25-5.625A.625.625 0 0 1 15 5a.625.625 0 0 1-.625-.625.625.625 0 0 1 1.25 0z"/></svg>';
815
816 return $svg;
817 }
818
819 public static function get_google_presentation_url($embedded_url)
820 {
821 $parsed_url = parse_url($embedded_url);
822 $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . $parsed_url['path'];
823 $base_url = strtok($base_url, '?');
824 $base_url = rtrim($base_url, '/');
825 return $base_url;
826 }
827
828 public static function check_media_format($url)
829 {
830 // Strip query/hash so signed streaming URLs (Mux, CloudFront) match.
831 $path = preg_replace('/[?#].*$/', '', (string) $url);
832 $pattern1 = '/\.(mp4|mov|avi|wmv|flv|mkv|webm|mpeg|mpg|m3u8|mpd)$/i';
833 $pattern2 = '/\.(mp3|wav|ogg|aac)$/i';
834
835 $isVideo = preg_match($pattern1, $path);
836 $isAudio = preg_match($pattern2, $path);
837
838 $is_self_hosted = false;
839 $format = '';
840
841 if (!empty($isVideo) || !empty($isAudio)) {
842 $is_self_hosted = true;
843 if (!empty($isVideo)) {
844 $format = 'video';
845 } else if (!empty($isAudio)) {
846 $format = 'audio';
847 }
848 }
849
850 if (!$is_self_hosted) {
851 return [
852 'selhosted' => false,
853 ];
854 }
855
856 return [
857 'selhosted' => true,
858 'format' => $format,
859 ];
860 }
861
862 // Ajax Methods get instagram feed data
863 public function loadmore_data_handler()
864 {
865 $connected_account_type = isset($_POST['connected_account_type']) ? sanitize_text_field($_POST['connected_account_type']) : 'personal';
866 $hashtag_id = isset($_POST['hashtag_id']) ? sanitize_text_field($_POST['hashtag_id']) : '';
867 $feed_type = isset($_POST['feed_type']) ? sanitize_text_field($_POST['feed_type']) : 'user_aacount_type';
868 $user_id = isset($_POST['user_id']) ? sanitize_text_field($_POST['user_id']) : '';
869 $loadmore_key = isset($_POST['loadmore_key']) ? sanitize_text_field($_POST['loadmore_key']) : '';
870 $nonce = isset($_POST['_nonce']) ? sanitize_text_field($_POST['_nonce']) : '';
871 $params = isset($_POST['params']) ? sanitize_text_field($_POST['params']) : '';
872 // $params = json_decode($params, true);
873 $params = stripslashes($params); // Remove extra backslashes
874 $params = json_decode($params, true);
875
876 // Verify nonce
877 if (!wp_verify_nonce($nonce, 'ep_nonce')) {
878 wp_send_json_error('Invalid nonce');
879 wp_die(); // Terminate script execution if nonce is invalid
880 }
881
882 $feeds_data = get_option('ep_instagram_feed_data');
883 $user_data = $feeds_data[$user_id]['feed_userinfo'];
884
885 if ($feed_type == 'hashtag_type') {
886 $feeds_data = get_option('ep_instagram_hashtag_feed');
887 }
888
889
890 $profile_picture_url = isset($user_data['profile_picture_url']) ? $user_data['profile_picture_url'] : '';
891
892 if ($feed_type === 'user_account_type' && isset($feeds_data[$loadmore_key]['feed_posts'])) {
893 $feed_posts = $feeds_data[$loadmore_key]['feed_posts'];
894 } else if ($feed_type === 'hashtag_type' && isset($feeds_data[$loadmore_key])) {
895 $feed_posts = $feeds_data[$loadmore_key];
896 } else {
897 $feed_posts = ['error'];
898 }
899
900
901 if (is_array($feed_posts) && count($feed_posts) > 0) {
902 $loaded_posts = isset($_POST['loaded_posts']) ? intval($_POST['loaded_posts']) : 0;
903 $posts_per_page = isset($_POST['posts_per_page']) ? intval($_POST['posts_per_page']) : 0;
904
905 $post_index = $loaded_posts + 1;
906 $start_index = $loaded_posts;
907
908 $next_posts = array_slice($feed_posts, $start_index, $posts_per_page);
909
910 ob_start();
911
912 if (is_array($next_posts) && count($next_posts) > 0) :
913 foreach ($next_posts as $post) :
914 $caption = !empty($post['caption']) ? $post['caption'] : '';
915 $media_type = !empty($post['media_type']) ? $post['media_type'] : '';
916 $media_url = !empty($post['media_url']) ? $post['media_url'] : '';
917 $permalink = !empty($post['permalink']) ? $post['permalink'] : '';
918 $timestamp = !empty($post['timestamp']) ? $post['timestamp'] : '';
919 $username = !empty($post['username']) ? $post['username'] : '';
920 $like_count = !empty($post['like_count']) ? $post['like_count'] : 0;
921 $comments_count = !empty($post['comments_count']) ? $post['comments_count'] : 0;
922
923 $post['profile_picture_url'] = $profile_picture_url;
924 $post['show_likes_count'] = isset($params['show_likes_count']) ? $params['show_likes_count'] : false;
925 $post['show_comments_count'] = isset($params['show_comments_count']) ? $params['show_comments_count'] : false;
926 $post['popup_follow_button'] = isset($params['popup_follow_button']) ? $params['popup_follow_button'] : true;
927 $post['popup_follow_button_text'] = isset($params['popup_follow_button_text']) ? $params['popup_follow_button_text'] : 'Follow';
928 ?>
929
930 <div class="insta-gallery-item cg-carousel__slide js-carousel__slide" data-insta-postid="<?php echo esc_attr($post['id']) ?>" data-postindex="<?php echo esc_attr($post_index); ?>" data-postdata="<?php echo htmlspecialchars(json_encode($post), ENT_QUOTES, 'UTF-8'); ?>" data-media-type="<?php echo esc_attr($media_type); ?>">
931 <?php
932
933 if (!empty($hashtag_id) && $media_type == 'CAROUSEL_ALBUM') {
934 if (isset($post['children']['data'][0]['media_url'])) {
935 $hashtag_media_url = $post['children']['data'][0]['media_url'];
936 $hashtag_media_type = $post['children']['data'][0]['media_type'];
937
938 if ($hashtag_media_type == 'VIDEO') {
939 echo '<video class="insta-gallery-image" src="' . esc_url($hashtag_media_url) . '"></video>';
940 } else {
941 echo ' <img class="insta-gallery-image" src="' . esc_url($hashtag_media_url) . '" alt="' . esc_attr('image') . '">';
942 }
943 }
944 } else {
945 if ($media_type == 'VIDEO') {
946 echo '<video class="insta-gallery-image" src="' . esc_url($media_url) . '"></video>';
947 } else {
948 echo ' <img class="insta-gallery-image" src="' . esc_url($media_url) . '" alt="' . esc_attr('image') . '">';
949 }
950 }
951 ?>
952
953 <div class="insta-gallery-item-type">
954 <div class="insta-gallery-item-type-icon">
955 <?php
956 if ($media_type == 'VIDEO') {
957 echo Helper::get_insta_video_icon();
958 } else if ($media_type == 'CAROUSEL_ALBUM') {
959 echo Helper::get_insta_image_carousel_icon();
960 } else {
961 echo Helper::get_insta_image_icon();
962 }
963 ?>
964 </div>
965 </div>
966 <div class="insta-gallery-item-info">
967 <?php if (apply_filters('embedpress/is_allow_rander', false)): ?>
968 <div class="insta-item-reaction-count">
969 <div class="insta-gallery-item-likes">
970 <?php echo Helper::get_insta_like_icon();
971 echo esc_html($like_count); ?>
972 </div>
973 <div class="insta-gallery-item-comments">
974 <?php echo Helper::get_insta_comment_icon();
975 echo esc_html($comments_count); ?>
976 </div>
977 </div>
978 <?php else : ?>
979 <div class="insta-gallery-item-permalink">
980 <?php echo Helper::get_instagram_icon(); ?>
981 </div>
982 <?php endif; ?>
983 </div>
984 </div>
985
986 <?php $post_index++;
987 endforeach;
988 endif;
989
990 $feed_item = ob_get_clean();
991
992 $next_start_index = $start_index + count($next_posts);
993
994 wp_send_json(array(
995 'html' => $feed_item,
996 'next_post_index' => $next_start_index,
997 'total_feed_posts' => count($feed_posts)
998 ));
999 } else {
1000 wp_send_json('');
1001 }
1002 }
1003
1004 public static function getCalendlyUuid($url)
1005 {
1006 $pattern = '/\/([0-9a-fA-F-]+)$/';
1007 if (preg_match($pattern, $url, $matches)) {
1008 $uuid = $matches[1];
1009 return $uuid;
1010 }
1011 return '';
1012 }
1013
1014 public static function getCalendlyUserInfo($access_token)
1015 {
1016 $transient_name = 'calendly_user_info_' . $access_token;
1017 $user_info = get_transient($transient_name);
1018 if (false === $user_info) {
1019 $user_endpoint = 'https://api.calendly.com/users/me';
1020 $headers = array(
1021 'Authorization' => "Bearer $access_token",
1022 'Content-Type' => 'application/json',
1023 );
1024 $args = array(
1025 'headers' => $headers,
1026 );
1027 $response = wp_remote_get($user_endpoint, $args);
1028 if (!is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response)) {
1029 $user_info = wp_remote_retrieve_body($response);
1030 set_transient($transient_name, $user_info, 3600);
1031 } else {
1032 return false;
1033 }
1034 }
1035
1036 return $user_info;
1037 }
1038
1039
1040
1041 public static function getCalaendlyEventTypes($user_uri, $access_token)
1042 {
1043 // Attempt to retrieve the data from the transient
1044 $events_list = get_transient('calendly_events_list_' . md5($access_token));
1045
1046 if (false === $events_list) {
1047 // If the data is not in the transient, fetch it from the API
1048 $events_endpoint = "https://api.calendly.com/event_types?user=$user_uri";
1049
1050 $headers = array(
1051 'Authorization' => "Bearer $access_token",
1052 'Content-Type' => 'application/json',
1053 );
1054
1055 $args = array(
1056 'headers' => $headers,
1057 );
1058
1059 $response = wp_remote_get($events_endpoint, $args);
1060
1061 if (!is_wp_error($response)) {
1062 $body = wp_remote_retrieve_body($response);
1063 $events_list = json_decode($body, true);
1064
1065 // Store the data in a transient for a specified time (e.g., 1 hour)
1066 set_transient('calendly_events_list', $events_list, HOUR_IN_SECONDS);
1067
1068 return $events_list;
1069 }
1070 }
1071
1072 return $events_list;
1073 }
1074
1075 public static function getListEventInvitee($uuid, $access_token)
1076 {
1077 // Attempt to retrieve the data from the transient
1078 $invitee_list = get_transient('calendly_invitee_list_' . md5($access_token));
1079
1080 if (false === $invitee_list) {
1081 // If the data is not in the transient, fetch it from the API
1082 $events_endpoint = "https://api.calendly.com/scheduled_events/$uuid/invitees";
1083
1084 $headers = array(
1085 'Authorization' => "Bearer $access_token",
1086 'Content-Type' => 'application/json',
1087 );
1088
1089 $args = array(
1090 'headers' => $headers,
1091 );
1092
1093 $response = wp_remote_get($events_endpoint, $args);
1094
1095 if (!is_wp_error($response)) {
1096 $body = wp_remote_retrieve_body($response);
1097 $invitee_list = json_decode($body, true);
1098
1099 // Store the data in a transient for a specified time (e.g., 1 hour)
1100 set_transient('calendly_invitee_list', $invitee_list, HOUR_IN_SECONDS);
1101
1102 return $invitee_list;
1103 }
1104 }
1105
1106 return $invitee_list;
1107 }
1108
1109 public static function getCalaendlyScheduledEvents($user_uri, $access_token)
1110 {
1111 // Attempt to retrieve the data from the transient
1112 $events_list = get_transient('calendly_events_list_' . md5($access_token));
1113
1114 if (false === $events_list) {
1115 // If the data is not in the transient, fetch it from the API
1116 $events_endpoint = "https://api.calendly.com/scheduled_events?user=$user_uri";
1117
1118 $headers = array(
1119 'Authorization' => "Bearer $access_token",
1120 'Content-Type' => 'application/json',
1121 );
1122
1123 $args = array(
1124 'headers' => $headers,
1125 );
1126
1127 $response = wp_remote_get($events_endpoint, $args);
1128
1129 if (!is_wp_error($response)) {
1130 $body = wp_remote_retrieve_body($response);
1131 $events_list = json_decode($body, true);
1132
1133 // Store the data in a transient for a specified time (e.g., 1 hour)
1134 set_transient('calendly_events_list', $events_list, HOUR_IN_SECONDS);
1135
1136 return $events_list;
1137 }
1138 }
1139
1140 return $events_list;
1141 }
1142
1143
1144 public static function parseDuration($durationString)
1145 {
1146 list($minutes, $seconds) = explode(':', $durationString);
1147 return intval($minutes) * 60 + intval($seconds);
1148 }
1149
1150
1151
1152
1153 public static function is_pro_active()
1154 {
1155 if (defined('EMBEDPRESS_SL_ITEM_SLUG')) {
1156 return true;
1157 }
1158 return false;
1159 }
1160
1161 /**
1162 * Check if pro features should be enabled based on license status
1163 *
1164 * @return bool True if pro features should be enabled, false otherwise
1165 */
1166 public static function is_pro_features_enabled()
1167 {
1168 // First check if pro plugin is active
1169 if (!self::is_pro_active()) {
1170 return false;
1171 }
1172
1173 // Get license status
1174 $license_status = get_option('embedpress_pro_software__license_status', '');
1175
1176 // Pro features are enabled only if license is valid
1177 return $license_status === 'valid';
1178 }
1179
1180 /**
1181 * Get detailed license information
1182 *
1183 * @return array License information including status, key, and data
1184 */
1185 public static function get_license_info()
1186 {
1187 $is_pro_active = self::is_pro_active();
1188 $license_status = '';
1189 $license_key = '';
1190 $license_data = [];
1191 $is_features_enabled = false;
1192
1193 if ($is_pro_active) {
1194 $license_status = get_option('embedpress_pro_software__license_status', '');
1195 $license_data_raw = get_transient('embedpress_pro_software__license_data');
1196
1197 if ($license_data_raw) {
1198 $license_data = json_decode(json_encode($license_data_raw), true);
1199 }
1200
1201 // The license STATUS is a persistent option, but the license_data
1202 // TRANSIENT (which carries the key) expires — so reading the key
1203 // only from the transient leaves an "Active" license with an empty
1204 // key field once it lapses. The Pro LicenseManager also persists the
1205 // full key in the `embedpress_pro_software__license` option, so use
1206 // that as the authoritative source; fall back to the transient's
1207 // (already-masked) key only if the option is somehow empty.
1208 $license_key = get_option('embedpress_pro_software__license', '');
1209 if ($license_key === '' && isset($license_data['license_key'])) {
1210 $license_key = $license_data['license_key'];
1211 }
1212
1213 $is_features_enabled = ($license_status === 'valid');
1214 }
1215
1216 return [
1217 'is_pro_active' => $is_pro_active,
1218 'license_status' => $license_status,
1219 'license_key' => $license_key,
1220 'license_data' => $license_data,
1221 'is_features_enabled' => $is_features_enabled,
1222 'status_message' => self::get_license_status_message($is_pro_active, $license_status)
1223 ];
1224 }
1225
1226 /**
1227 * Mask a license key for display, keeping only the head and tail visible.
1228 *
1229 * The full key must never render into the DOM of the settings page (it sits
1230 * in a disabled input, readable via view-source / dev-tools). This keeps the
1231 * real key's first 4 and last 5 characters and replaces every character in
1232 * between with an asterisk — so the mask length matches the true key length
1233 * (e.g. a 29-char key → "2343********************32432"). Safe to echo into
1234 * the visible input's value.
1235 *
1236 * Short keys (<= head+tail chars) are fully masked so nothing meaningful
1237 * leaks. Empty input returns empty.
1238 *
1239 * @param string $key The raw license key.
1240 * @param int $head Visible leading chars. Default 4.
1241 * @param int $tail Visible trailing chars. Default 5.
1242 * @return string Masked key, or '' when $key is empty.
1243 */
1244 public static function mask_license_key($key, $head = 4, $tail = 5)
1245 {
1246 $key = (string) $key;
1247 $len = strlen($key);
1248
1249 if ($len === 0) {
1250 return '';
1251 }
1252
1253 // Too short to reveal head+tail without overlap — mask entirely.
1254 if ($len <= ($head + $tail)) {
1255 return str_repeat('*', $len);
1256 }
1257
1258 // Star out the real middle so the masked length equals the key length.
1259 return substr($key, 0, $head) . str_repeat('*', $len - $head - $tail) . substr($key, -$tail);
1260 }
1261
1262 /**
1263 * Display license warning notice for pro features
1264 *
1265 * @param string $context Context where the notice is displayed (e.g., 'analytics', 'branding')
1266 * @return string HTML for the license warning notice
1267 */
1268 public static function get_pro_feature_notice($context = '')
1269 {
1270 $license_info = self::get_license_info();
1271
1272 if (!$license_info['is_pro_active']) {
1273 return '<div class="embedpress-pro-notice embedpress-notice-warning">
1274 <p><strong>' . __('EmbedPress Pro Required', 'embedpress') . '</strong></p>
1275 <p>' . __('This feature requires EmbedPress Pro. Please install and activate EmbedPress Pro to access this feature.', 'embedpress') . '</p>
1276 </div>';
1277 }
1278
1279 if (!$license_info['is_features_enabled']) {
1280 $message = $license_info['status_message'];
1281 $action_text = '';
1282 $action_link = admin_url('admin.php?page=embedpress&page_type=license');
1283
1284 switch ($license_info['license_status']) {
1285 case 'expired':
1286 $action_text = __('Renew License', 'embedpress');
1287 break;
1288 case 'invalid':
1289 case 'site_inactive':
1290 case 'disabled':
1291 case 'revoked':
1292 $action_text = __('Activate License', 'embedpress');
1293 break;
1294 default:
1295 $action_text = __('Activate License', 'embedpress');
1296 break;
1297 }
1298
1299 return '<div class="embedpress-pro-notice embedpress-notice-error">
1300 <p><strong>' . __('Pro Features Disabled', 'embedpress') . '</strong></p>
1301 <p>' . esc_html($message) . '</p>
1302 <p><a href="' . esc_url($action_link) . '" class="button button-primary">' . esc_html($action_text) . '</a></p>
1303 </div>';
1304 }
1305
1306 return '';
1307 }
1308
1309 /**
1310 * Initialize license checking hooks
1311 */
1312 public static function init_license_hooks()
1313 {
1314 // Add filter to disable pro features when license is not valid
1315 add_filter('embedpress_pro_features_enabled', [__CLASS__, 'is_pro_features_enabled']);
1316
1317 // Add action to show license notices in admin
1318 add_action('admin_notices', [__CLASS__, 'show_license_admin_notices']);
1319
1320 // Add styles for license notices
1321 add_action('admin_head', [__CLASS__, 'add_license_notice_styles']);
1322 }
1323
1324 /**
1325 * Show license admin notices
1326 */
1327 public static function show_license_admin_notices()
1328 {
1329 // Only show on EmbedPress admin pages
1330 if (!isset($_GET['page']) || $_GET['page'] !== 'embedpress') {
1331 return;
1332 }
1333
1334 $license_info = self::get_license_info();
1335
1336 if ($license_info['is_pro_active'] && !$license_info['is_features_enabled']) {
1337 $message = $license_info['status_message'];
1338 $action_link = admin_url('admin.php?page=embedpress&page_type=license');
1339 $action_text = $license_info['license_status'] === 'expired' ? __('Renew License', 'embedpress') : __('Activate License', 'embedpress');
1340
1341 echo '<div class="notice notice-warning is-dismissible">
1342 <p><strong>' . __('EmbedPress Pro Features Disabled', 'embedpress') . '</strong></p>
1343 <p>' . esc_html($message) . '</p>
1344 <p><a href="' . esc_url($action_link) . '" class="button button-primary">' . esc_html($action_text) . '</a></p>
1345 </div>';
1346 }
1347 }
1348
1349 /**
1350 * Add CSS styles for license notices
1351 */
1352 public static function add_license_notice_styles()
1353 {
1354 echo '<style>
1355 .embedpress-pro-notice {
1356 padding: 15px;
1357 margin: 15px 0;
1358 border-left: 4px solid #dc3545;
1359 background: #fff;
1360 box-shadow: 0 1px 1px rgba(0,0,0,.04);
1361 }
1362 .embedpress-pro-notice.embedpress-notice-warning {
1363 border-left-color: #ffb900;
1364 }
1365 .embedpress-pro-notice.embedpress-notice-error {
1366 border-left-color: #dc3545;
1367 }
1368 .embedpress-pro-notice p {
1369 margin: 0.5em 0;
1370 }
1371 .embedpress-pro-notice strong {
1372 color: #23282d;
1373 }
1374 .embedpress-text-error {
1375 color: #dc3545 !important;
1376 }
1377 .embedpress-text-warning {
1378 color: #ffb900 !important;
1379 }
1380 </style>';
1381 }
1382
1383 /**
1384 * Get user-friendly license status message
1385 *
1386 * @param bool $is_pro_active Whether pro plugin is active
1387 * @param string $license_status Current license status
1388 * @return string User-friendly status message
1389 */
1390 public static function get_license_status_message($is_pro_active, $license_status)
1391 {
1392 if (!$is_pro_active) {
1393 return __('EmbedPress Pro is not installed or activated.', 'embedpress');
1394 }
1395
1396 switch ($license_status) {
1397 case 'valid':
1398 return __('Your license is active and valid.', 'embedpress');
1399 case 'expired':
1400 return __('Your license has expired. Please renew to continue receiving updates and support.', 'embedpress');
1401 case 'invalid':
1402 case 'site_inactive':
1403 return __('Your license is not active for this URL. Please activate your license.', 'embedpress');
1404 case 'disabled':
1405 case 'revoked':
1406 return __('Your license key has been disabled or revoked.', 'embedpress');
1407 case 'missing':
1408 return __('License key is missing. Please enter your license key.', 'embedpress');
1409 case 'http_error':
1410 return __('Unable to verify license due to connection issues. Please try again later.', 'embedpress');
1411 case '':
1412 case false:
1413 default:
1414 return __('Please activate your license key to enable EmbedPress Pro features.', 'embedpress');
1415 }
1416 }
1417
1418
1419 public static function getInstagramUserInfo($accessToken, $accountType, $userId, $is_sync = false)
1420 {
1421 if ($is_sync) {
1422 // If $is_sync is true, don't use transient
1423 $use_transient = false;
1424 } else {
1425 // If $is_sync is false, use transient
1426 $transient_key = 'instagram_user_info_' . $userId;
1427 $use_transient = true;
1428 }
1429
1430 if ($use_transient && false !== ($userInfo = get_transient($transient_key))) {
1431 // If transient exists, return cached user info
1432 return $userInfo;
1433 }
1434
1435 if (strtolower($accountType) === 'personal') {
1436 $api_url = 'https://graph.instagram.com/v24.0/me?fields=biography,id,username,website,followers_count,media_count,profile_picture_url,name&access_token=' . $accessToken;
1437 } else {
1438 $api_url = 'https://graph.facebook.com/' . $userId . '?fields=biography,id,username,website,followers_count,media_count,profile_picture_url,name&access_token=' . $accessToken;
1439 }
1440
1441 $connected_account_type = $accountType;
1442
1443 $userInfoResponse = wp_remote_get($api_url);
1444
1445 if (is_wp_error($userInfoResponse)) {
1446 echo 'Error: Unable to retrieve Instagram user information.';
1447 } else {
1448 $userInfoBody = wp_remote_retrieve_body($userInfoResponse);
1449 $userInfo = json_decode($userInfoBody, true);
1450
1451 $userInfo['connected_account_type'] = $connected_account_type;
1452 $userInfo['access_token'] = $accessToken;
1453
1454
1455 if (!isset($userInfo['profile_picture_url'])) {
1456 $userInfo['profile_picture_url'] = '';
1457 }
1458
1459 // If not using transient, cache the user info for an hour
1460 if ($use_transient) {
1461 set_transient($transient_key, $userInfo, HOUR_IN_SECONDS);
1462 }
1463
1464 return $userInfo;
1465 }
1466 }
1467
1468
1469 // Get Instagram posts, videos, reels
1470 public static function getInstagramPosts($access_token, $account_type, $userId, $limit = 100, $is_sync = false)
1471 {
1472 if ($is_sync) {
1473 // If $is_sync is true, don't use transient
1474 $use_transient = false;
1475 } else {
1476 // If $is_sync is false, use transient
1477 $transient_key = 'instagram_posts_' . $userId;
1478 $use_transient = true;
1479 }
1480
1481 if ($use_transient && false !== ($posts = get_transient($transient_key))) {
1482 // If transient exists, return cached posts
1483 return $posts;
1484 }
1485
1486 if (strtolower($account_type) === 'personal') {
1487 $api_url = 'https://graph.instagram.com/v24.0/me/media?fields=id,caption,media_type,media_url,children{media_url,id,media_type},permalink,timestamp,username,thumbnail_url,comments_count,like_count&limit=' . $limit . '&access_token=' . $access_token;
1488 } else {
1489 $api_url = 'https://graph.facebook.com/v17.0/' . $userId . '/media?fields=media_url,media_product_type,thumbnail_url,caption,id,media_type,timestamp,username,comments_count,like_count,permalink,children%7Bmedia_url,id,media_type,timestamp,permalink,thumbnail_url%7D&limit=' . $limit . '&access_token=' . $access_token;
1490 }
1491
1492 $postsResponse = wp_remote_get($api_url);
1493
1494 if (is_wp_error($postsResponse)) {
1495 echo 'Error: Unable to retrieve Instagram posts.';
1496 } else {
1497 $postsBody = wp_remote_retrieve_body($postsResponse);
1498 $posts = json_decode($postsBody, true);
1499
1500 if (empty($posts['data'])) {
1501 return 'Please add Instagram Access Token';
1502 }
1503
1504 // If not using transient, cache the posts for an hour
1505 if ($use_transient) {
1506 set_transient($transient_key, $posts['data'], HOUR_IN_SECONDS);
1507 }
1508
1509 return $posts['data'];
1510 }
1511 }
1512
1513 public static function get_enable_settings_data_for_scripts($settings)
1514 {
1515 $settings_data = [
1516 'enabled_ads' => isset($settings['adManager']) && $settings['adManager'] === 'yes' ? 'yes' : '',
1517
1518 'enabled_custom_player' => isset($settings['emberpress_custom_player']) && $settings['emberpress_custom_player'] === 'yes' ? 'yes' : '',
1519
1520 'enabled_instafeed' => isset($settings['embedpress_pro_embeded_source']) && $settings['embedpress_pro_embeded_source'] === 'instafeed' ? 'yes' : '',
1521
1522 'enabled_docs_custom_viewer' => isset($settings['embedpress_document_viewer']) && $settings['embedpress_document_viewer'] === 'custom' ? 'yes' : '',
1523 ];
1524
1525 update_option('enabled_elementor_scripts', $settings_data);
1526 }
1527
1528 public static function get_options_value($key)
1529 {
1530 $g_settings = get_option(EMBEDPRESS_PLG_NAME);
1531
1532 if (isset($g_settings['enableEmbedResizeWidth']) && $g_settings['enableEmbedResizeWidth'] == 1) {
1533 $g_settings['enableEmbedResizeWidth'] = 600;
1534 update_option(EMBEDPRESS_PLG_NAME, $g_settings);
1535 }
1536 if (isset($g_settings['enableEmbedResizeHeight']) && $g_settings['enableEmbedResizeHeight'] == 1) {
1537 $g_settings['enableEmbedResizeHeight'] = 600;
1538 update_option(EMBEDPRESS_PLG_NAME, $g_settings);
1539 }
1540
1541 if (isset($g_settings[$key])) {
1542 return $g_settings[$key];
1543 }
1544
1545 return '';
1546 }
1547
1548
1549
1550 public static function get_branding_value($key, $provider)
1551 {
1552 $settings = get_option(EMBEDPRESS_PLG_NAME . ':' . $provider, []);
1553
1554 // Check if provider has custom branding enabled and the specific key set
1555 if (isset($settings['branding']) && $settings['branding'] === 'yes') {
1556 // If provider has custom logo, use it
1557 if (isset($settings[$key]) && !empty($settings[$key])) {
1558 return $settings[$key];
1559 }
1560
1561 // If branding is enabled but no custom logo, use global brand as fallback
1562 if ($key === 'logo_url') {
1563 $global_logo = self::get_global_brand_logo_url();
1564 if (!empty($global_logo)) {
1565 return $global_logo;
1566 }
1567 }
1568 }
1569
1570 return '';
1571 }
1572
1573 /**
1574 * Get global brand logo URL
1575 *
1576 * @return string
1577 */
1578 public static function get_global_brand_logo_url()
1579 {
1580 $global_brand_settings = get_option(EMBEDPRESS_PLG_NAME . ':global_brand', []);
1581 return isset($global_brand_settings['logo_url']) ? $global_brand_settings['logo_url'] : '';
1582 }
1583
1584 /**
1585 * Get global brand logo ID
1586 *
1587 * @return int
1588 */
1589 public static function get_global_brand_logo_id()
1590 {
1591 $global_brand_settings = get_option(EMBEDPRESS_PLG_NAME . ':global_brand', []);
1592 return isset($global_brand_settings['logo_id']) ? intval($global_brand_settings['logo_id']) : 0;
1593 }
1594
1595
1596 public static function format_number($number)
1597 {
1598 if ($number >= 1000000000) {
1599 return number_format($number / 1000000000, 1) . 'b';
1600 } elseif ($number >= 1000000) {
1601 return number_format($number / 1000000, 1) . 'm';
1602 } elseif ($number >= 1000) {
1603 return number_format($number / 1000, 1) . 'k';
1604 } else {
1605 return $number;
1606 }
1607 }
1608
1609 public static function get_id($item)
1610 {
1611 $vid = isset($item->snippet->resourceId->videoId) ? $item->snippet->resourceId->videoId : null;
1612 $vid = $vid ? $vid : (isset($item->id->videoId) ? $item->id->videoId : null);
1613 $vid = $vid ? $vid : (isset($item->id) ? $item->id : null);
1614 return $vid;
1615 }
1616
1617 public static function clean_api_error($raw_message)
1618 {
1619 return htmlspecialchars(strip_tags(preg_replace('@&key=[^& ]+@i', '&key=*******', $raw_message)));
1620 }
1621
1622 public static function clean_api_error_html($raw_message)
1623 {
1624 $clean_html = '';
1625 if ((defined('REST_REQUEST') && REST_REQUEST) || current_user_can('manage_options')) {
1626 $clean_html = '<div>' . __('EmbedPress: ', 'embedpress') . self::clean_api_error($raw_message) . '</div>';
1627 }
1628 return $clean_html;
1629 }
1630
1631 public static function get_thumbnail_url($item, $quality, $privacyStatus)
1632 {
1633 $url = "";
1634 if ($privacyStatus == 'private') {
1635 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/private.png';
1636 } elseif (isset($item->snippet->thumbnails->{$quality}->url)) {
1637 $url = $item->snippet->thumbnails->{$quality}->url;
1638 } elseif (isset($item->snippet->thumbnails->medium->url)) {
1639 $url = $item->snippet->thumbnails->medium->url;
1640 } elseif (isset($item->snippet->thumbnails->default->url)) {
1641 $url = $item->snippet->thumbnails->default->url;
1642 } elseif (isset($item->snippet->thumbnails->high->url)) {
1643 $url = $item->snippet->thumbnails->high->url;
1644 } else {
1645 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/deleted-video-thumb.png';
1646 }
1647 return $url;
1648 }
1649
1650 public static function compare_vid_date($a, $b)
1651 {
1652 $dateA = strtotime($a->snippet->publishedAt);
1653 $dateB = strtotime($b->snippet->publishedAt);
1654
1655 // Sort in descending order (newest first)
1656 return $dateB - $dateA;
1657 }
1658
1659
1660 public static function get_youtube_video_data($api_key, $video_id)
1661 {
1662 // Set a unique transient name based on the video ID
1663 $transient_name = 'youtube_video_data_' . $video_id;
1664
1665 // Try to get data from the transient cache
1666 $video_data = get_transient($transient_name);
1667
1668 // If no cached data, fetch from the API
1669 if ($video_data === false) {
1670 // YouTube Data API URL
1671 $url = "https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id={$video_id}&key={$api_key}";
1672
1673 // Fetch the data from the API
1674 $response = wp_remote_get($url);
1675
1676 if (is_wp_error($response)) {
1677 return false; // Return false if there is an error
1678 }
1679
1680 $body = wp_remote_retrieve_body($response);
1681 $video_data = json_decode($body, true);
1682
1683 if (isset($video_data['items'][0])) {
1684 $video_data = $video_data['items'][0];
1685
1686 // Cache the data in a transient for 12 hours
1687 set_transient($transient_name, $video_data, 24 * HOUR_IN_SECONDS);
1688 } else {
1689 return false; // Return false if no data found
1690 }
1691 }
1692
1693 return $video_data;
1694 }
1695
1696 public static function trimTitle($title, $wordCount)
1697 {
1698 $words = explode(' ', $title);
1699
1700 if (count($words) <= $wordCount) {
1701 return $title; // No trimming needed
1702 }
1703
1704 $trimmedWords = array_slice($words, 0, $wordCount);
1705
1706 return implode(' ', $trimmedWords) . ' ...';
1707 }
1708
1709 public static function timeAgo($datetime)
1710 {
1711 $now = new \DateTime();
1712 $date = new \DateTime($datetime);
1713 $interval = $now->diff($date);
1714
1715 if ($interval->y > 0) {
1716 return $interval->y . ' year' . ($interval->y > 1 ? 's' : '') . ' ago';
1717 } elseif ($interval->m > 0) {
1718 return $interval->m . ' month' . ($interval->m > 1 ? 's' : '') . ' ago';
1719 } elseif ($interval->d > 0) {
1720 return $interval->d . ' day' . ($interval->d > 1 ? 's' : '') . ' ago';
1721 } elseif ($interval->h > 0) {
1722 return $interval->h . ' hour' . ($interval->h > 1 ? 's' : '') . ' ago';
1723 } elseif ($interval->i > 0) {
1724 return $interval->i . ' minute' . ($interval->i > 1 ? 's' : '') . ' ago';
1725 } else {
1726 return 'just now';
1727 }
1728 }
1729
1730 public static function removeQuote($attributes)
1731 {
1732 $parsedAttributes = [];
1733
1734 $regex = '/^on.*/i';
1735
1736 foreach ($attributes as $key => $value) {
1737 if (is_string($value)) {
1738 $cleanValue = str_replace(['"', "'"], '', $value);
1739 } else {
1740 $cleanValue = $value;
1741 }
1742
1743 if (!preg_match($regex, $key)) {
1744 $parsedAttributes[$key] = $cleanValue;
1745 }
1746 }
1747
1748 return $parsedAttributes;
1749 }
1750
1751 public static function getBooleanParam($param, $default = false)
1752 {
1753 if (in_array($param, [true, 'true', 'yes', 1, '1'], true)) {
1754 return true;
1755 }
1756 if (in_array($param, [false, 'false', 'no', 0, '0'], true)) {
1757 return false;
1758 }
1759 return (bool) $default;
1760 }
1761
1762 public static function has_allowed_roles($allowed_roles = [])
1763 {
1764
1765 if (empty($allowed_roles) || (count($allowed_roles) == 1 && empty($allowed_roles[0]))) {
1766 return true;
1767 }
1768 $current_user = wp_get_current_user();
1769 $user_roles = $current_user->roles;
1770
1771 return !empty(array_intersect($user_roles, $allowed_roles));
1772 }
1773
1774 public static function get_elementor_global_color($settings, $key)
1775 {
1776
1777 $global_color = $settings[$key];
1778
1779 if (isset($settings['__globals__'][$key])) {
1780 $color_setting = $settings['__globals__'][$key];
1781
1782 if (strpos($color_setting, 'globals/colors?id=') === 0) {
1783 // It's a global color reference
1784 $global_id = str_replace('globals/colors?id=', '', $color_setting);
1785
1786 $kit = Plugin::$instance->kits_manager->get_current_settings();
1787
1788 $system_colors = isset($kit['system_colors']) ? $kit['system_colors'] : [];
1789 $custom_colors = isset($kit['custom_colors']) ? $kit['custom_colors'] : [];
1790 $global_colors = array_merge($system_colors, $custom_colors);
1791
1792 foreach ($global_colors as $color) {
1793
1794 if ($color['_id'] === $global_id) {
1795 $global_color = $color['color']; // Found a match, set the color
1796 break;
1797 }
1798 }
1799 }
1800 }
1801 return $global_color;
1802 }
1803
1804 public static function get_provider_name($source_url)
1805 {
1806 if (self::is_youtube_channel($source_url)) {
1807 $source_name = 'YoutubeChannel';
1808 } else if (self::is_youtube($source_url)) {
1809 $source_name = 'Youtube';
1810 } else if (!empty(self::is_file_url($source_url))) {
1811 $source_name = 'document_' . self::get_extension_from_file_url($source_url);
1812 } else if (self::is_opensea($source_url)) {
1813 $source_name = 'OpenSea';
1814 } else if (self::is_instagram_feed($source_url)) {
1815 $source_name = 'InstagramFeed';
1816 }else {
1817 Shortcode::get_embera_instance();
1818 $collectios = Shortcode::get_collection();
1819 $provider = $collectios->findProviders($source_url);
1820
1821 if (!empty($provider[$source_url])) {
1822 $source_name = $provider[$source_url]->getProviderName();
1823 } else {
1824 $host = parse_url($source_url, PHP_URL_HOST);
1825 if ($host) {
1826 $parts = explode('.', $host);
1827 if (count($parts) > 1) {
1828 $source_name = $parts[1];
1829 } else {
1830 // Handle the case where the host doesn't have at least two parts
1831 $source_name = $host;
1832 }
1833 } else {
1834 // Handle the case where parse_url fails
1835 $source_name = '';
1836 }
1837 }
1838 }
1839
1840 return $source_name;
1841 }
1842
1843 public static function get_user_roles()
1844 {
1845 global $wp_roles; // Access global roles object
1846 $user_roles = [];
1847
1848 if (isset($wp_roles->roles) && is_array($wp_roles->roles)) {
1849 foreach ($wp_roles->roles as $role_key => $role_data) {
1850 $user_roles[] = [
1851 'value' => $role_key, // Machine-readable key
1852 'label' => $role_data['name'], // Human-readable name
1853 ];
1854 }
1855 }
1856
1857 return $user_roles;
1858 }
1859
1860 public static function has_content_allowed_roles($allowed_roles = [])
1861 {
1862 // Ensure it's always an array
1863 if (!is_array($allowed_roles)) {
1864 $allowed_roles = [];
1865 }
1866
1867 if (count($allowed_roles) === 1 && empty($allowed_roles[0])) {
1868 return true;
1869 }
1870
1871 $current_user = wp_get_current_user();
1872 $user_roles = $current_user->roles;
1873
1874 return !empty(array_intersect($user_roles, $allowed_roles));
1875 }
1876
1877
1878 }
1879