PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 4.2.8
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v4.2.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 / Providers / GooglePhotos.php
embedpress / EmbedPress / Providers Last commit date
TemplateLayouts 1 year 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 Giphy.php 2 years ago GitHub.php 3 years ago GoogleDocs.php 2 years ago GoogleDrive.php 2 years ago GoogleMaps.php 2 years ago GooglePhotos.php 1 year ago Gumroad.php 2 years ago InstagramFeed.php 1 year ago LinkedIn.php 2 years ago NRKRadio.php 2 years ago OneDrive.php 1 year ago OpenSea.php 2 years 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 1 year ago index.html 7 years ago
GooglePhotos.php
418 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@latest/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 ];
40
41
42 /** inline {@inheritdoc} */
43 protected $httpsSupport = true;
44
45 public function getAllowedParams()
46 {
47 return $this->allowedParams;
48 }
49
50 public function __construct($url, array $config = [])
51 {
52 parent::__construct($url, $config);
53 }
54
55 public function validateUrl(Url $url)
56 {
57 return preg_match('~^https:\/\/(photos\.app\.goo\.gl|photos\.google\.com)\/.*$~i', (string) $url);
58 }
59
60 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')
61 {
62 if (is_object($link)) {
63 return $this->get_html($link, $expiration);
64 }
65
66 $props = $this->create_default_attr();
67 $props->link = $link;
68 $props->width = $width;
69 $props->height = $height;
70 $props->imageWidth = $imageWidth;
71 $props->imageHeight = $imageHeight;
72 $props->mode = $mode;
73 $props->slideshowAutoplay = $playerAutoplay;
74 $props->slideshowDelay = $delay;
75 $props->repeat = $repeat;
76 $props->backgroundColor = $backgroundColor;
77
78 $html = $this->get_html($props, $expiration);
79
80 return $html;
81 }
82
83 private function build_google_photos_html($props, $photos, $title = '')
84 {
85 $style = sprintf(
86 'display: none; width: %s; height: %s; max-width: 100%%;',
87 $props->width === 0 ? '100%' : ($props->width . 'px'),
88 $props->height === 0 ? '100%' : '100%'
89 );
90
91 $items_code = '';
92
93 if (in_array($props->mode, ['gallery-justify', 'gallery-masonary', 'gallery-grid'], true)) {
94 $items_code .= sprintf('<div class="photos-%s">', esc_attr($props->mode));
95 $counter = 0;
96
97 foreach ($photos as $photo) {
98 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
99 $full_src = $photo . '=w2500';
100
101 $items_code .= sprintf(
102 '<div class="photo-item" data-item-number="%d" id="photo-%s">
103 <img data-photo-src="%s" src="%s" loading="lazy" alt="Photo"/>
104 </div>',
105 esc_attr($counter++),
106 md5($preview_src),
107 esc_url($full_src),
108 esc_url($preview_src)
109 );
110 }
111
112 $items_code .= '</div>';
113
114 return sprintf(
115 "<div class=\"google-photos-%s-widget\"><h3 style='text-align:left; margin: 22px 10px;'>%s</h3>%s</div>\n",
116 esc_attr($props->mode),
117 esc_html($title),
118 $items_code
119 );
120 } else {
121 foreach ($photos as $photo) {
122 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
123 $items_code .= sprintf('<object data="%s"></object>', esc_url($preview_src));
124 }
125 }
126
127 // Metadata for embedding
128 $attributes = [
129 'data-link' => $props->link,
130 'data-found' => count($photos),
131 'data-title' => $title,
132 'data-mediaitems-aspect-ratio' => true,
133 'data-mediaitems-enlarge' => '',
134 'data-mediaitems-stretch' => '',
135 'data-mediaitems-cover' => '',
136 'data-background-color' => $props->backgroundColor,
137 ];
138
139 $attributes = apply_filters('embedpress_google_photos_attributes', $attributes, $props);
140
141 $attributes_string = '';
142 foreach ($attributes as $key => $value) {
143 if ($value !== null) {
144 $attributes_string .= sprintf(' %s="%s"', $key, $value);
145 }
146 }
147
148 return sprintf(
149 "<div class=\"pa-%s-widget\" style=\"%s\"%s>%s</div>\n",
150 esc_attr($props->mode),
151 $style,
152 $attributes_string,
153 $items_code
154 );
155 }
156
157 private function get_html($props, $expiration = 60)
158 {
159 $url_hash = md5($props->link);
160 $data_transient_key = sprintf('%s-data-%s', self::$name, $url_hash);
161 $option_expiration_key = sprintf('%s-expiration-%s', self::$name, $url_hash);
162
163 // Get cached data from transient
164 $data = get_transient($data_transient_key);
165
166 // Get previously stored expiration from options
167 $prev_expiration = get_option($option_expiration_key, 0);
168
169 $should_refresh = false;
170
171 // Refresh if no data or expiration changed
172 if (empty($data) || $prev_expiration != $expiration) {
173 $should_refresh = true;
174 }
175
176 if ($should_refresh) {
177 $contents = $this->get_remote_contents($props->link);
178 if (!$contents) {
179 return null;
180 }
181
182 $og = $this->parse_ogtags($contents);
183 $photos = $this->parse_photos($contents);
184
185 $data = [
186 'title' => $og['og:title'] ?? null,
187 'photos' => $photos,
188 ];
189
190 // Cache data transient with current expiration
191 set_transient($data_transient_key, $data, $expiration);
192
193 // Store current expiration persistently in options
194 update_option($option_expiration_key, $expiration);
195 }
196
197 return $this->build_google_photos_html($props, $data['photos'], $data['title']);
198 }
199
200
201
202
203 private function get_remote_contents($url)
204 {
205 if (preg_match($this->allowed_url_patttern, $url)) {
206 $response = wp_remote_get($url);
207 if (!is_wp_error($response)) {
208 return wp_remote_retrieve_body($response);
209 }
210 }
211 return null;
212 }
213
214 private function parse_ogtags($contents)
215 {
216 preg_match_all('~<\s*meta\s+property="(og:[^"]+)"\s+content="([^"]*)~i', $contents, $m);
217 $ogtags = [];
218 for ($i = 0; $i < count($m[1]); $i++) {
219 $ogtags[$m[1][$i]] = $m[2][$i];
220 }
221 return $ogtags;
222 }
223
224 private function parse_photos($contents)
225 {
226 preg_match_all('~\"(http[^"]+)\"\,[0-9^,]+\,[0-9^,]+~i', $contents, $m);
227 $photos = array_unique($m[1]);
228
229 // Use preg_replace_callback to remove width/height parameters directly in the matched URLs
230 return preg_replace_callback('/=[^&]+$/', function ($matches) {
231 return ''; // Remove the width/height parameters at the end
232 }, $photos);
233 }
234
235
236 private function get_embed_google_photos_html($props)
237 {
238 if ($contents = $this->get_remote_contents($props->link)) {
239 $og = $this->parse_ogtags($contents);
240 $title = $og['og:title'] ?? null;
241 $photos = $this->parse_photos($contents);
242
243
244 error_log(print_r($contents, true));
245
246 $style = sprintf(
247 'display: none; width: %s; height: %s; max-width: 100%%;',
248 $props->width === 0 ? '100%' : ($props->width . 'px'),
249 $props->height === 0 ? '100%' : ('100%')
250 );
251
252 $items_code = '';
253
254 if ($props->mode == 'gallery-justify' || $props->mode == 'gallery-masonary' || $props->mode == 'gallery-grid') {
255 $items_code .= sprintf('<div class="photos-%s">', esc_attr($props->mode));
256 $counter = 0;
257
258 foreach ($photos as $photo) {
259 // Preview image URL (small version for fast loading)
260 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
261
262 // Full image URL (original size or any desired size)
263 $full_src = $photo . '=w2500';
264
265 // Add image items with preview and full image source
266 $items_code .= sprintf(
267 '<div class="photo-item" data-item-number="%d" id="photo-%s">
268 <img data-photo-src="%s" src="%s" loading="lazy" alt="Photo"/>
269 </div>',
270 esc_attr($counter++),
271 md5($preview_src),
272 esc_url($full_src), // Full image source for later use
273 esc_url($preview_src) // Preview image source for fast loading
274 );
275 }
276
277 $items_code .= '</div>';
278
279 return sprintf(
280 "<div class=\"google-photos-%s-widget\"><h3 style='text-align:left; margin: 22px 10px;'>%s</h3>%s</div>\n",
281 esc_attr($props->mode),
282 $title,
283 $items_code
284 );
285 } else {
286 foreach ($photos as $photo) {
287 // Preview image URL (small version for fast loading)
288 $preview_src = sprintf('%s=w%d-h%d', $photo, $props->imageWidth, $props->imageHeight);
289
290 // Full image URL (original size or any desired size)
291 $full_src = $photo;
292
293 // Add image items with preview and full image source
294 $items_code .= sprintf(
295 '<object data="%s"></object>',
296 esc_url($preview_src) // Using preview image for non-gallery mode
297 );
298 }
299 }
300
301
302 $attributes = [
303 'data-link' => $props->link,
304 'data-found' => count($photos),
305 'data-title' => $title,
306 'data-mediaitems-aspect-ratio' => true,
307 'data-mediaitems-enlarge' => '',
308 'data-mediaitems-stretch' => '',
309 'data-mediaitems-cover' => '',
310 'data-background-color' => $props->backgroundColor,
311 ];
312
313 // Apply filter to allow modification of attributes
314 $attributes = apply_filters('embedpress_google_photos_attributes', $attributes, $props);
315
316 $attributes_string = '';
317 foreach ($attributes as $key => $value) {
318 if ($value !== null) {
319 $attributes_string .= sprintf(' %s="%s"', $key, $value);
320 }
321 }
322
323 return sprintf(
324 "<div class=\"pa-%s-widget\" style=\"%s\"%s>%s</div>\n",
325 esc_attr($props->mode),
326 $style,
327 $attributes_string,
328 $items_code
329 );
330 }
331 return null;
332 }
333
334 private function create_default_attr()
335 {
336 $props = new \stdClass();
337 $props->mode = 'gallery-player';
338 $props->width = 600;
339 $props->height = 450;
340 $props->imageWidth = 1920;
341 $props->imageHeight = 1080;
342 $props->slideshowAutoplay = false;
343 $props->slideshowDelay = 5;
344 $props->repeat = true;
345 $props->mediaitemsAspectRatio = true;
346 $props->mediaitemsEnlarge = true;
347 $props->mediaitemsStretch = true;
348 $props->mediaitemsCover = false;
349 $props->backgroundColor = '#000000';
350 return $props;
351 }
352
353 public function fakeResponse()
354 {
355 $src_url = urldecode($this->url);
356
357 // Fetch parameters
358 $params = $this->getParams();
359
360 // Extract configuration or set defaults
361 $width = isset($this->config['maxwidth']) ? $this->config['maxwidth'] : 600;
362 $height = isset($this->config['maxheight']) ? $this->config['maxheight'] : 450;
363
364
365 $expiration = $params['expiration'] ?? 60;
366 $mode = $params['mode'] ?? 'carousel';
367 $playerAutoplay = isset($params['playerAutoplay']) ? Helper::getBooleanParam($params['playerAutoplay']) : false;
368 $delay = $params['delay'] ?? 5;
369 $repeat = isset($params['repeat']) ? Helper::getBooleanParam($params['repeat']) : false;
370 $aspectRatio = isset($params['mediaitemsAspectRatio']) ? Helper::getBooleanParam($params['mediaitemsAspectRatio']) : false;
371 $enlarge = isset($params['mediaitemsEnlarge']) ? Helper::getBooleanParam($params['mediaitemsEnlarge']) : false;
372 $stretch = isset($params['mediaitemsStretch']) ? Helper::getBooleanParam($params['mediaitemsStretch']) : false;
373 $cover = isset($params['mediaitemsCover']) ? Helper::getBooleanParam($params['mediaitemsCover']) : false;
374 $backgroundColor = $params['backgroundColor'] ?? '#000000';
375
376 $html = $this->get_embeded_content(
377 $src_url,
378 $width,
379 $height,
380 $width,
381 $height,
382 $expiration,
383 $mode,
384 $playerAutoplay,
385 $delay,
386 $repeat,
387 $aspectRatio,
388 $enlarge,
389 $stretch,
390 $cover,
391 $backgroundColor
392 );
393
394 // Conditionally load player JS only if mode is 'carousel' or autoplay is enabled
395 if ($mode === 'carousel' || $mode === 'gallery-player') {
396 $html .= '<script src="' . $this->player_js . '"></script>';
397 }
398 if ($mode === 'gallery-justify') {
399 $html .= '<script src="' . EMBEDPRESS_PLUGIN_DIR_URL . 'assets/js/gallery-justify.js"></script>';
400 }
401
402 // Always load gallery layout script (or make this conditional too if needed)
403
404 return [
405 'type' => 'rich',
406 'provider_name' => 'Google Photos',
407 'provider_url' => 'https://photos.app.goo.gl',
408 'title' => $params['title'] ?? 'Unknown title',
409 'html' => $html,
410 ];
411 }
412
413 public function modifyResponse(array $response = [])
414 {
415 return $this->fakeResponse();
416 }
417 }
418