123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- <?php
- /**
- * MediaEmbed
- *
- * 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;
- use Grav\Common\Grav;
- use Grav\Common\GravTrait;
- use Grav\Plugin\MediaEmbed\Service;
- use RocketTheme\Toolbox\Event\Event;
- /**
- * MediaEmbed
- *
- * Helper class to embed several media sites (e.g. YouTube, Vimeo,
- * Soundcloud) by only providing the URL to the medium.
- */
- class MediaEmbed
- {
- /**
- * @var MediaEmbed
- */
- use GravTrait;
- /** ---------------------------
- * Private/protected properties
- * ----------------------------
- */
- /**
- * A unique identifier
- *
- * @var string
- */
- protected $id;
- /**
- * A key-valued array used for hashing math formulas of a page
- *
- * @var array
- */
- protected $hashes;
- /**
- * @var array
- */
- protected $config;
- /**
- * @var array
- */
- protected $assets = [];
- /**
- * @var Grav\Plugin\MediaEmbed\Service
- */
- protected $service;
- /** -------------
- * Public methods
- * --------------
- */
- /**
- * Constructor
- *
- * @param [type] $config [description]
- */
- public function __construct($config)
- {
- // Initialize Service class
- $this->service = new Service();
- $this->config = $config;
- $this->hashes = [];
- $services = $this->config->get('plugins.mediaembed.services', []);
- foreach ($services as $name => $config) {
- if (!$config['enabled']) {
- continue;
- }
- // Load providers in directory "services"
- $class = __NAMESPACE__ . "\\Services\\$name";
- if (!class_exists($class)) {
- // Fallback to a more generic one
- $type = isset($config['type']) ? $config['type'] : '';
- $class = __NAMESPACE__."\\OEmbed\\OEmbed".ucfirst($type);
- }
- // Populate config
- $config['media'] = $this->config->get('plugins.mediaembed.media', []);
- $config['name'] = $name;
- if (class_exists($class)) {
- // Load ServiceProvider
- $provider = new $class($config);
- // Register ServiceProvider
- $this->service->register($provider);
- }
- }
- }
- /**
- * Gets and sets the identifier for hashing.
- *
- * @param string $var the identifier
- *
- * @return string the identifier
- */
- public function id($var = null)
- {
- if ($var !== null) {
- $this->id = $var;
- }
- return $this->id;
- }
- public function prepare($content, $id = '')
- {
- // Set unique identifier based on page content
- $this->id(md5(time() . $id . md5($content)));
- // Reset class hashes before processing
- $this->reset();
- $regex = "~
- ( # wrap whole match in $1
- !\\[
- (?P<alt>.*?) # alt text = $2
- \\]
- \\( # literal paren
- [ \\t]*
- <?(?P<src>\S+?)>? # src url = $3
- [ \\t]*
- ( # $4
- (['\"]) # quote char = $5
- (?<title>.*?) # title = $6
- \\5 # matching quote
- [ \\t]*
- )? # title is optional
- \\)
- )
- ~xs";
- // Replace all mediaembed links by a (unique) hash
- $content = preg_replace_callback($regex, function($matches) {
- // Get the url and parse it
- $url = parse_url(htmlspecialchars_decode($matches[3]));
- // If there is no host set but there is a path, the file is local
- if (!isset($url['host']) && isset($url['path'])) {
- return $matches[0];
- }
- if (!isset($matches['title'])) {
- $matches['title'] = '';
- }
- return $this->hash($matches[0], $matches);
- }, $content);
- return $content;
- }
- public function process($content, $config = [])
- {
- /** @var Twig $twig */
- $twig = self::getGrav()['twig'];
- // Initialize unique per-page counter
- $uid = 1;
- // '~(<p>)?\s*<a[^>]*href\s*=\s*([\'"])(?P<href>.*?)\2[^>]*>(?P<code>.*?)</a>\s*(?(1)(</p>))~i',
- // Get all <a> tags and extract "href" attribute
- $content = preg_replace_callback(
- '~mediaembed::([0-9a-z]+)::([0-9]+)::M~i',
- function($match) use ($twig, &$uid, $config) {
- list($embed, $data) = $this->hashes[$match[0]];
- // Check if a service for a specific domain is registered
- if ($this->service->match($data['src'])) {
- $mediaembed = [
- 'uid' => $uid++,
- 'service' => null,
- 'config' => $config,
- 'raw' => [
- 'alt' => $data['alt'],
- 'title' => $data['title'],
- 'src' => html_entity_decode($data['src']),
- ],
- 'success' => true,
- 'message' => '',
- ];
- // Load and get data of OEmbed Media Service
- try {
- $provider = $this->service->embed($data['src']);
- } catch (\Exception $e) {
- $mediaembed['message'] = $e->getMessage();
- $mediaembed['success'] = false;
- }
- // Setup variables for embedding OEmbed Media Service
- if ($mediaembed['success']) {
- // Get assets/options of current provider
- $assets = $provider->onTwigTemplateVariables(
- new Event(['service' => $this->service, 'mediaembed' => $this])
- );
- // Assets are passed by value as an array
- if (is_array($assets)) {
- $this->addAssets($assets);
- }
- // Add OEmbed Service to variables
- $mediaembed['service'] = $provider;
- // TODO: Cache contents from thumbnail and url
- }
- // Embed OEmbed Media
- $vars = ['mediaembed' => $mediaembed];
- $template = 'partials/mediaembed' . TEMPLATE_EXT;
- $embed = $twig->processTemplate($template, $vars);
- } else {
- $text = (strlen($data['alt']) > 0) ? $data['alt'] : $data['src'];
- // If display link or img
- $link = $config->get('link');
- if($link == true) {
- $attributes = [
- 'href' => $data['src'],
- 'title' => $data['title'],
- ];
- $format = '<a%s>%s</a>';
- } else {
- $attributes = [
- 'src' => $data['src'],
- 'title' => $data['title'],
- 'alt' => $data['alt'],
- ];
- $format = '<img%s>';
- }
- foreach ($attributes as $key => $value) {
- if (strlen($value) == 0) {
- unset($attributes[$key]);
- } else {
- $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
- $attributes[$key] = $key . '="' . $value . '"';
- }
- }
- $attributes = $attributes ? ' ' . implode(' ', $attributes) : '';
- // Transform embed media to link or img for compatibility
- $embed = sprintf($format, $attributes, $text);
- }
- return $embed;
- }, $content);
- $this->reset();
- // Write content back to page
- return $content;
- }
- /**
- * Fires an event with optional parameters.
- *
- * @param string $eventName The name of the event.
- * @param Event $event Optional parameter to be passed to the
- * called methods.
- * @return Event
- */
- public function fireEvent($eventName, Event $event = null)
- {
- // Dispatch event; just propagate it to service class
- return $this->service->call($eventName, $event);
- }
- /**
- * Get assets of loaded media services.
- *
- * @param boolean $reset Toggle whether to reset assets after retrieving
- * or not.
- */
- public function getAssets($reset = true)
- {
- $assets = $this->assets;
- if ($reset) {
- $this->assets = [];
- }
- return $assets;
- }
- /**
- * Add assets to the queue of MediaEmbed plugin
- *
- * @param array $assets An array of assets to add.
- * @param boolean $append Append assets to array or reset assets.
- */
- public function addAssets($assets, $append = true)
- {
- // Append or reset assets
- if (!$append) {
- $this->assets = [];
- }
- // Wrap non-array assets in an array
- if (!is_array($assets)) {
- $assets = array($assets);
- }
- // Merge assets
- $assets = array_merge($this->assets, $assets);
- // Remove duplicates
- $this->assets = array_keys(array_flip($assets));
- }
- /**
- * Add assets to the queue of MediaEmbed plugin
- *
- * Alias for `addAssets`
- *
- * @param array $assets An array of assets to add.
- * @param boolean $append Append assets to array or reset assets.
- */
- public function add($assets, $append = true)
- {
- return $this->addAssets($assets, $append);
- }
- /**
- * Add assets to the queue of MediaEmbed plugin
- *
- * Alias for `addAssets`
- *
- * @param array $assets An array of assets to add.
- * @param boolean $append Append assets to array or reset assets.
- */
- public function addCss($assets, $append = true)
- {
- return $this->addAssets($assets, $append);
- }
- /**
- * Add assets to the queue of MediaEmbed plugin
- *
- * Alias for `addAssets`
- *
- * @param array $assets An array of assets to add.
- * @param boolean $append Append assets to array or reset assets.
- */
- public function addJs($assets, $append = true)
- {
- return $this->addAssets($assets, $append);
- }
- /** -------------------------------
- * Private/protected helper methods
- * --------------------------------
- */
- /**
- * Get cached media or media with key.
- *
- * @param string $key The key to load from the cache.
- * @return mixed The media content.
- */
- protected function getCachedMedia($key)
- {
- /** @var Cache $cache */
- $cache = $grav['cache'];
- // Check, if cache should be used or not
- if ($this->config->get('cache.enabled')) {
- // Get cache id and try to fetch data
- $cache_id = md5('mediaembed' . $key . $cache->getKey());
- $data = $cache->fetch($cache_id);
- if ((false === $data) || (time() > $data['expire'])) {
- // Pack and provide data with a time stamp.
- $data = array(
- 'content' => $this->service->embed($key),
- 'expire' => time() + $this->config->get('cache.lifetime'),
- );
- $cache->save($cache_id, $data);
- }
- // Return data contents
- $content = $data['content'];
- } else {
- // Just call callback and return result
- $content = $this->service->embed($key);
- }
- return $content;
- }
- 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']);
- }
- $parts['query'] = [];
- return $parts;
- }
- /**
- * Reset MathJax class
- */
- protected function reset()
- {
- $this->hashes = [];
- }
- /**
- * Hash a given text.
- *
- * Called whenever a tag must be hashed when a function insert an
- * atomic element in the text stream. Passing $text to through this
- * function gives a unique text-token which will be reverted back when
- * calling unhash.
- *
- * @param string $text The text to be hashed
- * @param string $type The type (category) the text should be saved
- *
- * @return string Return a unique text-token which will be
- * reverted back when calling unhash.
- */
- protected function hash($text, $data = [])
- {
- static $counter = 0;
- // Swap back any tag hash found in $text so we do not have to `unhash`
- // multiple times at the end.
- $text = $this->unhash($text);
- // Then hash the block
- $key = implode('::', array('mediaembed', $this->id, ++$counter, 'M'));
- $this->hashes[$key] = [$text, $data];
- // String that will replace the tag
- return $key;
- }
- /**
- * Swap back in all the tags hashed by hash.
- *
- * @param string $text The text to be un-hashed
- *
- * @return string A text containing no hash inside
- */
- protected function unhash($text)
- {
- $text = preg_replace_callback(
- '~mediaembed::([0-9a-z]+)::([0-9]+)::M~i', function($atches) {
- return $this->hashes[$matches[0]][0];
- }, $text);
- return $text;
- }
- }
|