PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 4.4.9
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v4.4.9
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 / Gutenberg / EmbedPressBlockRenderer.php
embedpress / EmbedPress / Gutenberg Last commit date
BlockManager.php 5 months ago EmbedPressBlockRenderer.php 5 months ago FallbackHandler.php 9 months ago InitBlocks.php 9 months ago
EmbedPressBlockRenderer.php
1428 lines
1 <?php
2
3 namespace EmbedPress\Gutenberg;
4
5 use EmbedPress\Includes\Classes\Helper;
6 use EmbedPress\Shortcode;
7 use Exception;
8
9 if (!defined('ABSPATH')) {
10 exit;
11 }
12
13 /**
14 * EmbedPress Block Renderer
15 *
16 * Handles rendering of EmbedPress blocks for Gutenberg editor
17 * Manages dynamic content, content protection, and various embed configurations
18 *
19 * @package EmbedPress\Gutenberg
20 * @since 1.0.0
21 */
22 class EmbedPressBlockRenderer
23 {
24 /**
25 * Dynamic providers that require real-time content fetching
26 *
27 * @var array
28 */
29 private static $dynamic_providers = [
30 'photos.app.goo.gl',
31 'photos.google.com',
32 'instagram.com',
33 'opensea.io',
34 'wistia.com',
35 'wistia.net',
36 ];
37
38 /**
39 * Alignment mapping for CSS classes
40 *
41 * @var array
42 */
43 private static $alignment_classes = [
44 'left' => 'alignleft',
45 'right' => 'alignright',
46 'wide' => 'alignwide',
47 'full' => 'alignfull',
48 'center' => 'aligncenter',
49 ];
50
51 /**
52 * Check if URL belongs to a dynamic provider
53 *
54 * @param string $url The URL to check
55 * @return bool True if dynamic provider, false otherwise
56 */
57 public static function is_dynamic_provider($url)
58 {
59 foreach (self::$dynamic_providers as $provider) {
60 if (strpos($url, $provider) !== false) {
61 return true;
62 }
63 }
64
65 return false;
66 }
67
68 /**
69 * Render dynamic content using EmbedPress shortcode parsing
70 *
71 * @param array $attributes Block attributes
72 * @return string|null Rendered content or null on failure
73 */
74 public static function render_dynamic_content($attributes)
75 {
76 $url = $attributes['url'] ?? '';
77
78 if (!class_exists('\\EmbedPress\\Shortcode')) {
79 return null;
80 }
81
82 // Map Meetup-specific Gutenberg attributes to shortcode attributes
83 if (!empty($url) && strpos($url, 'meetup.com') !== false) {
84 if (isset($attributes['meetupOrderBy'])) {
85 $attributes['orderby'] = $attributes['meetupOrderBy'];
86 }
87 if (isset($attributes['meetupOrder'])) {
88 $attributes['order'] = $attributes['meetupOrder'];
89 }
90 if (isset($attributes['meetupPerPage'])) {
91 $attributes['per_page'] = $attributes['meetupPerPage'];
92 }
93 if (isset($attributes['meetupEnablePagination'])) {
94 $attributes['enable_pagination'] = $attributes['meetupEnablePagination'];
95 }
96 if (isset($attributes['meetupTimezone'])) {
97 $attributes['timezone'] = $attributes['meetupTimezone'];
98 }
99 if (isset($attributes['meetupDateFormat'])) {
100 $attributes['date_format'] = $attributes['meetupDateFormat'];
101 }
102 if (isset($attributes['meetupTimeFormat'])) {
103 $attributes['time_format'] = $attributes['meetupTimeFormat'];
104 }
105 }
106
107 try {
108 $embed_result = Shortcode::parseContent($url, false, $attributes);
109
110 if (is_object($embed_result) && isset($embed_result->embed) && !empty($embed_result->embed)) {
111 return $embed_result->embed;
112 }
113 } catch (Exception $e) {
114 if (defined('WP_DEBUG') && WP_DEBUG) {
115 error_log('EmbedPress: Shortcode parsing failed: ' . $e->getMessage());
116 }
117 }
118
119 return null;
120 }
121
122 /**
123 * Main render method for EmbedPress blocks
124 *
125 * @param array $attributes Block attributes
126 * @param string $content Block content
127 * @param object $block Block object (unused but kept for compatibility)
128 * @return string Rendered HTML content
129 */
130 public static function render($attributes, $content = '', $block = null)
131 {
132 // Extract basic attributes
133 $url = $attributes['url'] ?? '';
134 $client_id = !empty($attributes['clientId']) ? md5($attributes['clientId']) : '';
135
136 // Handle content protection
137 $protection_data = self::extract_protection_data($attributes, $client_id);
138 $should_display_content = self::should_display_content($protection_data);
139 $isAdManager = !empty($attributes['adManager']) ? true : false;
140
141
142 // Early return for non-dynamic providers with displayable content
143 if ((!empty($content) && !self::is_dynamic_provider($url)) && $should_display_content && !$isAdManager) {
144 return $content;
145 }
146
147 // Process embed HTML if available
148 if (!empty($attributes['embedHTML'])) {
149 return self::render_embed_html($attributes, $attributes['embedHTML'], $protection_data, $should_display_content);
150 }
151
152 return '';
153 }
154
155
156 /**
157 * Legacy PDF render method - organized following current structure
158 *
159 * @param array $attributes Block attributes
160 * @return string Rendered HTML content
161 */
162 public static function embedpress_pdf_legacy_render_block($attributes)
163 {
164 // Extract basic attributes for PDF block
165 $href = $attributes['href'] ?? '';
166 $id = $attributes['id'] ?? 'embedpress-pdf-' . rand(100, 10000);
167 $client_id = md5($id);
168
169 // If no href is provided, return empty
170 if (empty($href)) {
171 return '';
172 }
173
174 // Handle content protection using existing methods
175 $protection_data = self::extract_protection_data($attributes, $client_id);
176 $should_display_content = self::should_display_content($protection_data);
177
178 // Build styling configuration using existing method
179 $styling = self::build_styling_config($attributes, $protection_data);
180
181 // Generate legacy PDF HTML
182 return self::render_legacy_pdf_html($attributes, $protection_data, $should_display_content, $styling);
183 }
184
185 /**
186 * Render legacy PDF HTML structure
187 *
188 * @param array $attributes Block attributes
189 * @param array $protection_data Protection data
190 * @param bool $should_display_content Whether content should be displayed
191 * @param array $styling Styling configuration
192 * @return string Rendered HTML
193 */
194 private static function render_legacy_pdf_html($attributes, $protection_data, $should_display_content, $styling)
195 {
196 $href = $attributes['href'];
197 $id = $attributes['id'] ?? 'embedpress-pdf-' . rand(100, 10000);
198 $client_id = md5($id);
199
200 // Extract legacy-specific configurations
201 $legacy_config = self::extract_legacy_pdf_config($attributes);
202
203 // Generate embed code
204 $embed_code = self::generate_legacy_embed_code($attributes, $legacy_config);
205
206 // Build wrapper classes
207 $wrapper_classes = self::build_legacy_wrapper_classes($attributes, $styling, $legacy_config);
208
209 ob_start();
210 ?>
211 <div id="ep-gutenberg-content-<?php echo esc_attr($client_id) ?>" class="ep-gutenberg-content <?php echo esc_attr($wrapper_classes); ?>">
212 <div class="embedpress-inner-iframe <?php echo esc_attr($legacy_config['unit_class']); ?> ep-doc-<?php echo esc_attr($client_id); ?>" style="<?php echo esc_attr($legacy_config['style_attr']); ?>" id="<?php echo esc_attr($id); ?>">
213 <div <?php echo esc_attr($styling['ads_attrs']); ?>>
214 <?php
215 do_action('embedpress_pdf_gutenberg_after_embed', $client_id, 'pdf', $attributes, $href);
216
217 if ($should_display_content) {
218 self::render_legacy_displayable_content($embed_code, $attributes, $styling);
219 } else {
220 self::render_legacy_protected_content($embed_code, $attributes, $protection_data, $styling);
221 }
222
223 // Render ad template if enabled
224 if (!empty($attributes['adManager'])) {
225 $embed_code = apply_filters('embedpress/generate_ad_template', $embed_code, $client_id, $attributes, 'gutenberg');
226 }
227 ?>
228 </div>
229 </div>
230 </div>
231 <?php
232 return ob_get_clean();
233 }
234
235 /**
236 * Extract legacy PDF configuration
237 *
238 * @param array $attributes Block attributes
239 * @return array Legacy configuration
240 */
241 private static function extract_legacy_pdf_config($attributes)
242 {
243 $unitoption = $attributes['unitoption'] ?? 'px';
244 $width = !empty($attributes['width']) ? $attributes['width'] . $unitoption : (Helper::get_options_value('enableEmbedResizeWidth') ?: 600) . 'px';
245 $height = !empty($attributes['height']) ? $attributes['height'] . 'px' : (Helper::get_options_value('enableEmbedResizeHeight') ?: 600) . 'px';
246
247 $width_class = ($unitoption == '%') ? 'ep-percentage-width' : 'ep-fixed-width';
248 $unit_class = ($unitoption === '%') ? 'emebedpress-unit-percent' : '';
249
250 $style_attr = ($unitoption === '%' && !empty($attributes['width']))
251 ? 'max-width:' . $attributes['width'] . '%'
252 : 'max-width:100%';
253
254 $dimension = "width:$width;height:$height";
255
256 return [
257 'unitoption' => $unitoption,
258 'width' => $width,
259 'height' => $height,
260 'width_class' => $width_class,
261 'unit_class' => $unit_class,
262 'style_attr' => $style_attr,
263 'dimension' => $dimension,
264 ];
265 }
266
267 /**
268 * Generate legacy embed code
269 *
270 * @param array $attributes Block attributes
271 * @param array $legacy_config Legacy configuration
272 * @return string Embed code
273 */
274 private static function generate_legacy_embed_code($attributes, $legacy_config)
275 {
276 $href = $attributes['href'];
277 $id = $attributes['id'] ?? 'embedpress-pdf-' . rand(100, 10000);
278 $renderer = Helper::get_pdf_renderer();
279
280 $src = $renderer . ((strpos($renderer, '?') == false) ? '?' : '&') . 'file=' . urlencode($href) . self::generate_pdf_params($attributes);
281
282 $iframe_title = self::get_iframe_title_from_url($href);
283
284 $embed_code = '<iframe title="' . esc_attr($iframe_title) . '" class="embedpress-embed-document-pdf ' . esc_attr($id) . '" style="' . esc_attr($legacy_config['dimension']) . '; max-width:100%; display: inline-block" src="' . esc_url($src) . '" frameborder="0" oncontextmenu="return false;"></iframe> ';
285
286 // Handle flip-book viewer style
287 if (isset($attributes['viewerStyle']) && $attributes['viewerStyle'] === 'flip-book') {
288 $src = urlencode($href) . self::generate_pdf_params($attributes);
289 $renderer = Helper::get_flipbook_renderer();
290 $src_url = $renderer . ((strpos($renderer, '?') == false) ? '?' : '&') . 'file=' . $src;
291 $embed_code = '<iframe title="' . esc_attr(Helper::get_file_title($href)) . '" class="embedpress-embed-document-pdf ' . esc_attr($id) . '" style="' . esc_attr($legacy_config['dimension']) . '; max-width:100%; display: inline-block" src="' . esc_url($src_url) . '" frameborder="0" oncontextmenu="return false;"></iframe> ';
292 }
293
294 // Add powered by if enabled
295 $gen_settings = get_option(EMBEDPRESS_PLG_NAME);
296 $powered_by = isset($gen_settings['embedpress_document_powered_by']) && 'yes' === $gen_settings['embedpress_document_powered_by'];
297 if (isset($attributes['powered_by'])) {
298 $powered_by = $attributes['powered_by'];
299 }
300
301 if ($powered_by) {
302 $embed_code .= sprintf('<p class="embedpress-el-powered">%s</p>', __('Powered By EmbedPress', 'embedpress'));
303 }
304
305 return $embed_code;
306 }
307
308 /**
309 * Build legacy wrapper classes
310 *
311 * @param array $attributes Block attributes
312 * @param array $styling Styling configuration
313 * @param array $legacy_config Legacy configuration
314 * @return string CSS classes
315 */
316 private static function build_legacy_wrapper_classes($attributes, $styling, $legacy_config)
317 {
318 return trim(sprintf(
319 '%s %s %s %s %s',
320 $styling['alignment'],
321 $legacy_config['width_class'],
322 $styling['content_share_class'],
323 $styling['share_position_class'],
324 $styling['content_protection_class']
325 ));
326 }
327
328 /**
329 * Render legacy displayable content
330 *
331 * @param string $embed_code Embed code
332 * @param array $attributes Block attributes
333 * @param array $styling Styling configuration
334 */
335 private static function render_legacy_displayable_content($embed_code, $attributes, $styling)
336 {
337 $share_position = $attributes['sharePosition'] ?? 'right';
338 $content_id = $attributes['id'];
339
340 echo '<div class="ep-embed-content-wraper">';
341 $embed = '<div class="position-' . esc_attr($share_position) . '-wraper gutenberg-pdf-wraper">';
342 $embed .= $embed_code;
343 $embed .= '</div>';
344
345 if (!empty($attributes['contentShare'])) {
346 $embed .= Helper::embed_content_share($content_id, $attributes);
347 }
348 echo $embed;
349 echo '</div>';
350 }
351
352 /**
353 * Render legacy protected content
354 *
355 * @param string $embed_code Embed code
356 * @param array $attributes Block attributes
357 * @param array $protection_data Protection data
358 * @param array $styling Styling configuration
359 */
360 private static function render_legacy_protected_content($embed_code, $attributes, $protection_data, $styling)
361 {
362 $share_position = $attributes['sharePosition'] ?? 'right';
363 $client_id = $protection_data['client_id'];
364
365 // Initialize $embed variable
366 $embed = $embed_code;
367
368 if (!empty($attributes['contentShare'])) {
369 $content_id = $attributes['clientId'];
370 $embed = '<div class="position-' . esc_attr($share_position) . '-wraper gutenberg-pdf-wraper">';
371 $embed .= $embed_code;
372 $embed .= '</div>';
373 $embed .= Helper::embed_content_share($content_id, $attributes);
374 }
375
376 echo '<div class="ep-embed-content-wraper">';
377 if ($attributes['protectionType'] == 'password') {
378 echo '<div id="ep-gutenberg-content-' . esc_attr($client_id) . '" class="ep-gutenberg-content">';
379 do_action('embedpress/display_password_form', $client_id, $embed, $styling['pass_hash_key'], $attributes);
380 echo '</div>';
381 } else {
382 do_action('embedpress/content_protection_content', $client_id, $attributes['protectionMessage'], $attributes['userRole']);
383 }
384 echo '</div>';
385 }
386
387 public static function render_embedpress_pdf($attributes, $content = '', $block = null)
388 {
389
390 // Extract basic attributes for PDF block
391 $href = $attributes['href'] ?? '';
392 $client_id = !empty($attributes['id']) ? md5($attributes['id']) : '';
393
394 // Handle content protection
395 $protection_data = self::extract_protection_data($attributes, $client_id);
396 $should_display_content = self::should_display_content($protection_data);
397 $isAdManager = !empty($attributes['adManager']) ? true : false;
398
399
400 // For PDF blocks, if we have saved content and should display it, return the content
401 if (!empty($content) && $should_display_content && !$isAdManager) {
402 return $content;
403 }
404
405 // If no href is provided, return empty
406 if (empty($href)) {
407 return '';
408 }
409
410
411 if (empty($content)) {
412 return self::embedpress_pdf_legacy_render_block($attributes);
413 }
414
415
416 // Render PDF-specific HTML
417 return self::render_embedpress_pdf_html($attributes, $content, $protection_data, $should_display_content);
418 }
419
420 public static function render_document($attributes, $content = '', $block = null)
421 {
422
423 // Extract basic attributes for PDF block
424 $href = $attributes['href'] ?? '';
425 $client_id = !empty($attributes['id']) ? md5($attributes['id']) : '';
426
427 // Handle content protection
428 $protection_data = self::extract_protection_data($attributes, $client_id);
429 $should_display_content = self::should_display_content($protection_data);
430 $isAdManager = !empty($attributes['adManager']) ? true : false;
431
432 // For PDF blocks, if we have saved content and should display it, return the content
433 if (!empty($content) && $should_display_content && !$isAdManager) {
434 return $content;
435 }
436
437 // If no href is provided, return empty
438 if (empty($href)) {
439 return '';
440 }
441
442
443 // Render PDF-specific HTML
444 return self::render_embedpress_document_html($attributes, $content, $protection_data, $should_display_content);
445 }
446
447 /**
448 * Extract content protection related data from attributes
449 *
450 * @param array $attributes Block attributes
451 * @param string $client_id Client ID for the block
452 * @return array Protection data array
453 */
454 private static function extract_protection_data($attributes, $client_id)
455 {
456 $content_password = $attributes['contentPassword'] ?? '';
457 $hash_pass = hash('sha256', wp_salt(32) . md5($content_password));
458 $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';
459 return [
460 'content_password' => $content_password,
461 'hash_pass' => $hash_pass,
462 'password_correct' => $password_correct,
463 'protection_type' => $attributes['protectionType'] ?? '',
464 'lock_content' => $attributes['lockContent'] ?? '',
465 'user_role' => $attributes['userRole'] ?? '',
466 'content_share' => $attributes['contentShare'] ?? '',
467 'protection_message' => $attributes['protectionMessage'] ?? '',
468 'content_id' => $attributes['clientId'] ?? '',
469 'client_id' => $client_id,
470 ];
471 }
472
473 /**
474 * Determine if content should be displayed based on protection settings
475 *
476 * @param array $protection_data Protection data array
477 * @return bool True if content should be displayed
478 */
479 private static function should_display_content($protection_data)
480 {
481 return (
482 !apply_filters('embedpress/is_allow_rander', false) ||
483 empty($protection_data['lock_content']) ||
484 ($protection_data['protection_type'] === 'password' && empty($protection_data['content_password'])) ||
485 ($protection_data['protection_type'] === 'password' &&
486 !empty(Helper::is_password_correct($protection_data['client_id'])) &&
487 ($protection_data['hash_pass'] === $protection_data['password_correct'])) ||
488 ($protection_data['protection_type'] === 'user-role' &&
489 Helper::has_content_allowed_roles($protection_data['user_role']))
490 );
491 }
492
493 /**
494 * Render the embed HTML with all configurations
495 *
496 * @param array $attributes Block attributes
497 * @param string $content Block content
498 * @param array $protection_data Protection data
499 * @param bool $should_display_content Whether content should be displayed
500 * @return string Rendered HTML
501 */
502 private static function render_embed_html($attributes, $content, $protection_data, $should_display_content)
503 {
504 // Extract basic configuration
505 $config = self::extract_basic_config($attributes);
506
507 // Build carousel configuration
508 $carousel_config = self::build_carousel_config($attributes, $protection_data['client_id']);
509
510 // Build player configuration
511 $player_config = self::build_player_config($attributes, $protection_data['client_id']);
512
513 // Get dynamic content
514 $embed = self::get_embed_content($attributes, $content);
515
516 // Inject iframeTitle derived from URL
517 $url = $attributes['url'] ?? '';
518 $title = self::get_iframe_title_from_url($url);
519
520 if (!empty($title)) {
521 if (is_array($embed) && isset($embed['html'])) {
522 $embed['html'] = preg_replace('/<iframe(.*?)>/i', '<iframe$1 title="' . esc_attr($title) . '">', $embed['html']);
523 } elseif (is_string($embed)) {
524 $embed = preg_replace('/<iframe(.*?)>/i', '<iframe$1 title="' . esc_attr($title) . '">', $embed);
525 }
526 }
527
528 // Build CSS classes and styling
529 $styling = self::build_styling_config($attributes, $protection_data);
530
531 // Generate final HTML
532 return self::generate_final_html($attributes, $embed, $config, $carousel_config, $player_config, $styling, $protection_data, $should_display_content);
533 }
534
535 private static function render_embedpress_document_html($attributes, $content, $protection_data, $should_display_content)
536 {
537
538 $href = $attributes['href'] ?? '';
539 if (empty($href)) return '';
540
541 $id = $attributes['id'] ?? 'embedpress-document-' . rand(100, 10000);
542 $client_id = $attributes['clientId'] ?? md5($id);
543 $contentShare = $attributes['contentShare'] ?? false;
544 $styling = self::build_styling_config($attributes, $protection_data);
545
546
547 ob_start();
548 ?>
549 <div class="wp-block-embedpress-document" data-embed-type="Document">
550 <?php self::render_embed_content($content, $contentShare, $id, $attributes, $should_display_content, $protection_data, $styling); ?>
551 <?php self::render_ad_template($attributes, $content, $client_id); ?>
552 </div>
553 <?php
554
555 return ob_get_clean();
556 }
557
558
559
560 private static function render_embedpress_pdf_html($attributes, $content, $protection_data, $should_display_content)
561 {
562 // Extract PDF-specific attributes
563 $href = $attributes['href'] ?? '';
564 if (empty($href)) {
565 return '';
566 }
567
568 $id = $attributes['id'] ?? 'embedpress-pdf-' . rand(100, 10000);
569 $client_id = md5($id);
570 $contentShare = $attributes['contentShare'] ?? false;
571
572 $styling = self::build_styling_config($attributes, $protection_data);
573
574 // Build the complete HTML structure
575 ob_start();
576 ?>
577 <div class="wp-block-embedpress-pdf" data-embed-type="PDF">
578 <?php self::render_embed_content($content, $contentShare, $id, $attributes, $should_display_content, $protection_data, $styling); ?>
579 <?php self::render_ad_template($attributes, $content, $client_id); ?>
580 </div>
581 <?php
582
583 return ob_get_clean();
584 }
585
586 /**
587 * Generate PDF parameters for viewer configuration
588 * Based on the self::generate_pdf_params function from the old implementation
589 */
590 private static function generate_pdf_params($attributes)
591 {
592 $urlParamData = array(
593 'themeMode' => !empty($attributes['themeMode']) ? $attributes['themeMode'] : 'default',
594 'toolbar' => !empty($attributes['toolbar']) ? 'true' : 'false',
595 'position' => $attributes['position'] ?? 'top',
596 'presentation' => !empty($attributes['presentation']) ? 'true' : 'false',
597 'lazyLoad' => !empty($attributes['lazyLoad']) ? 'true' : 'false',
598 'download' => !empty($attributes['download']) ? 'true' : 'false',
599 'copy_text' => !empty($attributes['copy_text']) ? 'true' : 'false',
600 'add_text' => !empty($attributes['add_text']) ? 'true' : 'false',
601 'draw' => !empty($attributes['draw']) ? 'true' : 'false',
602 'doc_rotation' => !empty($attributes['doc_rotation']) ? 'true' : 'false',
603 'add_image' => !empty($attributes['add_image']) ? 'true' : 'false',
604 'doc_details' => !empty($attributes['doc_details']) ? 'true' : 'false',
605 'zoom_in' => !empty($attributes['zoomIn']) ? 'true' : 'false',
606 'zoom_out' => !empty($attributes['zoomOut']) ? 'true' : 'false',
607 'fit_view' => !empty($attributes['fitView']) ? 'true' : 'false',
608 'bookmark' => !empty($attributes['bookmark']) ? 'true' : 'false',
609 'flipbook_toolbar_position' => !empty($attributes['flipbook_toolbar_position']) ? $attributes['flipbook_toolbar_position'] : 'bottom',
610 'selection_tool' => isset($attributes['selection_tool']) ? esc_attr($attributes['selection_tool']) : '0',
611 'scrolling' => isset($attributes['scrolling']) ? esc_attr($attributes['scrolling']) : '-1',
612 'spreads' => isset($attributes['spreads']) ? esc_attr($attributes['spreads']) : '-1',
613 );
614
615 // Add custom color for custom theme mode
616 if ($urlParamData['themeMode'] === 'custom') {
617 $urlParamData['customColor'] = !empty($attributes['customColor']) ? $attributes['customColor'] : '#403A81';
618 }
619
620 // Handle flip-book viewer style
621 if (isset($attributes['viewerStyle']) && $attributes['viewerStyle'] === 'flip-book') {
622 return "&key=" . base64_encode(mb_convert_encoding(http_build_query($urlParamData), 'UTF-8'));
623 }
624
625 return "#key=" . base64_encode(mb_convert_encoding(http_build_query($urlParamData), 'UTF-8'));
626 }
627
628 /**
629 * Generate document parameters for viewer configuration
630 * Based on document-specific attributes
631 */
632 private static function generate_document_params($attributes)
633 {
634 $urlParamData = array(
635 'theme_mode' => !empty($attributes['themeMode']) ? $attributes['themeMode'] : 'default',
636 'presentation' => !empty($attributes['presentation']) ? 'true' : 'false',
637 'position' => $attributes['position'] ?? 'top',
638 'download' => !empty($attributes['download']) ? 'true' : 'false',
639 'draw' => !empty($attributes['draw']) ? 'true' : 'false',
640 );
641
642 // Add custom color for custom theme mode
643 if ($urlParamData['theme_mode'] === 'custom') {
644 $urlParamData['custom_color'] = !empty($attributes['customColor']) ? $attributes['customColor'] : '#343434';
645 }
646
647 return '?' . http_build_query($urlParamData);
648 }
649
650 /**
651 * Extract basic configuration from attributes
652 *
653 * @param array $attributes Block attributes
654 * @return array Basic configuration
655 */
656 private static function extract_basic_config($attributes)
657 {
658 return [
659 'block_id' => !empty($attributes['clientId']) ? $attributes['clientId'] : '',
660 'custom_player' => !empty($attributes['customPlayer']) ? $attributes['customPlayer'] : 0,
661 'insta_layout' => !empty($attributes['instaLayout']) ? ' ' . $attributes['instaLayout'] : ' insta-grid',
662 'mode' => !empty($attributes['mode']) ? ' ep-google-photos-' . $attributes['mode'] : '',
663 'embed_type' => !empty($attributes['cEmbedType']) ? ' ' . $attributes['cEmbedType'] : '',
664 'url' => $attributes['url'] ?? '',
665 ];
666 }
667
668 /**
669 * Build carousel configuration
670 *
671 * @param array $attributes Block attributes
672 * @param string $client_id Client ID
673 * @return array Carousel configuration
674 */
675 private static function build_carousel_config($attributes, $client_id)
676 {
677 $carousel_options = '';
678 $carousel_id = '';
679
680 if (!empty($attributes['instaLayout']) && $attributes['instaLayout'] === 'insta-carousel') {
681 $carousel_id = 'data-carouselid=' . esc_attr($client_id);
682
683 $options = [
684 'layout' => $attributes['instaLayout'],
685 'slideshow' => !empty($attributes['slidesShow']) ? $attributes['slidesShow'] : 5,
686 'autoplay' => !empty($attributes['carouselAutoplay']) ? $attributes['carouselAutoplay'] : 0,
687 'autoplayspeed' => !empty($attributes['autoplaySpeed']) ? $attributes['autoplaySpeed'] : 3000,
688 'transitionspeed' => !empty($attributes['transitionSpeed']) ? $attributes['transitionSpeed'] : 1000,
689 'loop' => !empty($attributes['carouselLoop']) ? $attributes['carouselLoop'] : 0,
690 'arrows' => !empty($attributes['carouselArrows']) ? $attributes['carouselArrows'] : 0,
691 'spacing' => !empty($attributes['carouselSpacing']) ? $attributes['carouselSpacing'] : 0
692 ];
693
694 $carousel_options = 'data-carousel-options=' . htmlentities(json_encode($options), ENT_QUOTES);
695 }
696
697 return [
698 'carousel_id' => $carousel_id,
699 'carousel_options' => $carousel_options,
700 ];
701 }
702
703 /**
704 * Build player configuration
705 *
706 * @param array $attributes Block attributes
707 * @param string $client_id Client ID
708 * @return array Player configuration
709 */
710 private static function build_player_config($attributes, $client_id)
711 {
712 $custom_player = '';
713 $player_options = '';
714 $custom_player_enabled = !empty($attributes['customPlayer']) ? $attributes['customPlayer'] : 0;
715
716 if (!empty($custom_player_enabled)) {
717 $is_self_hosted = Helper::check_media_format($attributes['url']);
718 $custom_player = 'data-playerid=' . esc_attr($client_id);
719
720 $options = self::build_player_options($attributes, $is_self_hosted);
721 $player_options = 'data-options=' . htmlentities(json_encode($options), ENT_QUOTES);
722 }
723
724 return [
725 'custom_player' => $custom_player,
726 'player_options' => $player_options,
727 ];
728 }
729
730 /**
731 * Build player options array
732 *
733 * @param array $attributes Block attributes
734 * @param array $is_self_hosted Self-hosted media info
735 * @return array Player options
736 */
737 private static function build_player_options($attributes, $is_self_hosted)
738 {
739 $options = [
740 'rewind' => !empty($attributes['playerRewind']),
741 'restart' => !empty($attributes['playerRestart']),
742 'pip' => !empty($attributes['playerPip']),
743 'poster_thumbnail' => $attributes['posterThumbnail'] ?? '',
744 'player_color' => $attributes['playerColor'] ?? '',
745 'player_preset' => $attributes['playerPreset'] ?? 'preset-default',
746 'fast_forward' => !empty($attributes['playerFastForward']),
747 'player_tooltip' => !empty($attributes['playerTooltip']),
748 'hide_controls' => !empty($attributes['playerHideControls']),
749 'download' => !empty($attributes['playerDownload']),
750 ];
751
752 // Add conditional options
753 $conditional_options = [
754 'fullscreen' => 'fullscreen',
755 'starttime' => 'start',
756 'endtime' => 'end',
757 'relatedvideos' => 'rel',
758 'muteVideo' => 'mute',
759 'vstarttime' => 't',
760 'vautoplay' => 'vautoplay',
761 'vautopause' => 'autopause',
762 'vdnt' => 'dnt',
763 ];
764
765 foreach ($conditional_options as $attr_key => $option_key) {
766 if (!empty($attributes[$attr_key])) {
767 $options[$option_key] = $attributes[$attr_key];
768 }
769 }
770
771 // Add self-hosted options
772 if (!empty($is_self_hosted['selhosted'])) {
773 $options['self_hosted'] = $is_self_hosted['selhosted'];
774 $options['hosted_format'] = $is_self_hosted['format'];
775 }
776
777 return $options;
778 }
779
780 /**
781 * Get embed content with dynamic rendering
782 *
783 * @param array $attributes Block attributes
784 * @param string $content Block content
785 * @return string Embed content
786 */
787 private static function get_embed_content($attributes, $content)
788 {
789 return apply_filters('embedpress_render_dynamic_content', $content, $attributes);
790 }
791
792 /**
793 * Build styling configuration
794 *
795 * @param array $attributes Block attributes
796 * @param array $protection_data Protection data
797 * @return array Styling configuration
798 */
799 private static function build_styling_config($attributes, $protection_data)
800 {
801 $client_id = $protection_data['client_id'];
802
803 // Content sharing classes
804 $content_share_class = !empty($attributes['contentShare']) ? 'ep-content-share-enabled' : '';
805 $share_position = $attributes['sharePosition'] ?? 'right';
806 $share_position_class = !empty($attributes['contentShare']) ? 'ep-share-position-' . $share_position : '';
807
808 // Content protection classes
809 $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';
810 $hash_pass = hash('sha256', wp_salt(32) . md5($attributes['contentPassword'] ?? ''));
811 $content_protection_class = 'ep-content-protection-enabled';
812
813 if (empty($attributes['lockContent']) || empty($attributes['contentPassword']) || $hash_pass === $password_correct) {
814 $content_protection_class = 'ep-content-protection-disabled';
815 }
816
817 // Alignment classes
818 $alignment = self::get_alignment_class($attributes['align'] ?? '');
819
820 // Ad manager attributes
821 $ads_attrs = self::build_ads_attributes($attributes, $client_id);
822
823 // Custom branding styles and HTML
824 $custom_branding = self::build_custom_branding($attributes, $client_id);
825
826 // Media format classes
827 $hosted_format = self::get_hosted_format($attributes);
828 $yt_channel_class = (isset($attributes['url']) && Helper::is_youtube_channel($attributes['url'])) ? 'embedded-youtube-channel' : '';
829
830 $auto_pause = !empty($attributes['autoPause']) ? ' enabled-auto-pause' : '';
831
832 return [
833 'content_share_class' => $content_share_class,
834 'share_position_class' => $share_position_class,
835 'content_protection_class' => $content_protection_class,
836 'alignment' => $alignment,
837 'ads_attrs' => $ads_attrs,
838 'custom_branding' => $custom_branding,
839 'hosted_format' => $hosted_format,
840 'yt_channel_class' => $yt_channel_class,
841 'auto_pause' => $auto_pause,
842 'pass_hash_key' => isset($attributes['contentPassword']) ? md5($attributes['contentPassword']) : '',
843 ];
844 }
845
846 /**
847 * Get alignment CSS class
848 *
849 * @param string $align Alignment value
850 * @return string CSS class
851 */
852 private static function get_alignment_class($align)
853 {
854 return isset(self::$alignment_classes[$align])
855 ? self::$alignment_classes[$align] . ' clear'
856 : 'aligncenter';
857 }
858
859 /**
860 * Build ad manager attributes
861 *
862 * @param array $attributes Block attributes
863 * @param string $client_id Client ID
864 * @return string Ad attributes
865 */
866 private static function build_ads_attributes($attributes, $client_id)
867 {
868 if (empty($attributes['adManager'])) {
869 return '';
870 }
871
872 $ad = base64_encode(json_encode($attributes));
873 return "data-sponsored-id=$client_id data-sponsored-attrs=$ad class=sponsored-mask";
874 }
875
876 /**
877 * Get hosted media format
878 *
879 * @param array $attributes Block attributes
880 * @return string Media format
881 */
882 private static function get_hosted_format($attributes)
883 {
884 if (empty($attributes['customPlayer'])) {
885 return '';
886 }
887
888 $self_hosted = Helper::check_media_format($attributes['url']);
889 return $self_hosted['format'] ?? '';
890 }
891
892 /**
893 * Build custom branding configuration
894 *
895 * @param array $attributes Block attributes
896 * @param string $client_id Client ID
897 * @return array Custom branding configuration
898 */
899 private static function build_custom_branding($attributes, $client_id)
900 {
901 $custom_branding = [
902 'html' => '',
903 'styles' => ''
904 ];
905
906 // Check if custom logo is enabled
907 if (empty($attributes['customlogo'])) {
908 return $custom_branding;
909 }
910
911 $logo_url = $attributes['customlogo'];
912 $logo_x = $attributes['logoX'] ?? 5;
913 $logo_y = $attributes['logoY'] ?? 10;
914 $logo_opacity = $attributes['logoOpacity'] ?? 1;
915 $custom_logo_url = $attributes['customlogoUrl'] ?? '';
916
917 // Generate custom logo styles
918 $custom_branding['styles'] = sprintf(
919 '#ep-gutenberg-content-%s img.watermark {
920 border: 0;
921 position: absolute;
922 bottom: %s%%;
923 right: %s%%;
924 max-width: 150px;
925 max-height: 75px;
926 -o-transition: opacity 0.5s ease-in-out;
927 -moz-transition: opacity 0.5s ease-in-out;
928 -webkit-transition: opacity 0.5s ease-in-out;
929 transition: opacity 0.5s ease-in-out;
930 z-index: 1;
931 opacity: %s;
932 }
933 #ep-gutenberg-content-%s img.watermark:hover {
934 opacity: 1;
935 }',
936 esc_attr($client_id),
937 esc_attr($logo_y),
938 esc_attr($logo_x),
939 esc_attr($logo_opacity),
940 esc_attr($client_id)
941 );
942
943 // Generate custom logo HTML
944 $logo_html = sprintf(
945 '<img decoding="async" src="%s" class="watermark ep-custom-logo" width="auto" height="auto" alt="">',
946 esc_url($logo_url)
947 );
948
949 // Wrap with link if URL is provided
950 if (!empty($custom_logo_url)) {
951 $logo_html = sprintf(
952 '<a href="%s" target="_blank">%s</a>',
953 esc_url($custom_logo_url),
954 $logo_html
955 );
956 }
957
958 $custom_branding['html'] = $logo_html;
959
960 return $custom_branding;
961 }
962
963 /**
964 * Generate the final HTML output
965 *
966 * @param array $attributes Block attributes
967 * @param string $embed Embed content
968 * @param array $config Basic configuration
969 * @param array $carousel_config Carousel configuration
970 * @param array $player_config Player configuration
971 * @param array $styling Styling configuration
972 * @param array $protection_data Protection data
973 * @param bool $should_display_content Whether content should be displayed
974 * @return string Final HTML output
975 */
976 private static function generate_final_html($attributes, $embed, $config, $carousel_config, $player_config, $styling, $protection_data, $should_display_content)
977 {
978 ob_start();
979
980 // Extract variables for template
981 $url = $config['url'];
982 $block_id = $config['block_id'];
983 $client_id = $protection_data['client_id'];
984 $content_id = $protection_data['content_id'];
985 $content_share = $protection_data['content_share'];
986
987 // Build wrapper classes
988 $wrapper_classes = self::build_wrapper_classes($styling, $config);
989 $embed_wrapper_classes = self::build_embed_wrapper_classes($attributes);
990 $content_wrapper_classes = self::build_content_wrapper_classes($attributes, $config, $styling);
991
992 ?>
993 <?php if (!empty($styling['custom_branding']['styles'])): ?>
994 <style>
995 <?php echo $styling['custom_branding']['styles']; ?>
996 </style>
997 <?php endif; ?>
998
999 <div class="embedpress-gutenberg-wrapper source-provider-<?php echo Helper::get_provider_name($url); ?> <?php echo esc_attr($wrapper_classes); ?>" id="<?php echo esc_attr($block_id); ?>" data-embed-type="<?php echo Helper::get_provider_name($url); ?> ">
1000 <div class="wp-block-embed__wrapper <?php echo esc_attr($embed_wrapper_classes); ?>">
1001 <div id="ep-gutenberg-content-<?php echo esc_attr($client_id) ?>" class="ep-gutenberg-content<?php echo esc_attr($styling['auto_pause']); ?>">
1002 <div <?php echo esc_attr($styling['ads_attrs']); ?>>
1003 <div class="ep-embed-content-wraper <?php echo esc_attr($content_wrapper_classes); ?>"
1004 <?php echo esc_attr($player_config['custom_player']); ?>
1005 <?php echo esc_attr($player_config['player_options']); ?>
1006 <?php echo esc_attr($carousel_config['carousel_id']); ?>
1007 <?php echo esc_attr($carousel_config['carousel_options']); ?>>
1008
1009 <?php
1010 self::render_embed_content($embed, $content_share, $content_id, $attributes, $should_display_content, $protection_data, $styling);
1011 ?>
1012 </div>
1013
1014 <?php self::render_ad_template($attributes, $embed, $client_id); ?>
1015 </div>
1016 </div>
1017 </div>
1018 </div>
1019 <?php
1020
1021 return ob_get_clean();
1022 }
1023
1024 /**
1025 * Build wrapper CSS classes
1026 *
1027 * @param array $styling Styling configuration
1028 * @param array $config Basic configuration
1029 * @return string CSS classes
1030 */
1031 private static function build_wrapper_classes($styling, $config)
1032 {
1033 return trim(sprintf(
1034 '%s %s %s %s%s',
1035 $styling['alignment'],
1036 $styling['content_share_class'],
1037 $styling['share_position_class'],
1038 $styling['content_protection_class'],
1039 $config['embed_type']
1040 ));
1041 }
1042
1043 /**
1044 * Build embed wrapper CSS classes
1045 *
1046 * @param array $attributes Block attributes
1047 * @return string CSS classes
1048 */
1049 private static function build_embed_wrapper_classes($attributes)
1050 {
1051 $classes = [];
1052
1053 if (!empty($attributes['contentShare'])) {
1054 $share_position = $attributes['sharePosition'] ?? 'right';
1055 $classes[] = 'position-' . $share_position . '-wraper';
1056 }
1057
1058 if (($attributes['videosize'] ?? '') === 'responsive') {
1059 $classes[] = 'ep-video-responsive';
1060 }
1061
1062 return implode(' ', $classes);
1063 }
1064
1065 /**
1066 * Build content wrapper CSS classes
1067 *
1068 * @param array $attributes Block attributes
1069 * @param array $config Basic configuration
1070 * @param array $styling Styling configuration
1071 * @return string CSS classes
1072 */
1073 private static function build_content_wrapper_classes($attributes, $config, $styling)
1074 {
1075 return trim(sprintf(
1076 '%s%s%s %s %s',
1077 $attributes['playerPreset'] ?? '',
1078 $config['insta_layout'],
1079 $config['mode'],
1080 $styling['hosted_format'],
1081 $styling['yt_channel_class']
1082 ));
1083 }
1084
1085 /**
1086 * Render embed content with protection logic
1087 *
1088 * @param string $embed Embed content
1089 * @param string $content_share Content share setting
1090 * @param string $content_id Content ID
1091 * @param array $attributes Block attributes
1092 * @param bool $should_display_content Whether content should be displayed
1093 * @param array $protection_data Protection data
1094 * @param array $styling Styling configuration
1095 */
1096 private static function render_embed_content($embed, $content_share, $content_id, $attributes, $should_display_content, $protection_data, $styling)
1097 {
1098 if ($should_display_content) {
1099 self::render_displayable_content($embed, $content_share, $content_id, $attributes, $styling);
1100 } else {
1101 self::render_protected_content($embed, $content_share, $content_id, $attributes, $protection_data, $styling);
1102 }
1103 }
1104
1105 /**
1106 * Render displayable content
1107 *
1108 * @param string $embed Embed content
1109 * @param string $content_share Content share setting
1110 * @param string $content_id Content ID
1111 * @param array $attributes Block attributes
1112 * @param array $styling Styling configuration (optional)
1113 */
1114 private static function render_displayable_content($embed, $content_share, $content_id, $attributes, $styling = [])
1115 {
1116 // Add custom branding if available
1117 if (!empty($styling['custom_branding']['html'])) {
1118 if (is_array($embed)) {
1119 $embed['html'] .= $styling['custom_branding']['html'];
1120 } else {
1121 $embed .= $styling['custom_branding']['html'];
1122 }
1123 }
1124
1125 if (!empty($content_share)) {
1126 $embed .= Helper::embed_content_share($content_id, $attributes);
1127 }
1128
1129 if (is_array($embed)) {
1130 echo $embed['html'];
1131 } else {
1132 echo $embed;
1133 }
1134 }
1135
1136 /**
1137 * Render protected content
1138 *
1139 * @param string $embed Embed content
1140 * @param string $content_share Content share setting
1141 * @param string $content_id Content ID
1142 * @param array $attributes Block attributes
1143 * @param array $protection_data Protection data
1144 * @param array $styling Styling configuration
1145 */
1146 private static function render_protected_content($embed, $content_share, $content_id, $attributes, $protection_data, $styling)
1147 {
1148 // Add custom branding if available
1149 if (!empty($styling['custom_branding']['html'])) {
1150 if (is_array($embed)) {
1151 $embed['html'] .= $styling['custom_branding']['html'];
1152 } else {
1153 $embed .= $styling['custom_branding']['html'];
1154 }
1155 }
1156
1157 if (!empty($content_share)) {
1158 $embed .= Helper::embed_content_share($content_id, $attributes);
1159 }
1160
1161 if ($protection_data['protection_type'] === 'password') {
1162 echo '<div id="ep-gutenberg-content-' . esc_attr($protection_data['client_id']) . '" class="ep-gutenberg-content">';
1163 do_action('embedpress/display_password_form', $protection_data['client_id'], $embed, $styling['pass_hash_key'], $attributes);
1164 echo '</div>';
1165 } else {
1166 do_action('embedpress/content_protection_content', $protection_data['client_id'], $protection_data['protection_message'], $protection_data['user_role']);
1167 }
1168 }
1169
1170 /**
1171 * Render ad template if enabled
1172 *
1173 * @param array $attributes Block attributes
1174 * @param string $embed Embed content
1175 * @param string $client_id Client ID
1176 */
1177 private static function render_ad_template($attributes, $embed, $client_id)
1178 {
1179 if (!empty($attributes['adManager'])) {
1180 $embed = apply_filters('embedpress/generate_ad_template', $embed, $client_id, $attributes, 'gutenberg');
1181 }
1182 }
1183
1184 /**
1185 * YouTube block legacy render method
1186 *
1187 * @param array $attributes Block attributes
1188 * @param string $content Block content
1189 * @param object $block Block object (unused but kept for compatibility)
1190 * @return string Rendered HTML content
1191 */
1192 public static function render_youtube_block($attributes, $content = '', $block = null)
1193 {
1194
1195 if (!empty($content)) {
1196 return $content;
1197 }
1198 // Extract basic attributes for YouTube block
1199 $iframe_src = $attributes['iframeSrc'] ?? '';
1200 $align = $attributes['align'] ?? 'center';
1201
1202 // If no iframe source is provided, return empty
1203 if (empty($iframe_src)) {
1204 return '';
1205 }
1206
1207 // Validate YouTube URL
1208 if (!self::is_youtube_url($iframe_src)) {
1209 return '';
1210 }
1211
1212 // Apply YouTube parameters filter
1213 $youtube_params = apply_filters('embedpress_gutenberg_youtube_params', []);
1214 $processed_iframe_url = $iframe_src;
1215
1216 foreach ($youtube_params as $param => $value) {
1217 $processed_iframe_url = add_query_arg($param, $value, $processed_iframe_url);
1218 }
1219
1220 // Build alignment class
1221 $align_class = 'align' . $align;
1222
1223 // Generate YouTube block HTML
1224 ob_start();
1225 ?>
1226 <div class="ose-youtube wp-block-embed-youtube ose-youtube-single-video <?php echo esc_attr($align_class); ?>">
1227 <iframe src="<?php echo esc_url($processed_iframe_url); ?>"
1228 allowtransparency="true"
1229 allowfullscreen="true"
1230 frameborder="0"
1231 width="640" height="360">
1232 </iframe>
1233 </div>
1234 <?php
1235 return ob_get_clean();
1236 }
1237
1238 /**
1239 * Validate if URL is a YouTube URL
1240 *
1241 * @param string $url URL to validate
1242 * @return bool True if valid YouTube URL, false otherwise
1243 */
1244 private static function is_youtube_url($url)
1245 {
1246 $pattern = '/^(https?:\/\/)?(www\.)?(youtube\.com\/(watch\?(.*&)?v=|(embed|v)\/))|youtu.be\/([a-zA-Z0-9_-]{11})/';
1247 return preg_match($pattern, $url);
1248 }
1249
1250 /**
1251 * Wistia block legacy render method
1252 *
1253 * @param array $attributes Block attributes
1254 * @param string $content Block content
1255 * @param object $block Block object (unused but kept for compatibility)
1256 * @return string Rendered HTML content
1257 */
1258 public static function render_wistia_block($attributes, $content = '', $block = null)
1259 {
1260 if (!empty($content)) {
1261 return $content;
1262 }
1263
1264 // Extract basic attributes for Wistia block
1265 $url = $attributes['url'] ?? '';
1266 $iframe_src = $attributes['iframeSrc'] ?? '';
1267 $align = $attributes['align'] ?? 'center';
1268
1269 // If no URL is provided, return empty
1270 if (empty($url)) {
1271 return '';
1272 }
1273
1274 // Extract Wistia media ID from URL
1275 $wistia_id = self::extract_wistia_id($url);
1276 if (empty($wistia_id)) {
1277 return '';
1278 }
1279
1280 // Build alignment class
1281 $align_class = 'align' . $align;
1282
1283 // Generate Wistia block HTML
1284 ob_start();
1285 ?>
1286 <div class="ose-wistia wp-block-embed-youtube <?php echo esc_attr($align_class); ?>" id="wistia_<?php echo esc_attr($wistia_id); ?>">
1287 <iframe src="<?php echo esc_url($iframe_src); ?>"
1288 allowtransparency="true"
1289 frameborder="0"
1290 class="wistia_embed"
1291 name="wistia_embed"
1292 width="600"
1293 height="330">
1294 </iframe>
1295 <?php do_action('embedpress_gutenberg_wistia_block_after_embed', $attributes); ?>
1296 </div>
1297 <?php
1298 return ob_get_clean();
1299 }
1300
1301 /**
1302 * Extract Wistia media ID from URL
1303 *
1304 * @param string $url Wistia URL
1305 * @return string|false Wistia media ID or false if not found
1306 */
1307 private static function extract_wistia_id($url)
1308 {
1309 preg_match('~medias/(.*)~i', esc_url($url), $matches);
1310 return isset($matches[1]) ? $matches[1] : false;
1311 }
1312
1313 /**
1314 * Calendar block render method
1315 *
1316 * @param array $attributes Block attributes
1317 * @param string $content Block content
1318 * @param object $block Block object (unused but kept for compatibility)
1319 * @return string Rendered HTML content
1320 */
1321 public static function render_embedpress_calendar($attributes, $content = '', $block = null)
1322 {
1323 if (!empty($content)) {
1324 return $content;
1325 }
1326
1327 // Extract basic attributes for Calendar block
1328 $url = $attributes['url'] ?? '';
1329 $width = $attributes['width'] ?? '600';
1330 $height = $attributes['height'] ?? '600';
1331 $powered_by = $attributes['powered_by'] ?? false;
1332 $is_public = $attributes['is_public'] ?? true;
1333 $align = $attributes['align'] ?? 'center';
1334
1335 // If no URL is provided, return empty
1336 if (empty($url)) {
1337 return '';
1338 }
1339
1340 // Validate Google Calendar URL
1341 if (!self::is_google_calendar_url($url)) {
1342 return '<p class="embedpress-el-powered">' . esc_html__('Invalid Calendar Link', 'embedpress') . '</p>';
1343 }
1344
1345 // Build alignment class
1346 $align_class = 'align' . $align;
1347
1348 // Sanitize URL
1349 $sanitized_url = esc_url($url);
1350
1351 // Generate Calendar block HTML
1352 ob_start();
1353 ?>
1354 <figure class="wp-block-embedpress-embedpress-calendar <?php echo esc_attr($align_class); ?>" style="width: <?php echo esc_attr($width); ?>px; height: <?php echo esc_attr($height); ?>px;">
1355 <?php if ($is_public && self::is_google_calendar_url($url)) : ?>
1356 <iframe src="<?php echo esc_url($sanitized_url); ?>"
1357 width="<?php echo esc_attr($width); ?>"
1358 height="<?php echo esc_attr($height); ?>"
1359 frameborder="0"
1360 scrolling="no"
1361 title="<?php echo esc_attr(self::get_iframe_title_from_url($url)); ?>">
1362 </iframe>
1363 <?php endif; ?>
1364
1365 <?php if ($powered_by && self::is_google_calendar_url($url)) : ?>
1366 <p class="embedpress-el-powered"><?php echo esc_html__('Powered By EmbedPress', 'embedpress'); ?></p>
1367 <?php endif; ?>
1368 </figure>
1369 <?php
1370 return ob_get_clean();
1371 }
1372
1373 /**
1374 * Validate if URL is a Google Calendar URL
1375 *
1376 * @param string $url URL to validate
1377 * @return bool True if valid Google Calendar URL, false otherwise
1378 */
1379 private static function is_google_calendar_url($url)
1380 {
1381 $pattern = '/^https:\/\/calendar\.google\.com\/calendar\/(?:u\/\d+\/)?embed\?.*/';
1382 return preg_match($pattern, $url);
1383 }
1384
1385 /**
1386 * Get iframe title from URL
1387 *
1388 * @param string $url URL to derive title from
1389 * @return string Derived title
1390 */
1391 private static function get_iframe_title_from_url($url)
1392 {
1393 if (empty($url)) {
1394 return '';
1395 }
1396
1397 // Try getting title from WordPress attachment if it's a local file
1398 $file_title = Helper::get_file_title($url);
1399 if (!empty($file_title)) {
1400 return $file_title;
1401 }
1402
1403 // Try to get filename from URL
1404 $path = parse_url($url, PHP_URL_PATH);
1405 if ($path) {
1406 $filename = basename($path);
1407 // Remove extension
1408 $filename = preg_replace('/\.[^.]+$/', '', $filename);
1409 // Decode URL encoding
1410 $filename = urldecode($filename);
1411 // Replace hyphens/underscores with spaces
1412 $filename = str_replace(['-', '_'], ' ', $filename);
1413
1414 if (!empty($filename)) {
1415 return ucfirst($filename);
1416 }
1417 }
1418
1419 // Fallback to domain name
1420 $host = parse_url($url, PHP_URL_HOST);
1421 if ($host) {
1422 return $host;
1423 }
1424
1425 return $url;
1426 }
1427 }
1428 ?>