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