first commit

This commit is contained in:
2018-04-11 17:57:57 +02:00
commit fdfcfbd2a6
4073 changed files with 501866 additions and 0 deletions
@@ -0,0 +1,494 @@
<?php
/**
* OEmbed
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
use Grav\Common\GravTrait;
use Grav\Common\Data\Data;
use RocketTheme\Toolbox\Event\Event;
/**
* OEmbed
*/
class OEmbed implements OEmbedInterface
{
use GravTrait;
/**
* @var \Grav\Common\Data\Data
*/
protected $base_config;
/**
* @var \Grav\Common\Data\Data
*/
protected $config;
/**
* @var string
*/
protected $embedCode = '';
/**
* @var array
*/
protected $attributes;
/**
* @var array
*/
protected $params;
/**
* @var array
*/
protected $oembed;
protected $protocol;
/** -------------
* Public methods
* --------------
*/
/**
* Constructor.
*/
public function __construct(array $config = [])
{
$this->base_config = $this->config = new Data($config);
$schemes = $this->base_config->get('schemes', []);
if (!is_array($schemes)) {
$schemes = [$schemes];
}
foreach ($schemes as $index => $scheme) {
$scheme = preg_quote($scheme);
$schemes[$index] = preg_replace_callback('~((?:\\\\\*){1,2})(.[^\\\\]?|$)~',
function($match) {
// Remove control characters
$separator = preg_replace('~[^\p{L}]~i', '', $match[2]);
$star = strlen(str_replace('\\', '', $match[1]));
if (ctype_alnum($separator)) {
$replace = '.*?';
} else {
$separator = (strlen($separator) == 0) ? substr($match[2], -1) : $separator;
$replace = (strlen($match[2]) > 0) ? "[^$separator ]+" : '[^\"\&\?\. ]+';
}
// Wrap one star result in parenthesis
$replace = ($star > 1) ? $replace : "($replace)";
return $replace . $match[2];
}, $scheme);
}
$this->base_config->set('schemes', $schemes);
}
public function init($embedCode, $config = [])
{
$this->reset();
// Normalize URL to embed
$url = $this->parseUrl($embedCode);
$this->embedCode = $this->canonicalize($embedCode);
$this->oembed = new Data((array) $this->getOEmbed());
// Get media attributes and object parameters
$attributes = [
'width'=> $this->oembed->get('width', 0),
'height' => $this->oembed->get('height', 0),
'protocol' => ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://",
];
// $this->config->merge($config);
// $attributes = $this->config->get('media', []);
$params = array_replace_recursive($this->config->get('params', []), $url['query']);
// Copy media attributes from object parameters
$attr_keys = ['width', 'height', 'adjust', 'preview', 'responsive'];
foreach ($attr_keys as $key) {
if (isset($params[$key])) {
$attributes[$key] = $params[$key];
unset($params[$key]);
}
}
// Set media attributes and object parameters
$this->attributes($attributes);
$this->params($params);
}
public function canonicalize($embedCode)
{
$schemes = $this->config->get('schemes', []);
foreach ($schemes as $scheme) {
preg_match("~$scheme~i", $embedCode, $matches);
if ($matches && $this->validId(end($matches))) {
return end($matches);
}
}
}
/**
* Check if a media id is valid.
*
* @param string $id Id to check against the oembed stream.
*
* @return boolean TRUE if id is valid, FALSE otherwise. Throws errors
* on invalid ids.
*/
protected function validId($id)
{
$endpoint = $this->config->get('endpoint', '');
$endpoint = $this->format($endpoint, ['{:id}' => $id]);
if (!$id || !$endpoint) {
return false;
}
$response = \Requests::head($endpoint);
// If a head request fails, try to send a get request
if ($response->status_code != 200) {
$response = \Requests::get($endpoint);
}
if ( $response->status_code == 401 ) {
throw new \Exception('Embedding has been disabled for this media.');
} elseif ( $response->status_code == 404 ) {
throw new \Exception('The media ID was not found.');
} elseif ( $response->status_code == 501 ) {
throw new \Exception('Media informations can not be retrieved.');
} elseif ( $response->status_code != 200 ) {
throw new \Exception('The media ID is invalid or the media was deleted.');
} elseif (!$response->success) {
$response->throw_for_status();
}
return true;
}
public function reset()
{
// Reset values
$this->embedCode = '';
$this->oembed = null;
$this->attributes([], true);
$this->params([], true);
$this->config = new Data($this->base_config->toArray());
}
public function id()
{
return $this->embedCode;
}
public function slug()
{
$slug = strtolower($this->name()) . '://' . $this->id();
return $slug;
}
public function name()
{
$name = get_class($this);
$name = substr($name, strrpos($name, '\\') + 1);
if ($this->embedCode) {
$name = $this->config->get('name', $name);
if (mb_strlen($name) == 0 && $this->oembed) {
$this->oembed->get('provider_name', '');
}
}
return $name;
}
public function title()
{
$title = '';
if ($this->oembed) {
$title = $this->oembed->get('title', '');
}
return $title;
}
public function description()
{
$description = '';
if ($this->oembed) {
$description = $this->oembed->get('description', '');
}
return $description;
}
public function url()
{
$url = '';
if ($this->embedCode && $this->oembed) {
$url = $this->format($this->config->get('url', ''), ['{:url}' => '']);
if (strlen($url) == 0) {
$url = $this->oembed->get('url', $url);
} else {
$protocol = isset($this->attributes['protocol']) ? $this->attributes['protocol'] : '//';
$url = $protocol . $url;
}
}
return $url;
}
public function website()
{
$website = '';
if ($this->oembed) {
$website = $this->oembed->get('provider_url', '');
}
return $website;
}
/**
* Returns a png img
*
* @param array $stub or string $alias
* @return Resource or null if not available
*/
public function icon() {
$icon = '';
$endpoint = '';
if ($this->oembed) {
$endpoint = $this->format($this->config->get('endpoint', ''));
}
if (!$endpoint) {
return $icon;
}
$pieces = parse_url($endpoint);
$url = $pieces['host'];
// Grab favicon from Google cache
$icon = 'http://www.google.com/s2/favicons?domain=';
$icon .= urlencode($url);
return $icon;
}
public function thumbnail()
{
$thumbnail = '';
if ($this->oembed) {
$thumbnail = $this->oembed->get('thumbnail_url', '');
}
return $thumbnail;
}
public function type()
{
$type = $this->config->get('type', 'generic');
if ($type === 'generic' && $this->embedCode && $this->oembed) {
$type = $this->oembed->get('type', $type);
}
return $type;
}
public function author($key = 'name')
{
$author = '';
if ($this->embedCode && $this->oembed) {
$author = $this->oembed->get('author_' . strtolower($key), '');
}
return $author;
}
public function attributes($var = null, $reset = false)
{
if ($var !== null) {
if ($reset) {
$this->attributes = $var;
} else {
$this->attributes = array_replace_recursive($this->attributes, $var);
}
}
if (!is_array($this->attributes)) {
$this->attributes = [];
}
return $this->attributes;
}
public function params($var = null, $reset = false)
{
if ($var !== null) {
if ($reset) {
$this->params = $var;
} else {
$this->params = array_replace_recursive($this->params, $var);
}
}
if (!is_array($this->params)) {
$this->params = [];
}
return $this->params;
}
public function getEmbedCode($params = [])
{
$params = array_replace_recursive($this->params(), $params);
$url = $this->url();
$query = http_build_query($params);
if (mb_strlen($query) > 0) {
$query = (false === strpos($url, '?') ? '?' : '&') . $query;
}
return $url . $query;
}
/**
* Returns information about the media. See http://www.oembed.com/.
*
* @return
* If oEmbed information is available, an array containing 'title', 'type',
* 'url', and other information as specified by the oEmbed standard.
* Otherwise, NULL.
*/
public function getOEmbed()
{
if ($this->oembed) {
return $this->oembed;
}
$endpoint = $this->format($this->config->get('endpoint', ''));
if (!$endpoint) {
return [];
}
$response = \Requests::get($endpoint);
if (!$response->success) {
$response->throw_for_status();
}
return json_decode($response->body, true);
}
/**
* Return the domain(s) of this media resource
*
* @return string
*/
public function getDomains()
{
// Get domains of media resources
$schemes = $this->base_config->get('schemes', []);
// Ensure domains are of type array
if (!is_array($schemes)) {
$schemes = [$schemes];
}
$domains = [];
foreach ($schemes as $scheme) {
// Trick: extract domains from scheme attributes
$domain = parse_url(str_replace('\.', '.', "http://$scheme"), PHP_URL_HOST);
// Take out the www. in front of domain
$domains[] = preg_replace("/^www\./", '', $domain);
}
// Faster alternative to PHPs array unique function
return array_keys(array_flip($domains));
}
public function onTwigTemplateVariables(Event $event)
{
$mediaembed = $event['mediaembed'];
foreach ($this->config->get('assets', []) as $asset) {
if (is_string($asset) && strlen($asset) > 0) {
$mediaembed->add($asset);
}
}
}
/**
* Convenience wrapper for `echo $ServiceProvider`
*
* @return string
*/
public function __toString()
{
return $this->getEmbedCode();
}
/** -------------------------------
* Private/protected helper methods
* --------------------------------
*/
protected function format($string, $params = [])
{
$keys = ['id', 'name', 'url'];
foreach ($keys as $key) {
if (!isset($params["{:$key}"])) {
$params["{:$key}"] = $this->{$key}();
}
}
$params += [
'{:canonical}' => $this->config->get('canonical', ''),
];
// Format URL placeholder with params
$keys = ['{:url}', '{:canonical}'];
foreach ($keys as $key) {
$params[$key] = urlencode(str_ireplace(
array_keys($params), $params, $params[$key])
);
}
// Replace OEmbed calls with response
$string = preg_replace_callback('~\{\:oembed(?:\.(?=\w))([\.\w_]+)?\}~i',
function($match) {
$ombed = $this->getOEmbed();
return $oembed ? $oembed->get($match[1], '') : $match[0];
}, $string);
return str_ireplace(array_keys($params), $params, $string);
}
protected function parseUrl($url)
{
if (!filter_var($url, FILTER_VALIDATE_URL)) {
return [];
}
// Parse URL
$url = html_entity_decode($url, ENT_COMPAT | ENT_HTML401, 'UTF-8');
$parts = parse_url($url);
$parts['url'] = $url;
// Get top-level domain from URL
$parts['domain'] = isset($parts['host']) ? $parts['host'] : '';
if ( preg_match('~(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$~i', $parts['domain'], $match) ) {
$parts['domain'] = $match['domain'];
}
if (isset($parts['query'])) {
parse_str(urldecode($parts['query']), $parts['query']);
} else {
$parts['query'] = [];
}
return $parts;
}
}
@@ -0,0 +1,156 @@
<?php
/**
* OEmbedInterface
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
/**
* OEmbedInterface
*/
interface OEmbedInterface
{
/**
* Initialize service.
*/
public function init($embedCode, $config = []);
/**
* Reset service.
*/
public function reset();
/**
* Extract and normalize id from embed code.
*
* @param string $embedCode The embed code to be canonicalized
* @return string Returns the canonicalized embed code,
* usually an id.
*/
public function canonicalize($embedCode);
/**
* Returns the unique id of a media resource.
*
* @return string
*/
public function id();
/**
* Returns the host as slugged string.
*
* @return string
*/
public function slug();
/**
* Returns the name of this media.
*
* @return string
*/
public function name();
/**
* Returns the title of this media.
*
* @return string
*/
public function title();
/**
* Returns the description of this media.
*
* @return string
*/
public function description();
/**
* The URL of this media
*
* @return string
*/
public function url();
/**
* The website where this media come from.
*
* @return string
*/
public function website();
/**
* Gets the thumbnail of the media and its dimensions.
*
* @return array
*/
public function thumbnail();
/**
* Returns the type of this media.
*
* @return string
*/
public function type();
/**
* Gets the author and informations about him from the media.
*
* @return array
*/
public function author($key = 'name');
/**
* Gets or sets object attributes about the media
*
* @param bool $var Media attributes
*
* @return array Returns object attributes about the media i.e.
* width, height and so on.
*/
public function attributes($var = [], $reset = false);
/**
* Gets or sets object parameter about the media
*
* @param bool $var Media parameter.
*
* @return array Returns the object parameter about the media i.e.
* additional parameter for the request
*/
public function params($var = [], $reset = false);
/**
* Returns the final HTML code for display.
*
* @return string
*/
public function getEmbedCode($params = []);
/**
* Returns information about the media. See http://www.oembed.com/.
*
* @return
* If oEmbed information is available, an array containing 'title', 'type',
* 'url', and other information as specified by the oEmbed standard.
* Otherwise, NULL.
*/
public function getOEmbed();
/**
* Returns the accepted domains of this media resource
*
* @return array
*/
public function getDomains();
/**
* Special Template events fired by Grav\Plugin\MediaEmbed\Service.
*/
// public function onTwigTemplatePaths();
// public function onTwigTemplateVariables(Event $event);
}
@@ -0,0 +1,25 @@
<?php
/**
* OEmbedLink
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
use Grav\Plugin\MediaEmbed\OEmbed\OEmbed;
/**
* OEmbedLink
*
* Responses of this type allow a provider to return any generic embed
* data (such as title and author_name), without providing either the
* url or html parameters. The consumer may then link to the resource,
* using the URL specified in the original request.
*/
class OEmbedLink extends OEmbed
{
}
@@ -0,0 +1,48 @@
<?php
/**
* OEmbedPhoto
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
use Grav\Plugin\MediaEmbed\OEmbed\OEmbed;
/**
* OEmbedPhoto
*
* This type is used for representing static photos. The following
* parameters are defined:
*
* url (required)
* The source URL of the image. Consumers should be able to insert
* this URL into an <img> element. Only HTTP and HTTPS URLs are valid.
*
* width (required)
* The width in pixels of the image specified in the url parameter.
*
* height (required)
* The height in pixels of the image specified in the url parameter.
*
* Responses of this type must obey the maxwidth and maxheight request
* parameter.
*/
class OEmbedPhoto extends OEmbed
{
public function getOEmbed()
{
$oembed = parent::getOEmbed();
if ($this->embedCode && $this->oembed) {
$width = $this->oembed->get('width');
$height = $this->oembed->get('height');
$this->attributes(['width' => $width, 'height' => $height]);
}
return $oembed;
}
}
@@ -0,0 +1,62 @@
<?php
/**
* OEmbedRich
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
use Grav\Plugin\MediaEmbed\OEmbed\OEmbed;
/**
* OEmbedRich
*
* This type is used for rich HTML content that does not fall under one
* of the other categories. The following parameters are defined:
*
* html (required)
* The HTML required to display the resource. The HTML should have no
* padding or margins. Consumers may wish to load the HTML in an
* off-domain iframe to avoid XSS vulnerabilities. The markup should
* be valid XHTML 1.0 Basic.
*
* width (required)
* The width in pixels required to display the HTML.
*
* height (required)
* The height in pixels required to display the HTML.
*
* Responses of this type must obey the maxwidth and maxheight request
* parameters.
*/
class OEmbedRich extends OEmbed
{
public function getOEmbed()
{
$oembed = parent::getOEmbed();
$sizes = ['width', 'height'];
foreach ($sizes as $key) {
$size = isset($oembed[$key]) ? $oembed[$key] : 0;
if (!preg_match('~^\d+$~', $size)) {
$oembed[$key] = 0;
}
}
return $oembed;
}
public function getEmbedCode($params = [])
{
$embed = parent::getEmbedCode($params);
if ($this->embedCode && $this->oembed) {
$embed = $this->oembed->get('html', '');
}
return $embed;
}
}
@@ -0,0 +1,61 @@
<?php
/**
* OEmbedVideo
*
* This file is part of Grav MediaEmbed plugin.
*
* Dual licensed under the MIT or GPL Version 3 licenses, see LICENSE.
* http://benjamin-regler.de/license/
*/
namespace Grav\Plugin\MediaEmbed\OEmbed;
use Grav\Plugin\MediaEmbed\OEmbed\OEmbed;
/**
* OEmbedVideo
*
* This type is used for representing playable videos. The following
* parameters are defined:
*
* html (required)
* The HTML required to embed a video player. The HTML should have
* no padding or margins. Consumers may wish to load the HTML in an
* off-domain iframe to avoid XSS vulnerabilities.
*
* width (required)
* The width in pixels required to display the HTML.
*
* height (required)
* The height in pixels required to display the HTML.
*
* Responses of this type must obey the maxwidth and maxheight request
* parameters. If a provider wishes the consumer to just provide a
* thumbnail, rather than an embeddable player, they should instead
* return a photo response type.
*/
class OEmbedVideo extends OEmbed
{
public function getOEmbed()
{
$oembed = parent::getOEmbed();
if ($this->embedCode && $this->oembed) {
$width = $this->oembed->get('width');
$height = $this->oembed->get('height');
$this->attributes(['width' => $width, 'height' => $height]);
}
return $oembed;
}
public function getEmbedCode($params = [])
{
$embed = parent::getEmbedCode($params);
if (mb_strlen($embed) == 0 && $this->embedCode && $this->oembed) {
$embed = $this->oembed->get('html', '');
}
return $embed;
}
}