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 / Providers / GooglePhotos.php
embedpress / EmbedPress / Providers Last commit date
TemplateLayouts 8 months ago AirTable.php 1 year ago Boomplay.php 1 year ago Calendly.php 2 years ago Canva.php 1 year ago FITE.php 1 year ago GettyImages.php 9 months ago Giphy.php 2 years ago GitHub.php 3 years ago GoogleCalendar.php 8 months ago GoogleDocs.php 2 years ago GoogleDrive.php 2 years ago GoogleMaps.php 2 years ago GooglePhotos.php 7 months ago Gumroad.php 2 years ago InstagramFeed.php 6 months ago LinkedIn.php 2 years ago Meetup.php 8 months ago NRKRadio.php 2 years ago OneDrive.php 10 months ago OpenSea.php 9 months ago SelfHosted.php 2 years ago Spreaker.php 1 year ago TikTok.php 2 years ago Twitch.php 2 years ago Wrapper.php 3 years ago X.php 2 years ago Youtube.php 9 months ago index.html 7 years ago
GooglePhotos.php
491 lines
1 <?php
2
3 namespace EmbedPress\Providers;
4
5 use EmbedPress\Includes\Classes\Helper;
6 use Embera\Provider\ProviderAdapter;
7 use Embera\Provider\ProviderInterface;
8 use Embera\Url;
9
10 (defined('ABSPATH') && defined('EMBEDPRESS_IS_LOADED')) or die("No direct script access allowed.");
11
12 class GooglePhotos extends ProviderAdapter implements ProviderInterface
13 {
14 protected static $hosts = ["photos.app.goo.gl", "photos.google.com"];
15 private $player_js = "https://cdn.jsdelivr.net/npm/publicalbum@1.3.4/embed-ui.min.js"; //EMBEDPRESS_URL_ASSETS . "js/embed-ui.min.js";
16 private $min_expiration = 0;
17 private $allowed_url_patttern = "/^https:\/\/photos\.app\.goo\.gl\/|^https:\/\/photos\.google\.com(?:\/u\/\d+)?\/share\//";
18
19 static public $name = "google-photos-album";
20
21 /** @var array Array with allowed params for the current Provider */
22 protected $allowedParams = [
23 'mode',
24 'maxwidth',
25 'google_photos_width',
26 'google_photos_height',
27 'maxheight',
28 'imageWidth',
29 'imageHeight',
30 'playerAutoplay',
31 'delay',
32 'repeat',
33 'mediaitemsAspectRatio',
34 'mediaitemsEnlarge',
35 'mediaitemsStretch',
36 'mediaitemsCover',
37 'backgroundColor',
38 'expiration',
39 'photos_link',
40 ];
41
42
43 /** inline {@inheritdoc} */
44 protected $httpsSupport = true;
45
46 public function getAllowedParams()
47 {
48 return $this->allowedParams;
49 }
50
51 public function __construct($url, array $config = [])
52 {
53 parent::__construct($url, $config);
54 add_filter('embedpress_render_dynamic_content', [$this, 'fakeDynamicResponse'], 10, 2);
55 }
56
57 public function validateUrl(Url $url)
58 {
59 return preg_match('~^https:\/\/(photos\.app\.goo\.gl|photos\.google\.com)\/.*$~i', (string) $url);
60 }
61
62 public function get_embeded_content($link, $width = 0, $height = 480, $imageWidth = 1920, $imageHeight = 1080, $expiration = 60, $mode = 'gallery-player', $playerAutoplay = false, $delay = 5, $repeat = true, $aspectRatio = true, $enlarge = true, $stretch = true, $cover = false, $backgroundColor = '#000000', $photos_link = true)
63 {
64 if (is_object($link)) {
65 return $this->get_html($link, $expiration);
66 }
67
68 $props = $this->create_default_attr();
69 $props->link = $link;
70 $props->width = $width;
71 $props->height = $height;
72 $props->imageWidth = $imageWidth;
73 $props->imageHeight = $imageHeight;
74 $props->mode = $mode;
75 $props->slideshowAutoplay = $playerAutoplay;
76 $props->slideshowDelay = $delay;
77 $props->repeat = $repeat;
78 $props->backgroundColor = $backgroundColor;
79 $props->photos_link = $photos_link;
80
81 $html = $this->get_html($props, $expiration);
82
83 return $html;
84 }
85
86 private function build_google_photos_html($props, $photos, $title = '')
87 {
88 $style = sprintf(
89 'display: none; width: %s; height: %s; max-width: 100%%;',
90 $props->width === 0 ? '100%' : ($props->width . 'px'),
91 $props->height === 0 ? '100%' : ($props->height . 'px')
92 );
93
94 $items_code = '';
95
96 if (in_array($props->mode, ['gallery-justify', 'gallery-masonary', 'gallery-grid'], true)) {
97 $items_code .= sprintf('<div class="photos-%s">', esc_attr($props->mode));
98 $counter = 0;
99
100 foreach ($photos as $photo) {
101 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
102 $full_src = $photo . '=w2500';
103
104 $items_code .= sprintf(
105 '<div class="photo-item" data-item-number="%d" id="photo-%s">
106 <img data-photo-src="%s" src="%s" loading="lazy" alt="Photo"/>
107 </div>',
108 esc_attr($counter++),
109 md5($preview_src),
110 esc_url($full_src),
111 esc_url($preview_src)
112 );
113 }
114
115 $items_code .= '</div>';
116
117 return sprintf(
118 "<div class=\"google-photos-%s-widget\"><h3 style='text-align:left; margin: 22px 10px;'>%s</h3>%s</div>\n",
119 esc_attr($props->mode),
120 esc_html($title),
121 $items_code
122 );
123 } else {
124 foreach ($photos as $photo) {
125 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
126 $items_code .= sprintf('<object data="%s"></object>', esc_url($preview_src));
127 }
128 }
129
130 // Metadata for embedding
131 $attributes = [
132 'data-link' => $props->link,
133 'data-found' => count($photos),
134 'data-title' => $title,
135 'data-mediaitems-aspect-ratio' => true,
136 'data-mediaitems-enlarge' => '',
137 'data-mediaitems-stretch' => '',
138 'data-mediaitems-cover' => '',
139 'data-background-color' => $props->backgroundColor,
140 'data-photos-link' => isset($props->photos_link) ? ($props->photos_link ? 'true' : 'false') : 'true',
141 ];
142
143 $attributes = apply_filters('embedpress_google_photos_attributes', $attributes, $props);
144
145 // Add external link icon if photos_link is enabled and mode is gallery-player or carousel
146 $external_link_html = '';
147
148 if (
149 isset($props->photos_link) &&
150 $props->photos_link &&
151 in_array($props->mode, ['gallery-player', 'carousel']) &&
152 $props->photos_link !== 'false'
153 ) {
154 $external_link_html = sprintf(
155 '<a href="%s" target="_blank" rel="noopener noreferrer"
156 class="ep-google-photos-visit-btn"
157 style="position: absolute; top: 16px; right: 12px; background: rgb(0, 0, 0); color: white; border: none; border-radius: 50px; font-size: 12px; display: flex; align-items: center; justify-content: center; gap: 4px; z-index: 1000; text-decoration: none; width: 40px; height: 40px;">
158 <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
159 <path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
160 </svg>
161 </a>',
162 esc_url($props->link)
163 );
164 }
165
166 $attributes_string = '';
167 foreach ($attributes as $key => $value) {
168 if ($value !== null) {
169 $attributes_string .= sprintf(' %s="%s"', $key, $value);
170 }
171 }
172
173
174 return sprintf(
175 "<div class=\"pa-%s-widget\" style=\"%s\" %s>%s</div>%s\n",
176 esc_attr($props->mode),
177 $style,
178 $attributes_string,
179 $items_code,
180 $external_link_html
181 );
182 }
183
184 private function get_html($props, $expiration = 60)
185 {
186 $url_hash = md5($props->link);
187 $data_transient_key = sprintf('%s-data-%s', self::$name, $url_hash);
188 $option_expiration_key = sprintf('%s-expiration-%s', self::$name, $url_hash);
189
190 // Get cached data from transient
191 $data = get_transient($data_transient_key);
192
193 // Get previously stored expiration from options
194 $prev_expiration = get_option($option_expiration_key, 0);
195
196 $should_refresh = false;
197
198 // Refresh if no data or expiration changed
199 if (empty($data) || $prev_expiration != $expiration) {
200 $should_refresh = true;
201 }
202
203 if ($should_refresh) {
204 $contents = $this->get_remote_contents($props->link);
205 if (!$contents) {
206 return null;
207 }
208
209 $og = $this->parse_ogtags($contents);
210 $photos = $this->parse_photos($contents);
211
212 $data = [
213 'title' => $og['og:title'] ?? null,
214 'photos' => $photos,
215 ];
216
217 // Cache data transient with current expiration
218 set_transient($data_transient_key, $data, $expiration);
219
220 // Store current expiration persistently in options
221 update_option($option_expiration_key, $expiration);
222 }
223 return $this->build_google_photos_html($props, $data['photos'], $data['title']);
224 }
225
226
227 private function get_remote_contents($url)
228 {
229 if (preg_match($this->allowed_url_patttern, $url)) {
230 $response = wp_remote_get($url);
231 if (!is_wp_error($response)) {
232 return wp_remote_retrieve_body($response);
233 }
234 }
235 return null;
236 }
237
238 private function parse_ogtags($contents)
239 {
240 preg_match_all('~<\s*meta\s+property="(og:[^"]+)"\s+content="([^"]*)~i', $contents, $m);
241 $ogtags = [];
242 for ($i = 0; $i < count($m[1]); $i++) {
243 $ogtags[$m[1][$i]] = $m[2][$i];
244 }
245 return $ogtags;
246 }
247
248 private function parse_photos($contents)
249 {
250 preg_match_all('~\"(http[^"]+)\"\,[0-9^,]+\,[0-9^,]+~i', $contents, $m);
251 $photos = array_unique($m[1]);
252
253 // Use preg_replace_callback to remove width/height parameters directly in the matched URLs
254 return preg_replace_callback('/=[^&]+$/', function ($matches) {
255 return ''; // Remove the width/height parameters at the end
256 }, $photos);
257 }
258
259
260 private function get_embed_google_photos_html($props)
261 {
262 if ($contents = $this->get_remote_contents($props->link)) {
263 $og = $this->parse_ogtags($contents);
264 $title = $og['og:title'] ?? null;
265 $photos = $this->parse_photos($contents);
266
267
268 $style = sprintf(
269 'display: none; width: %s; height: %s; max-width: 100%%;',
270 $props->width === 0 ? '100%' : ($props->width . 'px'),
271 $props->height === 0 ? '100%' : ('100%')
272 );
273
274 $items_code = '';
275
276 if ($props->mode == 'gallery-justify' || $props->mode == 'gallery-masonary' || $props->mode == 'gallery-grid') {
277 $items_code .= sprintf('<div class="photos-%s">', esc_attr($props->mode));
278 $counter = 0;
279
280 foreach ($photos as $photo) {
281 // Preview image URL (small version for fast loading)
282 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
283
284 // Full image URL (original size or any desired size)
285 $full_src = $photo . '=w2500';
286
287 // Add image items with preview and full image source
288 $items_code .= sprintf(
289 '<div class="photo-item" data-item-number="%d" id="photo-%s">
290 <img data-photo-src="%s" src="%s" loading="lazy" alt="Photo"/>
291 </div>',
292 esc_attr($counter++),
293 md5($preview_src),
294 esc_url($full_src), // Full image source for later use
295 esc_url($preview_src) // Preview image source for fast loading
296 );
297 }
298
299 $items_code .= '</div>';
300
301 return sprintf(
302 "<div class=\"google-photos-%s-widget\"><h3 style='text-align:left; margin: 22px 10px;'>%s</h3>%s</div>\n",
303 esc_attr($props->mode),
304 $title,
305 $items_code
306 );
307 } else {
308 foreach ($photos as $photo) {
309 // Preview image URL (small version for fast loading)
310 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
311
312 // Full image URL (original size or any desired size)
313 $full_src = $photo;
314
315 // Add image items with preview and full image source
316 $items_code .= sprintf(
317 '<object data="%s"></object>',
318 esc_url($preview_src) // Using preview image for non-gallery mode
319 );
320 }
321 }
322
323
324 $attributes = [
325 'data-link' => $props->link,
326 'data-found' => count($photos),
327 'data-title' => $title,
328 'data-mediaitems-aspect-ratio' => true,
329 'data-mediaitems-enlarge' => '',
330 'data-mediaitems-stretch' => '',
331 'data-mediaitems-cover' => '',
332 'data-background-color' => $props->backgroundColor,
333 ];
334
335 // Apply filter to allow modification of attributes
336 $attributes = apply_filters('embedpress_google_photos_attributes', $attributes, $props);
337
338 $attributes_string = '';
339 foreach ($attributes as $key => $value) {
340 if ($value !== null) {
341 $attributes_string .= sprintf(' %s="%s"', $key, $value);
342 }
343 }
344
345 return sprintf(
346 "<div class=\"pa-%s-widget\" style=\"%s\"%s>%s</div>\n",
347 esc_attr($props->mode),
348 $style,
349 $attributes_string,
350 $items_code
351 );
352 }
353 return null;
354 }
355
356 private function create_default_attr()
357 {
358 $props = new \stdClass();
359 $props->mode = 'gallery-player';
360 $props->width = 600;
361 $props->height = 450;
362 $props->imageWidth = 1920;
363 $props->imageHeight = 1080;
364 $props->slideshowAutoplay = false;
365 $props->slideshowDelay = 5;
366 $props->repeat = true;
367 $props->mediaitemsAspectRatio = true;
368 $props->mediaitemsEnlarge = true;
369 $props->mediaitemsStretch = true;
370 $props->mediaitemsCover = false;
371 $props->backgroundColor = '#000000';
372 $props->photos_link = true;
373 return $props;
374 }
375
376 public function fakeDynamicResponse($html, $params)
377 {
378
379 return $html;
380
381 $src_url = urldecode($params['url']);
382
383 $width = $params['width'] ?? 600;
384 $height = $params['height'] ?? 450;
385
386 $expiration = $params['expiration'] ?? 60;
387 $mode = $params['mode'] ?? 'carousel';
388 $playerAutoplay = isset($params['playerAutoplay']) ? Helper::getBooleanParam($params['playerAutoplay']) : false;
389 $delay = $params['delay'] ?? 5;
390 $repeat = isset($params['repeat']) ? Helper::getBooleanParam($params['repeat']) : false;
391 $aspectRatio = isset($params['mediaitemsAspectRatio']) ? Helper::getBooleanParam($params['mediaitemsAspectRatio']) : false;
392 $enlarge = isset($params['mediaitemsEnlarge']) ? Helper::getBooleanParam($params['mediaitemsEnlarge']) : false;
393 $stretch = isset($params['mediaitemsStretch']) ? Helper::getBooleanParam($params['mediaitemsStretch']) : false;
394 $cover = isset($params['mediaitemsCover']) ? Helper::getBooleanParam($params['mediaitemsCover']) : false;
395 $backgroundColor = $params['backgroundColor'] ?? '#000000';
396
397 $html = $this->get_embeded_content(
398 $src_url,
399 $width,
400 $height,
401 $width,
402 $height,
403 $expiration,
404 $mode,
405 $playerAutoplay,
406 $delay,
407 $repeat,
408 $aspectRatio,
409 $enlarge,
410 $stretch,
411 $cover,
412 $backgroundColor
413 );
414
415 // Conditionally load player JS only if mode is 'carousel' or autoplay is enabled
416 if ($mode === 'carousel' || $mode === 'gallery-player') {
417 $html .= '<script src="' . $this->player_js . '"></script>';
418 }
419 if ($mode === 'gallery-justify') {
420 $html .= '<script src="' . EMBEDPRESS_PLUGIN_DIR_URL . 'assets/js/gallery-justify.js"></script>';
421 }
422 return $html;
423 }
424
425 public function fakeResponse()
426 {
427 $params = $this->getParams();
428
429 $src_url = urldecode($params['url']);
430
431 $width = $params['maxwidth'] ?? $params['width'] ?? 600;
432 $height = $params['maxheight'] ?? $params['height'] ?? 450;
433
434
435
436 $expiration = $params['expiration'] ?? 60;
437 $mode = $params['mode'] ?? 'carousel';
438 $playerAutoplay = isset($params['playerAutoplay']) ? Helper::getBooleanParam($params['playerAutoplay']) : false;
439 $delay = $params['delay'] ?? 5;
440 $repeat = isset($params['repeat']) ? Helper::getBooleanParam($params['repeat']) : false;
441 $aspectRatio = isset($params['mediaitemsAspectRatio']) ? Helper::getBooleanParam($params['mediaitemsAspectRatio']) : false;
442 $enlarge = isset($params['mediaitemsEnlarge']) ? Helper::getBooleanParam($params['mediaitemsEnlarge']) : false;
443 $stretch = isset($params['mediaitemsStretch']) ? Helper::getBooleanParam($params['mediaitemsStretch']) : false;
444 $cover = isset($params['mediaitemsCover']) ? Helper::getBooleanParam($params['mediaitemsCover']) : false;
445 $backgroundColor = $params['backgroundColor'] ?? '#000000';
446 $photos_link = isset($params['photos_link']) ? Helper::getBooleanParam($params['photos_link']) : true;
447
448 $html = $this->get_embeded_content(
449 $src_url,
450 $width,
451 $height,
452 $width,
453 $height,
454 $expiration,
455 $mode,
456 $playerAutoplay,
457 $delay,
458 $repeat,
459 $aspectRatio,
460 $enlarge,
461 $stretch,
462 $cover,
463 $backgroundColor,
464 $photos_link
465 );
466
467 // Conditionally load player JS only if mode is 'carousel' or autoplay is enabled
468 if ($mode === 'carousel' || $mode === 'gallery-player') {
469 $html .= '<script src="' . $this->player_js . '"></script>';
470 }
471 if ($mode === 'gallery-justify') {
472 $html .= '<script src="' . EMBEDPRESS_PLUGIN_DIR_URL . 'assets/js/gallery-justify.js"></script>';
473 }
474
475 // Always load gallery layout script (or make this conditional too if needed)
476
477 return [
478 'type' => 'rich',
479 'provider_name' => 'Google Photos',
480 'provider_url' => 'https://photos.app.goo.gl',
481 'title' => $params['title'] ?? 'Unknown title',
482 'html' => $html,
483 ];
484 }
485
486 public function modifyResponse(array $response = [])
487 {
488 return $this->fakeResponse();
489 }
490 }
491