PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 1.6.0
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v1.6.0
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 / Shortcode.php
embedpress / EmbedPress Last commit date
Ends 9 years ago Plugins 9 years ago Providers 9 years ago AutoLoader.php 9 years ago Core.php 9 years ago Disabler.php 9 years ago Loader.php 9 years ago Shortcode.php 9 years ago Updater.php 9 years ago index.html 9 years ago
Shortcode.php
525 lines
1 <?php
2 namespace EmbedPress;
3
4 use \EmbedPress\Core;
5 use \Embera\Embera;
6 use \Embera\Formatter;
7
8 (defined('ABSPATH') && defined('EMBEDPRESS_IS_LOADED')) or die("No direct script access allowed.");
9
10 /**
11 * Entity responsible to handle the plugin's shortcode events and behaviors.
12 *
13 * @package EmbedPress
14 * @author PressShack <help@pressshack.com>
15 * @copyright Copyright (C) 2017 Open Source Training, LLC. All rights reserved.
16 * @license GPLv2 or later
17 * @since 1.0.0
18 */
19 class Shortcode
20 {
21 /**
22 * The WP_oEmbed class instance.
23 *
24 * @since 1.0.0
25 * @access private
26 * @static
27 *
28 * @var string $oEmbedInstance
29 */
30 private static $oEmbedInstance = null;
31
32 /**
33 * Register the plugin's shortcode into WordPress.
34 *
35 * @since 1.0.0
36 * @static
37 *
38 * @return void
39 */
40 public static function register()
41 {
42 // Register the new shortcode for embeds.
43 add_shortcode(EMBEDPRESS_SHORTCODE, array('\\EmbedPress\\Shortcode', 'do_shortcode'), 1);
44 }
45
46 /**
47 * Method that converts the plugin shortcoded-string into its complex content.
48 *
49 * @since 1.0.0
50 * @static
51 *
52 * @param array $attributes Array of attributes
53 * @param string $subject The given string
54 * @return string
55 */
56 public static function do_shortcode($attributes = array(), $subject = null)
57 {
58 $embed = self::parseContent($subject, true, $attributes);
59
60 return is_object($embed) ? $embed->embed : $embed;
61 }
62
63 /**
64 * Replace a given content with its embeded HTML code.
65 *
66 * @since 1.0.0
67 * @static
68 *
69 * @param string The raw content that will be replaced.
70 * @param boolean Optional. If true, new lines at the end of the embeded code are stripped.
71 * @return string
72 */
73 public static function parseContent($subject, $stripNewLine = false, $customAttributes = array())
74 {
75 if (!empty($subject)) {
76 if (empty($customAttributes)) {
77 $customAttributes = self::parseContentAttributesFromString($subject);
78 }
79
80 $content = preg_replace('/(\['. EMBEDPRESS_SHORTCODE .'(?:\]|.+?\])|\[\/'. EMBEDPRESS_SHORTCODE .'\])/i', "", $subject);
81
82 // Converts any special HTML entities back to characters.
83 $content = htmlspecialchars_decode($content);
84
85 // Check if the WP_oEmbed class is loaded
86 if (!self::$oEmbedInstance) {
87 require_once ABSPATH .'wp-includes/class-oembed.php';
88
89 self::$oEmbedInstance = _wp_oembed_get_object();
90 }
91
92 $emberaInstanceSettings = array(
93 'params' => array()
94 );
95
96 $attributes = self::parseContentAttributes($customAttributes);
97 if (isset($attributes['width']) || isset($attributes['height'])) {
98 if (isset($attributes['width'])) {
99 $emberaInstanceSettings['params']['width'] = $attributes['width'];
100 unset($attributes['width']);
101 }
102
103 if (isset($attributes['height'])) {
104 $emberaInstanceSettings['params']['height'] = $attributes['height'];
105 unset($attributes['height']);
106 }
107 }
108
109 // Identify what service provider the shortcode's link belongs to
110 $serviceProvider = self::$oEmbedInstance->get_provider($content);
111 // Check if OEmbed was unable to detect the url service provider.
112 if (empty($serviceProvider)) {
113 // Attempt to do the same using Embera.
114 $emberaInstance = new Embera($emberaInstanceSettings);
115 // Add support to the user's custom service providers
116 $additionalServiceProviders = Core::getAdditionalServiceProviders();
117 if (!empty($additionalServiceProviders)) {
118 foreach ($additionalServiceProviders as $serviceProviderClassName => $serviceProviderUrls) {
119 self::addServiceProvider($serviceProviderClassName, $serviceProviderUrls, $emberaInstance);
120 }
121
122 unset($serviceProviderUrls, $serviceProviderClassName);
123 }
124
125 // Attempt to fetch more info about the url-embed.
126 $urlData = $emberaInstance->getUrlInfo($content);
127 } else {
128 // Attempt to fetch more info about the url-embed.
129 $urlData = self::$oEmbedInstance->fetch($serviceProvider, $content, $attributes);
130 }
131
132 $eventResults = apply_filters('embedpress:onBeforeEmbed', $urlData);
133 if (empty($eventResults)) {
134 // EmbedPress seems unable to embed the url.
135 return $subject;
136 }
137
138 // Transform all shortcode attributes into html form. I.e.: {foo: "joe"} -> foo="joe"
139 $attributesHtml = array();
140 foreach ($attributes as $attrName => $attrValue) {
141 $attributesHtml[] = $attrName .'="'. $attrValue .'"';
142 }
143
144 // Define the EmbedPress html template where the generated embed will be injected in
145 $embedTemplate = '<div '. implode(' ', $attributesHtml) .'>{html}</div>';
146
147 // Check if $content is a google shortened url and tries to extract from it which Google service it refers to.
148 if (preg_match('/http[s]?:\/\/goo\.gl\/(?:([a-z]+)\/)?[a-z0-9]+\/?$/i', $content, $matches)) {
149 // Fetch all headers from the short-url so we can know how to handle its original content depending on the service.
150 $headers = get_headers($content);
151
152 $supportedServicesHeadersPatterns = array(
153 'maps' => '/^Location:\s+(http[s]?:\/\/.+)$/i'
154 );
155
156 $service = isset($matches[1]) ? strtolower($matches[1]) : null;
157 // No specific service was found in the url.
158 if (empty($service)) {
159 // Let's try to guess which service the original url belongs to.
160 foreach ($headers as $header) {
161 // Check if the short-url reffers to a Google Maps url.
162 if (preg_match($supportedServicesHeadersPatterns['maps'], $header, $matches)) {
163 // Replace the shortened url with its original url.
164 $content = $matches[1];
165 break;
166 }
167 }
168 unset($header);
169 } else {
170 // Check if the Google service is supported atm.
171 if (isset($supportedServicesHeadersPatterns[$service])) {
172 // Tries to extract the url based on its headers.
173 $originalUrl = self::extractContentFromHeaderAsArray($supportedServicesHeadersPatterns[$service], $headers);
174 // Replace the shortened url with its original url if the specific header was found.
175 if (!empty($originalUrl)) {
176 $content = $originalUrl;
177 }
178 unset($originalUrl);
179 }
180 }
181 unset($service, $supportedServicesHeadersPatterns, $headers, $matches);
182 }
183
184 // Facebook is a special case. WordPress will try to embed them using OEmbed, but they always end up embedding the profile page, regardless
185 // if the url was pointing to a photo, a post, etc. So, since Embera can embed only facebook-media/posts, we'll use it only for that.
186 if (isset($urlData->provider_name) && in_array($urlData->provider_name, array('Facebook'))) {
187 // Check if this is a Facebook profile url.
188 if (preg_match('/facebook\.com\/(?:[^\/]+?)\/?$/', $content, $match)) {
189 // Try to embed the url using WP's OSEmbed.
190 $parsedContent = self::$oEmbedInstance->get_html($content, $attributes);
191 } else {
192 // Try to embed the url using EmbedPress' Embera.
193 $parsedContent = false;
194 }
195 } else {
196 // Try to embed the url using WP's OSEmbed.
197 $parsedContent = self::$oEmbedInstance->get_html($content, $attributes);
198 }
199
200 if (!$parsedContent) {
201 if (!isset($emberaInstance)) {
202 // If the embed couldn't be generated, we'll try to use Embera's API
203 $emberaInstance = new Embera($emberaInstanceSettings);
204 // Add support to the user's custom service providers
205 $additionalServiceProviders = Core::getAdditionalServiceProviders();
206 if (!empty($additionalServiceProviders)) {
207 foreach ($additionalServiceProviders as $serviceProviderClassName => $serviceProviderUrls) {
208 self::addServiceProvider($serviceProviderClassName, $serviceProviderUrls, $emberaInstance);
209 }
210
211 unset($serviceProviderUrls, $serviceProviderClassName);
212 }
213 }
214
215 // Register the html template
216 $emberaFormaterInstance = new Formatter($emberaInstance, true);
217 $emberaFormaterInstance->setTemplate($embedTemplate);
218
219 // Try to generate the embed using Embera API
220 $parsedContent = $emberaFormaterInstance->transform($content);
221
222 unset($emberaFormaterInstance, $additionalServiceProviders, $emberaInstance);
223 } else {
224 // Inject the generated code inside the html template
225 $parsedContent = str_replace('{html}', $parsedContent, $embedTemplate);
226
227 // Replace all single quotes to double quotes. I.e: foo='joe' -> foo="joe"
228 $parsedContent = str_replace("'", '"', $parsedContent);
229
230 // Replace the flag `{provider_alias}` which is used by Embera with the "ose-<serviceProviderAlias>". I.e: YouTube -> "ose-youtube"
231 $parsedContent = preg_replace('/((?:ose-)?\{provider_alias\})/i', "ose-". strtolower($urlData->provider_name), $parsedContent);
232 }
233
234 if (isset($urlData->provider_name) || isset($urlData[$content]['provider_name'])) {
235 // NFB seems to always return their embed code with all HTML entities into their applicable characters string.
236 if ((isset($urlData->provider_name) && strtoupper($urlData->provider_name) === "NATIONAL FILM BOARD OF CANADA") || (is_array($urlData) && isset($urlData[$content]['provider_name']) && strtoupper($urlData[$content]['provider_name']) === "NATIONAL FILM BOARD OF CANADA")) {
237 $parsedContent = html_entity_decode($parsedContent);
238 } else if ((isset($urlData->provider_name) && strtoupper($urlData->provider_name) === "FACEBOOK") || (is_array($urlData) && isset($urlData[$content]['provider_name']) && strtoupper($urlData[$content]['provider_name']) === "FACEBOOK")) {
239 $plgSettings = Core::getSettings();
240
241 // Check if the user wants to force a certain language into Facebook embeds.
242 $locale = isset($plgSettings->fbLanguage) && !empty($plgSettings->fbLanguage) ? $plgSettings->fbLanguage : false;
243 if (!!$locale) {
244 // Replace the automatically detected language by Facebook's API with the language chosen by the user.
245 $parsedContent = preg_replace('/\/[a-z]{2}\_[a-z]{2}\/sdk\.js/i', "/{$locale}/sdk.js", $parsedContent);
246 }
247
248 // Make sure `adapt_container_width` parameter is set to false. Setting to true, as it is by default, might cause Facebook to render embeds inside editors (in admin) with only 180px wide.
249 if (is_admin()) {
250 $parsedContent = preg_replace('~data\-adapt\-container\-width=\"(?:true|1)\"~i', 'data-adapt-container-width="0"', $parsedContent);
251 }
252
253 unset($locale, $plgSettings);
254 }
255 }
256
257 unset($embedTemplate, $serviceProvider);
258
259 // This assure that the iframe has the same dimensions the user wants to
260 if (isset($emberaInstanceSettings['params']['width']) || isset($emberaInstanceSettings['params']['height'])) {
261 if (isset($emberaInstanceSettings['params']['width']) && isset($emberaInstanceSettings['params']['height'])) {
262 $customWidth = (int)$emberaInstanceSettings['params']['width'];
263 $customHeight = (int)$emberaInstanceSettings['params']['height'];
264 } else {
265 if (preg_match('~width="(\d+)"|width\s+:\s+(\d+)~i', $parsedContent, $matches)) {
266 $iframeWidth = (int)$matches[1];
267 }
268
269 if (preg_match('~height="(\d+)"|height\s+:\s+(\d+)~i', $parsedContent, $matches)) {
270 $iframeHeight = (int)$matches[1];
271 }
272
273 if (isset($iframeWidth) && isset($iframeHeight) && $iframeWidth > 0 && $iframeHeight > 0) {
274 $iframeRatio = ceil($iframeWidth / $iframeHeight);
275
276 if (isset($emberaInstanceSettings['params']['width'])) {
277 $customWidth = (int)$emberaInstanceSettings['params']['width'];
278 $customHeight = ceil($customWidth / $iframeRatio);
279 } else {
280 $customHeight = (int)$emberaInstanceSettings['params']['height'];
281 $customWidth = $iframeRatio * $customHeight;
282 }
283 }
284 }
285
286 if (isset($customWidth) && isset($customHeight)) {
287 if (preg_match('~width="(\d+)"~i', $parsedContent)) {
288 $parsedContent = preg_replace('~width="(\d+)"~i', 'width="'. $customWidth .'"', $parsedContent);
289 }
290
291 if (preg_match('~height="(\d+)"~i', $parsedContent)) {
292 $parsedContent = preg_replace('~height="(\d+)"~i', 'height="'. $customHeight .'"', $parsedContent);
293 }
294
295 if (preg_match('~width\s+:\s+(\d+)~i', $parsedContent)) {
296 $parsedContent = preg_replace('~width\s+:\s+(\d+)~i', 'width: '. $customWidth, $parseContent);
297 }
298
299 if (preg_match('~height\s+:\s+(\d+)~i', $parsedContent)) {
300 $parsedContent = preg_replace('~height\s+:\s+(\d+)~i', 'height: '. $customHeight, $parseContent);
301 }
302 }
303 }
304
305 if ($stripNewLine) {
306 $parsedContent = preg_replace('/\n/', '', $parsedContent);
307 }
308
309 if (!empty($parsedContent)) {
310 $embed = (object)array_merge((array)$urlData, array(
311 'attributes' => (object)$attributes,
312 'embed' => $parsedContent,
313 'url' => $content
314 ));
315
316 $embed = apply_filters('embedpress:onAfterEmbed', $embed);
317
318 return $embed;
319 }
320 }
321
322 return $subject;
323 }
324
325 /**
326 * Method that adds support to a given new service provider (SP).
327 *
328 * @since 1.0.0
329 * @static
330 *
331 * @param string $className The new SP class name.
332 * @param string $reference The new SP reference name.
333 * @param \Embera\Embera $emberaInstance The embera's instance where the SP will be registered in.
334 * @return boolean
335 */
336 public static function addServiceProvider($className, $reference, &$emberaInstance)
337 {
338 if (empty($className) || empty($reference)) {
339 return false;
340 }
341
342 if (is_string($reference)) {
343 $emberaInstance->addProvider($reference, EMBEDPRESS_NAMESPACE ."\\Providers\\{$className}");
344 } else if (is_array($reference)) {
345 foreach ($reference as $serviceProviderUrl) {
346 self::addServiceProvider($className, $serviceProviderUrl, $emberaInstance);
347 }
348 } else {
349 return false;
350 }
351 }
352
353 /**
354 * Method that retrieves all custom parameters from a shortcoded string.
355 *
356 * @since 1.0.0
357 * @static
358 *
359 * @param string $subject The given shortcoded string.
360 * @return array
361 */
362 public static function parseContentAttributesFromString($subject)
363 {
364 $customAttributes = array();
365 if (preg_match('/\[embed\s*(.*?)\]/i', stripslashes($subject), $m)) {
366 if (preg_match_all('/(\!?\w+-?\w*)(?:="(.+?)")?/i', stripslashes($m[1]), $matches)) {
367 $attributes = $matches[1];
368 $attrValues = $matches[2];
369
370 foreach ($attributes as $attrIndex => $attrName) {
371 $customAttributes[$attrName] = $attrValues[$attrIndex];
372 }
373 }
374 }
375
376 return $customAttributes;
377 }
378
379 /**
380 * Method that parses and adds the "data-" prefix to the given custom shortcode attributes.
381 *
382 * @since 1.0.0
383 * @access private
384 * @static
385 *
386 * @param array $attributes The array containing the embed attributes.
387 * @return array
388 */
389 private static function parseContentAttributes(array $customAttributes)
390 {
391 $attributes = array(
392 'class' => array("embedpress-wrapper")
393 );
394
395 $embedShouldBeResponsive = true;
396 $embedShouldHaveCustomDimensions = false;
397 if (!empty($customAttributes)) {
398 if (isset($customAttributes['class'])) {
399 if (!empty($customAttributes['class'])) {
400 $customAttributes['class'] = explode(' ', $customAttributes['class']);
401
402 $attributes['class'] = array_merge($attributes['class'], $customAttributes['class']);
403 }
404
405 unset($customAttributes['class']);
406 }
407
408 if (isset($customAttributes['width'])) {
409 if (!empty($customAttributes['width'])) {
410 $attributes['width'] = (int)$customAttributes['width'];
411 $embedShouldHaveCustomDimensions = true;
412 }
413 }
414
415 if (isset($customAttributes['height'])) {
416 if (!empty($customAttributes['height'])) {
417 $attributes['height'] = (int)$customAttributes['height'];
418 $embedShouldHaveCustomDimensions = true;
419 }
420 }
421
422 if (!empty($customAttributes)) {
423 $attrNameDefaultPrefix = "data-";
424 foreach ($customAttributes as $attrName => $attrValue) {
425 if (is_numeric($attrName)) {
426 $attrName = $attrValue;
427 $attrValue = "";
428 }
429
430 $attrName = str_replace($attrNameDefaultPrefix, "", $attrName);
431
432 if (!strlen($attrValue)) {
433 if ($attrName[0] === "!") {
434 $attrValue = "false";
435 $attrName = substr($attrName, 1);
436 } else {
437 $attrValue = "true";
438 }
439 }
440
441 $attributes[$attrNameDefaultPrefix . $attrName] = $attrValue;
442 }
443 }
444
445 // Check if there's any "responsive" parameter
446 $responsiveAttributes = array("responsive", "data-responsive");
447 foreach ($responsiveAttributes as $responsiveAttr) {
448 if (isset($attributes[$responsiveAttr])) {
449 if (!strlen($attributes[$responsiveAttr])) { // If the parameter is passed but have no value, it will be true by default
450 $embedShouldBeResponsive = true;
451 } else {
452 $embedShouldBeResponsive = !self::valueIsFalse($attributes[$responsiveAttr]);
453 }
454
455 break;
456 }
457 }
458 unset($responsiveAttr, $responsiveAttributes);
459 }
460
461 if ($embedShouldBeResponsive && !$embedShouldHaveCustomDimensions) {
462 $attributes['class'][] = 'ose-{provider_alias} responsive';
463 } else {
464 $attributes['data-responsive'] = "false";
465 }
466
467 $attributes['class'] = implode(' ', array_unique(array_filter($attributes['class'])));
468
469 return $attributes;
470 }
471
472 /**
473 * Method that checks if a given value is/can be identified as (bool)false.
474 *
475 * @since 1.0.0
476 * @static
477 *
478 * @param mixed $subject The value to be checked.
479 * @return boolean
480 */
481 public static function valueIsFalse($subject)
482 {
483 $subject = strtolower(trim((string)$subject));
484 switch ($subject) {
485 case "0":
486 case "false":
487 case "off":
488 case "no":
489 case "n":
490 case "nil":
491 case "null":
492 return true;
493 default:
494 return false;
495 }
496 }
497
498 /**
499 * Return the value from a header which is in an array resulted from a get_headers() call.
500 * If the header cannot be found, this method will return null instead.
501 *
502 * @since 1.1.0
503 * @access private
504 * @static
505 *
506 * @param string $headerPattern Regex pattern the header and its value must match.
507 * @param array $headersList A list of headers resulted from a get_headers() call.
508 *
509 * @return mixed
510 */
511 private static function extractContentFromHeaderAsArray($headerPattern, $headersList)
512 {
513 $headerValue = null;
514
515 foreach ($headersList as $header) {
516 if (preg_match($headerPattern, $header, $matches)) {
517 $headerValue = $matches[1];
518 break;
519 }
520 }
521
522 return $headerValue;
523 }
524 }
525