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