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