PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 4.4.8
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v4.4.8
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 9 months ago EmbedPressBlockRenderer.php 6 months ago FallbackHandler.php 9 months ago InitBlocks.php 9 months ago
EmbedPressBlockRenderer.php
1424 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 do_action('embedpress/display_password_form', $client_id, $embed, $styling['pass_hash_key'], $attributes);
379 } else {
380 do_action('embedpress/content_protection_content', $client_id, $attributes['protectionMessage'], $attributes['userRole']);
381 }
382 echo '</div>';
383 }
384
385 public static function render_embedpress_pdf($attributes, $content = '', $block = null)
386 {
387
388 // Extract basic attributes for PDF block
389 $href = $attributes['href'] ?? '';
390 $client_id = !empty($attributes['id']) ? md5($attributes['id']) : '';
391
392 // Handle content protection
393 $protection_data = self::extract_protection_data($attributes, $client_id);
394 $should_display_content = self::should_display_content($protection_data);
395 $isAdManager = !empty($attributes['adManager']) ? true : false;
396
397
398 // For PDF blocks, if we have saved content and should display it, return the content
399 if (!empty($content) && $should_display_content && !$isAdManager) {
400 return $content;
401 }
402
403 // If no href is provided, return empty
404 if (empty($href)) {
405 return '';
406 }
407
408
409 if (empty($content)) {
410 return self::embedpress_pdf_legacy_render_block($attributes);
411 }
412
413
414 // Render PDF-specific HTML
415 return self::render_embedpress_pdf_html($attributes, $content, $protection_data, $should_display_content);
416 }
417
418 public static function render_document($attributes, $content = '', $block = null)
419 {
420
421 // Extract basic attributes for PDF block
422 $href = $attributes['href'] ?? '';
423 $client_id = !empty($attributes['id']) ? md5($attributes['id']) : '';
424
425 // Handle content protection
426 $protection_data = self::extract_protection_data($attributes, $client_id);
427 $should_display_content = self::should_display_content($protection_data);
428 $isAdManager = !empty($attributes['adManager']) ? true : false;
429
430 // For PDF blocks, if we have saved content and should display it, return the content
431 if (!empty($content) && $should_display_content && !$isAdManager) {
432 return $content;
433 }
434
435 // If no href is provided, return empty
436 if (empty($href)) {
437 return '';
438 }
439
440
441 // Render PDF-specific HTML
442 return self::render_embedpress_document_html($attributes, $content, $protection_data, $should_display_content);
443 }
444
445 /**
446 * Extract content protection related data from attributes
447 *
448 * @param array $attributes Block attributes
449 * @param string $client_id Client ID for the block
450 * @return array Protection data array
451 */
452 private static function extract_protection_data($attributes, $client_id)
453 {
454 $content_password = $attributes['contentPassword'] ?? '';
455 $hash_pass = hash('sha256', wp_salt(32) . md5($content_password));
456 $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';
457 return [
458 'content_password' => $content_password,
459 'hash_pass' => $hash_pass,
460 'password_correct' => $password_correct,
461 'protection_type' => $attributes['protectionType'] ?? '',
462 'lock_content' => $attributes['lockContent'] ?? '',
463 'user_role' => $attributes['userRole'] ?? '',
464 'content_share' => $attributes['contentShare'] ?? '',
465 'protection_message' => $attributes['protectionMessage'] ?? '',
466 'content_id' => $attributes['clientId'] ?? '',
467 'client_id' => $client_id,
468 ];
469 }
470
471 /**
472 * Determine if content should be displayed based on protection settings
473 *
474 * @param array $protection_data Protection data array
475 * @return bool True if content should be displayed
476 */
477 private static function should_display_content($protection_data)
478 {
479 return (
480 !apply_filters('embedpress/is_allow_rander', false) ||
481 empty($protection_data['lock_content']) ||
482 ($protection_data['protection_type'] === 'password' && empty($protection_data['content_password'])) ||
483 ($protection_data['protection_type'] === 'password' &&
484 !empty(Helper::is_password_correct($protection_data['client_id'])) &&
485 ($protection_data['hash_pass'] === $protection_data['password_correct'])) ||
486 ($protection_data['protection_type'] === 'user-role' &&
487 Helper::has_content_allowed_roles($protection_data['user_role']))
488 );
489 }
490
491 /**
492 * Render the embed HTML with all configurations
493 *
494 * @param array $attributes Block attributes
495 * @param string $content Block content
496 * @param array $protection_data Protection data
497 * @param bool $should_display_content Whether content should be displayed
498 * @return string Rendered HTML
499 */
500 private static function render_embed_html($attributes, $content, $protection_data, $should_display_content)
501 {
502 // Extract basic configuration
503 $config = self::extract_basic_config($attributes);
504
505 // Build carousel configuration
506 $carousel_config = self::build_carousel_config($attributes, $protection_data['client_id']);
507
508 // Build player configuration
509 $player_config = self::build_player_config($attributes, $protection_data['client_id']);
510
511 // Get dynamic content
512 $embed = self::get_embed_content($attributes, $content);
513
514 // Inject iframeTitle derived from URL
515 $url = $attributes['url'] ?? '';
516 $title = self::get_iframe_title_from_url($url);
517
518 if (!empty($title)) {
519 if (is_array($embed) && isset($embed['html'])) {
520 $embed['html'] = preg_replace('/<iframe(.*?)>/i', '<iframe$1 title="' . esc_attr($title) . '">', $embed['html']);
521 } elseif (is_string($embed)) {
522 $embed = preg_replace('/<iframe(.*?)>/i', '<iframe$1 title="' . esc_attr($title) . '">', $embed);
523 }
524 }
525
526 // Build CSS classes and styling
527 $styling = self::build_styling_config($attributes, $protection_data);
528
529 // Generate final HTML
530 return self::generate_final_html($attributes, $embed, $config, $carousel_config, $player_config, $styling, $protection_data, $should_display_content);
531 }
532
533 private static function render_embedpress_document_html($attributes, $content, $protection_data, $should_display_content)
534 {
535
536 $href = $attributes['href'] ?? '';
537 if (empty($href)) return '';
538
539 $id = $attributes['id'] ?? 'embedpress-document-' . rand(100, 10000);
540 $client_id = $attributes['clientId'] ?? md5($id);
541 $contentShare = $attributes['contentShare'] ?? false;
542 $styling = self::build_styling_config($attributes, $protection_data);
543
544
545 ob_start();
546 ?>
547 <div class="wp-block-embedpress-document" data-embed-type="Document">
548 <?php self::render_embed_content($content, $contentShare, $id, $attributes, $should_display_content, $protection_data, $styling); ?>
549 <?php self::render_ad_template($attributes, $content, $client_id); ?>
550 </div>
551 <?php
552
553 return ob_get_clean();
554 }
555
556
557
558 private static function render_embedpress_pdf_html($attributes, $content, $protection_data, $should_display_content)
559 {
560 // Extract PDF-specific attributes
561 $href = $attributes['href'] ?? '';
562 if (empty($href)) {
563 return '';
564 }
565
566 $id = $attributes['id'] ?? 'embedpress-pdf-' . rand(100, 10000);
567 $client_id = md5($id);
568 $contentShare = $attributes['contentShare'] ?? false;
569
570 $styling = self::build_styling_config($attributes, $protection_data);
571
572 // Build the complete HTML structure
573 ob_start();
574 ?>
575 <div class="wp-block-embedpress-pdf" data-embed-type="PDF">
576 <?php self::render_embed_content($content, $contentShare, $id, $attributes, $should_display_content, $protection_data, $styling); ?>
577 <?php self::render_ad_template($attributes, $content, $client_id); ?>
578 </div>
579 <?php
580
581 return ob_get_clean();
582 }
583
584 /**
585 * Generate PDF parameters for viewer configuration
586 * Based on the self::generate_pdf_params function from the old implementation
587 */
588 private static function generate_pdf_params($attributes)
589 {
590 $urlParamData = array(
591 'themeMode' => !empty($attributes['themeMode']) ? $attributes['themeMode'] : 'default',
592 'toolbar' => !empty($attributes['toolbar']) ? 'true' : 'false',
593 'position' => $attributes['position'] ?? 'top',
594 'presentation' => !empty($attributes['presentation']) ? 'true' : 'false',
595 'lazyLoad' => !empty($attributes['lazyLoad']) ? 'true' : 'false',
596 'download' => !empty($attributes['download']) ? 'true' : 'false',
597 'copy_text' => !empty($attributes['copy_text']) ? 'true' : 'false',
598 'add_text' => !empty($attributes['add_text']) ? 'true' : 'false',
599 'draw' => !empty($attributes['draw']) ? 'true' : 'false',
600 'doc_rotation' => !empty($attributes['doc_rotation']) ? 'true' : 'false',
601 'add_image' => !empty($attributes['add_image']) ? 'true' : 'false',
602 'doc_details' => !empty($attributes['doc_details']) ? 'true' : 'false',
603 'zoom_in' => !empty($attributes['zoomIn']) ? 'true' : 'false',
604 'zoom_out' => !empty($attributes['zoomOut']) ? 'true' : 'false',
605 'fit_view' => !empty($attributes['fitView']) ? 'true' : 'false',
606 'bookmark' => !empty($attributes['bookmark']) ? 'true' : 'false',
607 'flipbook_toolbar_position' => !empty($attributes['flipbook_toolbar_position']) ? $attributes['flipbook_toolbar_position'] : 'bottom',
608 'selection_tool' => isset($attributes['selection_tool']) ? esc_attr($attributes['selection_tool']) : '0',
609 'scrolling' => isset($attributes['scrolling']) ? esc_attr($attributes['scrolling']) : '-1',
610 'spreads' => isset($attributes['spreads']) ? esc_attr($attributes['spreads']) : '-1',
611 );
612
613 // Add custom color for custom theme mode
614 if ($urlParamData['themeMode'] === 'custom') {
615 $urlParamData['customColor'] = !empty($attributes['customColor']) ? $attributes['customColor'] : '#403A81';
616 }
617
618 // Handle flip-book viewer style
619 if (isset($attributes['viewerStyle']) && $attributes['viewerStyle'] === 'flip-book') {
620 return "&key=" . base64_encode(mb_convert_encoding(http_build_query($urlParamData), 'UTF-8'));
621 }
622
623 return "#key=" . base64_encode(mb_convert_encoding(http_build_query($urlParamData), 'UTF-8'));
624 }
625
626 /**
627 * Generate document parameters for viewer configuration
628 * Based on document-specific attributes
629 */
630 private static function generate_document_params($attributes)
631 {
632 $urlParamData = array(
633 'theme_mode' => !empty($attributes['themeMode']) ? $attributes['themeMode'] : 'default',
634 'presentation' => !empty($attributes['presentation']) ? 'true' : 'false',
635 'position' => $attributes['position'] ?? 'top',
636 'download' => !empty($attributes['download']) ? 'true' : 'false',
637 'draw' => !empty($attributes['draw']) ? 'true' : 'false',
638 );
639
640 // Add custom color for custom theme mode
641 if ($urlParamData['theme_mode'] === 'custom') {
642 $urlParamData['custom_color'] = !empty($attributes['customColor']) ? $attributes['customColor'] : '#343434';
643 }
644
645 return '?' . http_build_query($urlParamData);
646 }
647
648 /**
649 * Extract basic configuration from attributes
650 *
651 * @param array $attributes Block attributes
652 * @return array Basic configuration
653 */
654 private static function extract_basic_config($attributes)
655 {
656 return [
657 'block_id' => !empty($attributes['clientId']) ? $attributes['clientId'] : '',
658 'custom_player' => !empty($attributes['customPlayer']) ? $attributes['customPlayer'] : 0,
659 'insta_layout' => !empty($attributes['instaLayout']) ? ' ' . $attributes['instaLayout'] : ' insta-grid',
660 'mode' => !empty($attributes['mode']) ? ' ep-google-photos-' . $attributes['mode'] : '',
661 'embed_type' => !empty($attributes['cEmbedType']) ? ' ' . $attributes['cEmbedType'] : '',
662 'url' => $attributes['url'] ?? '',
663 ];
664 }
665
666 /**
667 * Build carousel configuration
668 *
669 * @param array $attributes Block attributes
670 * @param string $client_id Client ID
671 * @return array Carousel configuration
672 */
673 private static function build_carousel_config($attributes, $client_id)
674 {
675 $carousel_options = '';
676 $carousel_id = '';
677
678 if (!empty($attributes['instaLayout']) && $attributes['instaLayout'] === 'insta-carousel') {
679 $carousel_id = 'data-carouselid=' . esc_attr($client_id);
680
681 $options = [
682 'layout' => $attributes['instaLayout'],
683 'slideshow' => !empty($attributes['slidesShow']) ? $attributes['slidesShow'] : 5,
684 'autoplay' => !empty($attributes['carouselAutoplay']) ? $attributes['carouselAutoplay'] : 0,
685 'autoplayspeed' => !empty($attributes['autoplaySpeed']) ? $attributes['autoplaySpeed'] : 3000,
686 'transitionspeed' => !empty($attributes['transitionSpeed']) ? $attributes['transitionSpeed'] : 1000,
687 'loop' => !empty($attributes['carouselLoop']) ? $attributes['carouselLoop'] : 0,
688 'arrows' => !empty($attributes['carouselArrows']) ? $attributes['carouselArrows'] : 0,
689 'spacing' => !empty($attributes['carouselSpacing']) ? $attributes['carouselSpacing'] : 0
690 ];
691
692 $carousel_options = 'data-carousel-options=' . htmlentities(json_encode($options), ENT_QUOTES);
693 }
694
695 return [
696 'carousel_id' => $carousel_id,
697 'carousel_options' => $carousel_options,
698 ];
699 }
700
701 /**
702 * Build player configuration
703 *
704 * @param array $attributes Block attributes
705 * @param string $client_id Client ID
706 * @return array Player configuration
707 */
708 private static function build_player_config($attributes, $client_id)
709 {
710 $custom_player = '';
711 $player_options = '';
712 $custom_player_enabled = !empty($attributes['customPlayer']) ? $attributes['customPlayer'] : 0;
713
714 if (!empty($custom_player_enabled)) {
715 $is_self_hosted = Helper::check_media_format($attributes['url']);
716 $custom_player = 'data-playerid=' . esc_attr($client_id);
717
718 $options = self::build_player_options($attributes, $is_self_hosted);
719 $player_options = 'data-options=' . htmlentities(json_encode($options), ENT_QUOTES);
720 }
721
722 return [
723 'custom_player' => $custom_player,
724 'player_options' => $player_options,
725 ];
726 }
727
728 /**
729 * Build player options array
730 *
731 * @param array $attributes Block attributes
732 * @param array $is_self_hosted Self-hosted media info
733 * @return array Player options
734 */
735 private static function build_player_options($attributes, $is_self_hosted)
736 {
737 $options = [
738 'rewind' => !empty($attributes['playerRewind']),
739 'restart' => !empty($attributes['playerRestart']),
740 'pip' => !empty($attributes['playerPip']),
741 'poster_thumbnail' => $attributes['posterThumbnail'] ?? '',
742 'player_color' => $attributes['playerColor'] ?? '',
743 'player_preset' => $attributes['playerPreset'] ?? 'preset-default',
744 'fast_forward' => !empty($attributes['playerFastForward']),
745 'player_tooltip' => !empty($attributes['playerTooltip']),
746 'hide_controls' => !empty($attributes['playerHideControls']),
747 'download' => !empty($attributes['playerDownload']),
748 ];
749
750 // Add conditional options
751 $conditional_options = [
752 'fullscreen' => 'fullscreen',
753 'starttime' => 'start',
754 'endtime' => 'end',
755 'relatedvideos' => 'rel',
756 'muteVideo' => 'mute',
757 'vstarttime' => 't',
758 'vautoplay' => 'vautoplay',
759 'vautopause' => 'autopause',
760 'vdnt' => 'dnt',
761 ];
762
763 foreach ($conditional_options as $attr_key => $option_key) {
764 if (!empty($attributes[$attr_key])) {
765 $options[$option_key] = $attributes[$attr_key];
766 }
767 }
768
769 // Add self-hosted options
770 if (!empty($is_self_hosted['selhosted'])) {
771 $options['self_hosted'] = $is_self_hosted['selhosted'];
772 $options['hosted_format'] = $is_self_hosted['format'];
773 }
774
775 return $options;
776 }
777
778 /**
779 * Get embed content with dynamic rendering
780 *
781 * @param array $attributes Block attributes
782 * @param string $content Block content
783 * @return string Embed content
784 */
785 private static function get_embed_content($attributes, $content)
786 {
787 return apply_filters('embedpress_render_dynamic_content', $content, $attributes);
788 }
789
790 /**
791 * Build styling configuration
792 *
793 * @param array $attributes Block attributes
794 * @param array $protection_data Protection data
795 * @return array Styling configuration
796 */
797 private static function build_styling_config($attributes, $protection_data)
798 {
799 $client_id = $protection_data['client_id'];
800
801 // Content sharing classes
802 $content_share_class = !empty($attributes['contentShare']) ? 'ep-content-share-enabled' : '';
803 $share_position = $attributes['sharePosition'] ?? 'right';
804 $share_position_class = !empty($attributes['contentShare']) ? 'ep-share-position-' . $share_position : '';
805
806 // Content protection classes
807 $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';
808 $hash_pass = hash('sha256', wp_salt(32) . md5($attributes['contentPassword'] ?? ''));
809 $content_protection_class = 'ep-content-protection-enabled';
810
811 if (empty($attributes['lockContent']) || empty($attributes['contentPassword']) || $hash_pass === $password_correct) {
812 $content_protection_class = 'ep-content-protection-disabled';
813 }
814
815 // Alignment classes
816 $alignment = self::get_alignment_class($attributes['align'] ?? '');
817
818 // Ad manager attributes
819 $ads_attrs = self::build_ads_attributes($attributes, $client_id);
820
821 // Custom branding styles and HTML
822 $custom_branding = self::build_custom_branding($attributes, $client_id);
823
824 // Media format classes
825 $hosted_format = self::get_hosted_format($attributes);
826 $yt_channel_class = (isset($attributes['url']) && Helper::is_youtube_channel($attributes['url'])) ? 'embedded-youtube-channel' : '';
827
828 $auto_pause = !empty($attributes['autoPause']) ? ' enabled-auto-pause' : '';
829
830 return [
831 'content_share_class' => $content_share_class,
832 'share_position_class' => $share_position_class,
833 'content_protection_class' => $content_protection_class,
834 'alignment' => $alignment,
835 'ads_attrs' => $ads_attrs,
836 'custom_branding' => $custom_branding,
837 'hosted_format' => $hosted_format,
838 'yt_channel_class' => $yt_channel_class,
839 'auto_pause' => $auto_pause,
840 'pass_hash_key' => isset($attributes['contentPassword']) ? md5($attributes['contentPassword']) : '',
841 ];
842 }
843
844 /**
845 * Get alignment CSS class
846 *
847 * @param string $align Alignment value
848 * @return string CSS class
849 */
850 private static function get_alignment_class($align)
851 {
852 return isset(self::$alignment_classes[$align])
853 ? self::$alignment_classes[$align] . ' clear'
854 : 'aligncenter';
855 }
856
857 /**
858 * Build ad manager attributes
859 *
860 * @param array $attributes Block attributes
861 * @param string $client_id Client ID
862 * @return string Ad attributes
863 */
864 private static function build_ads_attributes($attributes, $client_id)
865 {
866 if (empty($attributes['adManager'])) {
867 return '';
868 }
869
870 $ad = base64_encode(json_encode($attributes));
871 return "data-sponsored-id=$client_id data-sponsored-attrs=$ad class=sponsored-mask";
872 }
873
874 /**
875 * Get hosted media format
876 *
877 * @param array $attributes Block attributes
878 * @return string Media format
879 */
880 private static function get_hosted_format($attributes)
881 {
882 if (empty($attributes['customPlayer'])) {
883 return '';
884 }
885
886 $self_hosted = Helper::check_media_format($attributes['url']);
887 return $self_hosted['format'] ?? '';
888 }
889
890 /**
891 * Build custom branding configuration
892 *
893 * @param array $attributes Block attributes
894 * @param string $client_id Client ID
895 * @return array Custom branding configuration
896 */
897 private static function build_custom_branding($attributes, $client_id)
898 {
899 $custom_branding = [
900 'html' => '',
901 'styles' => ''
902 ];
903
904 // Check if custom logo is enabled
905 if (empty($attributes['customlogo'])) {
906 return $custom_branding;
907 }
908
909 $logo_url = $attributes['customlogo'];
910 $logo_x = $attributes['logoX'] ?? 5;
911 $logo_y = $attributes['logoY'] ?? 10;
912 $logo_opacity = $attributes['logoOpacity'] ?? 1;
913 $custom_logo_url = $attributes['customlogoUrl'] ?? '';
914
915 // Generate custom logo styles
916 $custom_branding['styles'] = sprintf(
917 '#ep-gutenberg-content-%s img.watermark {
918 border: 0;
919 position: absolute;
920 bottom: %s%%;
921 right: %s%%;
922 max-width: 150px;
923 max-height: 75px;
924 -o-transition: opacity 0.5s ease-in-out;
925 -moz-transition: opacity 0.5s ease-in-out;
926 -webkit-transition: opacity 0.5s ease-in-out;
927 transition: opacity 0.5s ease-in-out;
928 z-index: 1;
929 opacity: %s;
930 }
931 #ep-gutenberg-content-%s img.watermark:hover {
932 opacity: 1;
933 }',
934 esc_attr($client_id),
935 esc_attr($logo_y),
936 esc_attr($logo_x),
937 esc_attr($logo_opacity),
938 esc_attr($client_id)
939 );
940
941 // Generate custom logo HTML
942 $logo_html = sprintf(
943 '<img decoding="async" src="%s" class="watermark ep-custom-logo" width="auto" height="auto" alt="">',
944 esc_url($logo_url)
945 );
946
947 // Wrap with link if URL is provided
948 if (!empty($custom_logo_url)) {
949 $logo_html = sprintf(
950 '<a href="%s" target="_blank">%s</a>',
951 esc_url($custom_logo_url),
952 $logo_html
953 );
954 }
955
956 $custom_branding['html'] = $logo_html;
957
958 return $custom_branding;
959 }
960
961 /**
962 * Generate the final HTML output
963 *
964 * @param array $attributes Block attributes
965 * @param string $embed Embed content
966 * @param array $config Basic configuration
967 * @param array $carousel_config Carousel configuration
968 * @param array $player_config Player configuration
969 * @param array $styling Styling configuration
970 * @param array $protection_data Protection data
971 * @param bool $should_display_content Whether content should be displayed
972 * @return string Final HTML output
973 */
974 private static function generate_final_html($attributes, $embed, $config, $carousel_config, $player_config, $styling, $protection_data, $should_display_content)
975 {
976 ob_start();
977
978 // Extract variables for template
979 $url = $config['url'];
980 $block_id = $config['block_id'];
981 $client_id = $protection_data['client_id'];
982 $content_id = $protection_data['content_id'];
983 $content_share = $protection_data['content_share'];
984
985 // Build wrapper classes
986 $wrapper_classes = self::build_wrapper_classes($styling, $config);
987 $embed_wrapper_classes = self::build_embed_wrapper_classes($attributes);
988 $content_wrapper_classes = self::build_content_wrapper_classes($attributes, $config, $styling);
989
990 ?>
991 <?php if (!empty($styling['custom_branding']['styles'])): ?>
992 <style>
993 <?php echo $styling['custom_branding']['styles']; ?>
994 </style>
995 <?php endif; ?>
996
997 <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); ?> ">
998 <div class="wp-block-embed__wrapper <?php echo esc_attr($embed_wrapper_classes); ?>">
999 <div id="ep-gutenberg-content-<?php echo esc_attr($client_id) ?>" class="ep-gutenberg-content<?php echo esc_attr($styling['auto_pause']); ?>">
1000 <div <?php echo esc_attr($styling['ads_attrs']); ?>>
1001 <div class="ep-embed-content-wraper <?php echo esc_attr($content_wrapper_classes); ?>"
1002 <?php echo esc_attr($player_config['custom_player']); ?>
1003 <?php echo esc_attr($player_config['player_options']); ?>
1004 <?php echo esc_attr($carousel_config['carousel_id']); ?>
1005 <?php echo esc_attr($carousel_config['carousel_options']); ?>>
1006
1007 <?php
1008 self::render_embed_content($embed, $content_share, $content_id, $attributes, $should_display_content, $protection_data, $styling);
1009 ?>
1010 </div>
1011
1012 <?php self::render_ad_template($attributes, $embed, $client_id); ?>
1013 </div>
1014 </div>
1015 </div>
1016 </div>
1017 <?php
1018
1019 return ob_get_clean();
1020 }
1021
1022 /**
1023 * Build wrapper CSS classes
1024 *
1025 * @param array $styling Styling configuration
1026 * @param array $config Basic configuration
1027 * @return string CSS classes
1028 */
1029 private static function build_wrapper_classes($styling, $config)
1030 {
1031 return trim(sprintf(
1032 '%s %s %s %s%s',
1033 $styling['alignment'],
1034 $styling['content_share_class'],
1035 $styling['share_position_class'],
1036 $styling['content_protection_class'],
1037 $config['embed_type']
1038 ));
1039 }
1040
1041 /**
1042 * Build embed wrapper CSS classes
1043 *
1044 * @param array $attributes Block attributes
1045 * @return string CSS classes
1046 */
1047 private static function build_embed_wrapper_classes($attributes)
1048 {
1049 $classes = [];
1050
1051 if (!empty($attributes['contentShare'])) {
1052 $share_position = $attributes['sharePosition'] ?? 'right';
1053 $classes[] = 'position-' . $share_position . '-wraper';
1054 }
1055
1056 if (($attributes['videosize'] ?? '') === 'responsive') {
1057 $classes[] = 'ep-video-responsive';
1058 }
1059
1060 return implode(' ', $classes);
1061 }
1062
1063 /**
1064 * Build content wrapper CSS classes
1065 *
1066 * @param array $attributes Block attributes
1067 * @param array $config Basic configuration
1068 * @param array $styling Styling configuration
1069 * @return string CSS classes
1070 */
1071 private static function build_content_wrapper_classes($attributes, $config, $styling)
1072 {
1073 return trim(sprintf(
1074 '%s%s%s %s %s',
1075 $attributes['playerPreset'] ?? '',
1076 $config['insta_layout'],
1077 $config['mode'],
1078 $styling['hosted_format'],
1079 $styling['yt_channel_class']
1080 ));
1081 }
1082
1083 /**
1084 * Render embed content with protection logic
1085 *
1086 * @param string $embed Embed content
1087 * @param string $content_share Content share setting
1088 * @param string $content_id Content ID
1089 * @param array $attributes Block attributes
1090 * @param bool $should_display_content Whether content should be displayed
1091 * @param array $protection_data Protection data
1092 * @param array $styling Styling configuration
1093 */
1094 private static function render_embed_content($embed, $content_share, $content_id, $attributes, $should_display_content, $protection_data, $styling)
1095 {
1096 if ($should_display_content) {
1097 self::render_displayable_content($embed, $content_share, $content_id, $attributes, $styling);
1098 } else {
1099 self::render_protected_content($embed, $content_share, $content_id, $attributes, $protection_data, $styling);
1100 }
1101 }
1102
1103 /**
1104 * Render displayable content
1105 *
1106 * @param string $embed Embed content
1107 * @param string $content_share Content share setting
1108 * @param string $content_id Content ID
1109 * @param array $attributes Block attributes
1110 * @param array $styling Styling configuration (optional)
1111 */
1112 private static function render_displayable_content($embed, $content_share, $content_id, $attributes, $styling = [])
1113 {
1114 // Add custom branding if available
1115 if (!empty($styling['custom_branding']['html'])) {
1116 if (is_array($embed)) {
1117 $embed['html'] .= $styling['custom_branding']['html'];
1118 } else {
1119 $embed .= $styling['custom_branding']['html'];
1120 }
1121 }
1122
1123 if (!empty($content_share)) {
1124 $embed .= Helper::embed_content_share($content_id, $attributes);
1125 }
1126
1127 if (is_array($embed)) {
1128 echo $embed['html'];
1129 } else {
1130 echo $embed;
1131 }
1132 }
1133
1134 /**
1135 * Render protected content
1136 *
1137 * @param string $embed Embed content
1138 * @param string $content_share Content share setting
1139 * @param string $content_id Content ID
1140 * @param array $attributes Block attributes
1141 * @param array $protection_data Protection data
1142 * @param array $styling Styling configuration
1143 */
1144 private static function render_protected_content($embed, $content_share, $content_id, $attributes, $protection_data, $styling)
1145 {
1146 // Add custom branding if available
1147 if (!empty($styling['custom_branding']['html'])) {
1148 if (is_array($embed)) {
1149 $embed['html'] .= $styling['custom_branding']['html'];
1150 } else {
1151 $embed .= $styling['custom_branding']['html'];
1152 }
1153 }
1154
1155 if (!empty($content_share)) {
1156 $embed .= Helper::embed_content_share($content_id, $attributes);
1157 }
1158
1159 if ($protection_data['protection_type'] === 'password') {
1160 do_action('embedpress/display_password_form', $protection_data['client_id'], $embed, $styling['pass_hash_key'], $attributes);
1161 } else {
1162 do_action('embedpress/content_protection_content', $protection_data['client_id'], $protection_data['protection_message'], $protection_data['user_role']);
1163 }
1164 }
1165
1166 /**
1167 * Render ad template if enabled
1168 *
1169 * @param array $attributes Block attributes
1170 * @param string $embed Embed content
1171 * @param string $client_id Client ID
1172 */
1173 private static function render_ad_template($attributes, $embed, $client_id)
1174 {
1175 if (!empty($attributes['adManager'])) {
1176 $embed = apply_filters('embedpress/generate_ad_template', $embed, $client_id, $attributes, 'gutenberg');
1177 }
1178 }
1179
1180 /**
1181 * YouTube block legacy render method
1182 *
1183 * @param array $attributes Block attributes
1184 * @param string $content Block content
1185 * @param object $block Block object (unused but kept for compatibility)
1186 * @return string Rendered HTML content
1187 */
1188 public static function render_youtube_block($attributes, $content = '', $block = null)
1189 {
1190
1191 if (!empty($content)) {
1192 return $content;
1193 }
1194 // Extract basic attributes for YouTube block
1195 $iframe_src = $attributes['iframeSrc'] ?? '';
1196 $align = $attributes['align'] ?? 'center';
1197
1198 // If no iframe source is provided, return empty
1199 if (empty($iframe_src)) {
1200 return '';
1201 }
1202
1203 // Validate YouTube URL
1204 if (!self::is_youtube_url($iframe_src)) {
1205 return '';
1206 }
1207
1208 // Apply YouTube parameters filter
1209 $youtube_params = apply_filters('embedpress_gutenberg_youtube_params', []);
1210 $processed_iframe_url = $iframe_src;
1211
1212 foreach ($youtube_params as $param => $value) {
1213 $processed_iframe_url = add_query_arg($param, $value, $processed_iframe_url);
1214 }
1215
1216 // Build alignment class
1217 $align_class = 'align' . $align;
1218
1219 // Generate YouTube block HTML
1220 ob_start();
1221 ?>
1222 <div class="ose-youtube wp-block-embed-youtube ose-youtube-single-video <?php echo esc_attr($align_class); ?>">
1223 <iframe src="<?php echo esc_url($processed_iframe_url); ?>"
1224 allowtransparency="true"
1225 allowfullscreen="true"
1226 frameborder="0"
1227 width="640" height="360">
1228 </iframe>
1229 </div>
1230 <?php
1231 return ob_get_clean();
1232 }
1233
1234 /**
1235 * Validate if URL is a YouTube URL
1236 *
1237 * @param string $url URL to validate
1238 * @return bool True if valid YouTube URL, false otherwise
1239 */
1240 private static function is_youtube_url($url)
1241 {
1242 $pattern = '/^(https?:\/\/)?(www\.)?(youtube\.com\/(watch\?(.*&)?v=|(embed|v)\/))|youtu.be\/([a-zA-Z0-9_-]{11})/';
1243 return preg_match($pattern, $url);
1244 }
1245
1246 /**
1247 * Wistia block legacy render method
1248 *
1249 * @param array $attributes Block attributes
1250 * @param string $content Block content
1251 * @param object $block Block object (unused but kept for compatibility)
1252 * @return string Rendered HTML content
1253 */
1254 public static function render_wistia_block($attributes, $content = '', $block = null)
1255 {
1256 if (!empty($content)) {
1257 return $content;
1258 }
1259
1260 // Extract basic attributes for Wistia block
1261 $url = $attributes['url'] ?? '';
1262 $iframe_src = $attributes['iframeSrc'] ?? '';
1263 $align = $attributes['align'] ?? 'center';
1264
1265 // If no URL is provided, return empty
1266 if (empty($url)) {
1267 return '';
1268 }
1269
1270 // Extract Wistia media ID from URL
1271 $wistia_id = self::extract_wistia_id($url);
1272 if (empty($wistia_id)) {
1273 return '';
1274 }
1275
1276 // Build alignment class
1277 $align_class = 'align' . $align;
1278
1279 // Generate Wistia block HTML
1280 ob_start();
1281 ?>
1282 <div class="ose-wistia wp-block-embed-youtube <?php echo esc_attr($align_class); ?>" id="wistia_<?php echo esc_attr($wistia_id); ?>">
1283 <iframe src="<?php echo esc_url($iframe_src); ?>"
1284 allowtransparency="true"
1285 frameborder="0"
1286 class="wistia_embed"
1287 name="wistia_embed"
1288 width="600"
1289 height="330">
1290 </iframe>
1291 <?php do_action('embedpress_gutenberg_wistia_block_after_embed', $attributes); ?>
1292 </div>
1293 <?php
1294 return ob_get_clean();
1295 }
1296
1297 /**
1298 * Extract Wistia media ID from URL
1299 *
1300 * @param string $url Wistia URL
1301 * @return string|false Wistia media ID or false if not found
1302 */
1303 private static function extract_wistia_id($url)
1304 {
1305 preg_match('~medias/(.*)~i', esc_url($url), $matches);
1306 return isset($matches[1]) ? $matches[1] : false;
1307 }
1308
1309 /**
1310 * Calendar block render method
1311 *
1312 * @param array $attributes Block attributes
1313 * @param string $content Block content
1314 * @param object $block Block object (unused but kept for compatibility)
1315 * @return string Rendered HTML content
1316 */
1317 public static function render_embedpress_calendar($attributes, $content = '', $block = null)
1318 {
1319 if (!empty($content)) {
1320 return $content;
1321 }
1322
1323 // Extract basic attributes for Calendar block
1324 $url = $attributes['url'] ?? '';
1325 $width = $attributes['width'] ?? '600';
1326 $height = $attributes['height'] ?? '600';
1327 $powered_by = $attributes['powered_by'] ?? false;
1328 $is_public = $attributes['is_public'] ?? true;
1329 $align = $attributes['align'] ?? 'center';
1330
1331 // If no URL is provided, return empty
1332 if (empty($url)) {
1333 return '';
1334 }
1335
1336 // Validate Google Calendar URL
1337 if (!self::is_google_calendar_url($url)) {
1338 return '<p class="embedpress-el-powered">' . esc_html__('Invalid Calendar Link', 'embedpress') . '</p>';
1339 }
1340
1341 // Build alignment class
1342 $align_class = 'align' . $align;
1343
1344 // Sanitize URL
1345 $sanitized_url = esc_url($url);
1346
1347 // Generate Calendar block HTML
1348 ob_start();
1349 ?>
1350 <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;">
1351 <?php if ($is_public && self::is_google_calendar_url($url)) : ?>
1352 <iframe src="<?php echo esc_url($sanitized_url); ?>"
1353 width="<?php echo esc_attr($width); ?>"
1354 height="<?php echo esc_attr($height); ?>"
1355 frameborder="0"
1356 scrolling="no"
1357 title="<?php echo esc_attr(self::get_iframe_title_from_url($url)); ?>">
1358 </iframe>
1359 <?php endif; ?>
1360
1361 <?php if ($powered_by && self::is_google_calendar_url($url)) : ?>
1362 <p class="embedpress-el-powered"><?php echo esc_html__('Powered By EmbedPress', 'embedpress'); ?></p>
1363 <?php endif; ?>
1364 </figure>
1365 <?php
1366 return ob_get_clean();
1367 }
1368
1369 /**
1370 * Validate if URL is a Google Calendar URL
1371 *
1372 * @param string $url URL to validate
1373 * @return bool True if valid Google Calendar URL, false otherwise
1374 */
1375 private static function is_google_calendar_url($url)
1376 {
1377 $pattern = '/^https:\/\/calendar\.google\.com\/calendar\/(?:u\/\d+\/)?embed\?.*/';
1378 return preg_match($pattern, $url);
1379 }
1380
1381 /**
1382 * Get iframe title from URL
1383 *
1384 * @param string $url URL to derive title from
1385 * @return string Derived title
1386 */
1387 private static function get_iframe_title_from_url($url)
1388 {
1389 if (empty($url)) {
1390 return '';
1391 }
1392
1393 // Try getting title from WordPress attachment if it's a local file
1394 $file_title = Helper::get_file_title($url);
1395 if (!empty($file_title)) {
1396 return $file_title;
1397 }
1398
1399 // Try to get filename from URL
1400 $path = parse_url($url, PHP_URL_PATH);
1401 if ($path) {
1402 $filename = basename($path);
1403 // Remove extension
1404 $filename = preg_replace('/\.[^.]+$/', '', $filename);
1405 // Decode URL encoding
1406 $filename = urldecode($filename);
1407 // Replace hyphens/underscores with spaces
1408 $filename = str_replace(['-', '_'], ' ', $filename);
1409
1410 if (!empty($filename)) {
1411 return ucfirst($filename);
1412 }
1413 }
1414
1415 // Fallback to domain name
1416 $host = parse_url($url, PHP_URL_HOST);
1417 if ($host) {
1418 return $host;
1419 }
1420
1421 return $url;
1422 }
1423 }
1424 ?>