Initial commit

This commit is contained in:
isUnknown 2026-02-12 15:22:46 +01:00
commit 65e0da7e11
1397 changed files with 596542 additions and 0 deletions

View file

@ -0,0 +1,111 @@
<?php
namespace Kirby\Embed;
use A;
use C;
use Kirby\Embed\Cache;
use Kirby\Embed\Thumb;
class Core {
public $provider = null;
public function __construct($url, $args = []) {
$this->input = $url;
$this->cache = c::get('plugin.embed.caching', true) ? new Cache('embed', $url) : false;
$this->load();
if($this->data !== false) {
$this->options = $this->options($args);
$this->provider = $this->provider();
$this->url = new Url($this->data()->code);
}
}
// ================================================
// Load remote or cached data
// ================================================
protected function load() {
// get data from cache
if($this->cache && $this->cache->exists()) {
return $this->data = $this->cache->get();
// load data from source
} else {
$this->data = Data::get($this->input);
}
// cache the data
if($this->cache && $this->data) {
$this->cache->set($this->data, c::get('plugin.embed.caching.duration', 24) * 60);
}
}
// ================================================
// Default options
// ================================================
protected function options($options) {
return a::merge([
'class' => null,
'thumb' => null,
'autoplay' => c::get('plugin.embed.video.autoplay', false),
'lazyvideo' => c::get('plugin.embed.video.lazyload', true),
'jsapi' => c::get('plugin.embed.providers.jsapi', false),
], $options);
}
// ================================================
// Thumb
// ================================================
public function thumb() {
if($this->options['thumb']) return $this->options['thumb'];
$thumb = $this->image();
if($this->cache) {
return new Thumb('embed', $thumb, (c::get('plugin.embed.caching.duration', 24) * 60 * 60));
} else {
return $thumb;
}
}
// ================================================
// Custom provider instance
// ================================================
protected function provider() {
$namespace = 'Kirby\Embed\Providers\\';
$class = $namespace . $this->data()->providerName;
$class = class_exists($class) ? $class : $namespace . 'Provider';
return $this->provider ?: new $class($this, $this->input);
}
// ================================================
// Magic methods
// ================================================
public function __call($name, $args) {
if(method_exists($this->provider, $name)) {
return $this->provider->{$name}($this->data()->{$name}, $args);
} else {
return $this->data()->{$name};
}
}
public function data() {
return is_array($this->data) ? $this->data[0] : $this->data;
}
public function __toString() {
return !$this->data ? Html::error($this->input) : (string)new Html($this);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Kirby\Embed;
use C;
use Embed\Embed;
class Data {
public static function get($url) {
try {
return Embed::create($url, static::config());
} catch (\Exception $e) {
return false;
}
}
protected static function config() {
return [
'choose_bigger_image' => true,
'google' => [
'key' => c::get('plugin.embed.providers.google.key', null)
],
'soundcloud' => [
'key' => c::get('plugin.embed.providers.soundcloud.key', null)
]
];
}
}

View file

@ -0,0 +1,157 @@
<?php
namespace Kirby\Embed;
use C;
use L;
use Tpl;
class Html {
public function __construct($core) {
$this->core = $core;
$this->options = $this->core->options;
$this->data = [
'code' => $this->core->code(),
'class' => $this->core->options['class'],
'type' => $this->core->type(),
'provider' => $this->core->providerName(),
'style' => null,
'more' => null
];
}
// ================================================
// Outputs
// ================================================
public function __toString() {
// call preparation method for type
$this->prepareType();
// w3c valid code
$this->ensureValidCode();
// update embed iframe url
$this->updateData('code', function($code) {
return $this->core->url->update($code);
});
return $this->snippet('wrapper', $this->data);
}
public static function error($url, $msg = null) {
if(!$msg) $msg = 'noembed';
$msg = l::get('plugin.embed.error.' . $msg);
$path = __DIR__ . DS . 'snippets' . DS . 'error.php';
return tpl::load($path, [
'url' => $url,
'msg' => $msg
]);
}
public static function cheatsheet($parameters) {
$path = __DIR__ . DS . 'snippets' . DS . 'cheatsheet.php';
return tpl::load($path, [
'entries' => $parameters
]);
}
// ================================================
// Types
// ================================================
protected function prepareType() {
$prepareType = 'prepare' . ucfirst($this->core->type());
if(method_exists($this, $prepareType)) {
$this->{$prepareType}();
}
if(!$this->data['code']) {
$this->updateData('code', $this->error($this->core->input, 'nocode'));
$this->updateData('class', false);
}
}
// ================================================
// Videos
// ================================================
protected function prepareVideo() {
if($this->data['code']) {
// Container ratio
$this->data['style'] = 'padding-top:' . str_replace(',', '.', $this->core->aspectRatio()) . '%';
// Lazy video
if($this->options['lazyvideo'] && $this->core->supportsLazyVideo()) {
$this->lazyVideo();
}
}
}
protected function lazyVideo() {
// src -> data-src
$this->updateData('code', function($code) {
$pattern = '/(<iframe.*)(src)(="[^[:space:]]*")/';
$replace = '$1data-src$3';
return preg_replace($pattern, $replace, $code);
});
// thumb
$this->data['more'] = $this->snippet('thumb', [
'url' => $this->core->thumb(),
'alt' => $this->core->title() . ($this->core->authorName() ? ' (by ' . $this->core->authorName() . ')' : '')
]);
}
// ================================================
// Links
// ================================================
protected function prepareLink() {
if(!$this->data['code']) {
$this->updateData('code', $this->snippet('typeLink', [
'url' => $this->core->url(),
'text' => $this->core->title()
]));
}
}
// ================================================
// Code validity
// ================================================
protected function ensureValidCode() {
if(c::get('plugin.embed.w3c.enforce', false)) {
$this->updateData('code', function($code) {
$pattern = '/(frameborder="0"|allowtransparency="true")/';
return preg_replace($pattern, '', $code);
});
}
}
// ================================================
// Helpers
// ================================================
protected function updateData($data, $value) {
if(is_callable($value)) {
$this->data[$data] = $value($this->data[$data]);
} else {
$this->data[$data] = $value;
}
}
protected function snippet($name, $data) {
return tpl::load(__DIR__ . DS . 'snippets' . DS . $name . '.php', $data);
}
public static function removeEmojis($string) {
return trim(preg_replace('/[\x00-\x1F\x80-\xFF]/', '', mb_convert_encoding($string, "UTF-8")));
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Kirby\Embed;
use Dir;
class Autoloader {
public static function load($paths) {
foreach($paths as $group => $files) {
// single file
if(!is_array($files)) {
static::loadFile($group . '.php');
// directory
} else {
foreach($files as $file) {
// load all files from this directory
if($file === true) {
foreach(dir::read(dirname(dirname(__DIR__)) . DS . $group) as $file) {
static::loadFile($group . DS . $file);
}
// load specified files
} else {
static::loadFile($group . DS . $file . '.php');
}
}
}
}
}
protected static function loadFile($path) {
$file = dirname(dirname(__DIR__)) . DS . str_replace('/', DS, $path);
if(file_exists($file)) {
require_once($file);
}
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Kirby\Embed;
class Cache {
public function __construct($plugin, $url) {
$this->plugin = $plugin;
$this->key = md5($url);
// Cache DirectoryIterator
$dir = kirby()->roots()->cache() . DS . $this->plugin;
if(!file_exists($dir)) mkdir($dir);
// Cache setup
$this->cache = \cache::setup('file', ['root' => $dir]);
// Cache clean-up
if($this->cache->expired($this->key)) {
$this->cache->remove($this->key);
}
}
public function __call($name, $args = []) {
return $this->cache->{$name}($this->key, $args);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Kirby\Embed;
use C;
use F;
class Thumb {
protected $dir;
protected $root;
protected $url = '';
public function __construct($plugin, $url, $lifetime) {
$this->plugin = $plugin;
$this->source = $url;
$this->lifetime = $lifetime;
$this->root = kirby()->roots()->thumbs() . DS . '_plugins';
$this->dir = $this->root . DS . $this->plugin;
$this->extension = pathinfo(strtok($this->source, '?'), PATHINFO_EXTENSION);
$this->file = md5($this->source) . '.' . $this->extension;
$this->path = $this->dir . DS . $this->file;
$this->url = $url;
$this->cache();
}
public function __toString() {
return $this->url;
}
protected function cache() {
$cache = url('thumbs/_plugins/' . $this->plugin . '/' . $this->file);
if(f::exists($this->path) and !$this->expired()) {
return $this->url = $cache;
} else {
try {
if(is_writable(kirby()->roots()->thumbs())) {
if(!file_exists($this->root)) mkdir($this->root, 0755, true);
if(!file_exists($this->dir)) mkdir($this->dir, 0755, true);
file_put_contents($this->path, file_get_contents($this->source));
$this->url = $cache;
}
} catch (Exception $e) {}
}
}
protected function expired() {
if((f::modified($this->path) + $this->lifetime) < time()) {
return f::remove($this->path);
}
return false;
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace Kirby\Embed\Providers;
class CollegeHumor extends Provider {
public function code($code) {
$this->setAutoplay();
return $code;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Kirby\Embed\Providers;
class Meetup extends Provider {
public function code($code) {
$code .= $this->fixStyles();
return $code;
}
// ================================================
// Fix styles
// ================================================
protected function fixStyles() {
return '<style>#meetup_oembed{height: auto !important; text-align: left;}</style>';
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Kirby\Embed\Providers;
class phorkie extends Provider {
public function code($code) {
$code .= $this->fixStyles();
return $code;
}
// ================================================
// Fix styles
// ================================================
protected function fixStyles() {
return '<style>.phork {text-align: left;}</style>';
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Kirby\Embed\Providers;
class Provider {
public function __construct($core, $url) {
$this->core = $core;
$this->url = $url;
$this->init();
}
// ================================================
// Placeholder methods
// ================================================
protected function init() {}
public function supportsLazyVideo() { return $this->core->type() === 'video'; }
// ================================================
// Custom URL parameter helpers
// ================================================
protected function set($paramenter) {
if($this->{$paramenter} !== false) {
$this->parameter($paramenter . '=' . $this->{$paramenter});
}
}
protected function get($paramenter, $pattern) {
$this->{$paramenter} = preg_match('/' . $paramenter . '=(' . $pattern . ')/', $this->url, $result) ? $result[1] : false;
}
protected function getBool($paramenter) {
$this->get($paramenter, '[0-1]');
}
protected function getNumber($paramenter, $offset = 0) {
$this->get($paramenter, '[0-9]*');
if($offset !== 0) {
$this->{$paramenter} += $offset;
}
}
protected function getString($paramenter) {
$this->get($paramenter, '[a-zA-Z]*');
}
protected function getAll($paramenter) {
$this->get($paramenter, '[a-zA-Z0-9]*');
}
// ================================================
// Video Autoplay
// ================================================
protected function setAutoplay() {
if($this->option('lazyvideo') || $this->option('autoplay')) {
$this->parameter(['rel=0', 'autoplay=1', 'auto=1']);
}
}
// ================================================
// General helpers
// ================================================
protected function option($option) {
return $this->core->options[$option];
}
protected function parameter($parameter) {
return $this->core->url->parameter($parameter);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Kirby\Embed\Providers;
class Slideshare extends Provider {
protected function init() {
$this->getNumber('width');
$this->getNumber('height');
}
public function code($code) {
$code = $this->setSizes($code);
return $code;
}
// ================================================
// Parameters for Panel Field Cheatsheet
// ================================================
public function urlParameters() {
return [
['width', 'Set the width of the embed (e.g. 600)'],
['height', 'Set the height of the embed (e.g. 80)'],
];
}
// ================================================
// Sizes
// ================================================
protected function setSizes($code) {
if($this->width !== false) {
$code = str_ireplace('<iframe', '<iframe width="' . $this->width . '"', $code);
}
if($this->height !== false) {
$code = str_ireplace('<iframe', '<iframe height="' . $this->height . '"', $code);
}
return $code;
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Kirby\Embed\Providers;
class Spotify extends Provider {
protected function init() {
$this->getString('theme');
$this->getString('view');
$this->getNumber('width');
$this->getNumber('height');
}
public function code($code) {
$this->set('theme');
$this->set('view');
$code = $this->setSizes($code);
return $code;
}
// ================================================
// Parameters for Panel Field Cheatsheet
// ================================================
public function urlParameters() {
return [
['view', 'Set the view style (list/coverart)'],
['theme', 'Set the theme (white/black)'],
['width', 'Set the width of the embed (e.g. 600)'],
['height', 'Set the height of the embed (e.g. 80)'],
];
}
// ================================================
// Sizes
// ================================================
protected function setSizes($code) {
if($this->width !== false) {
$code = str_ireplace('<iframe', '<iframe width="' . $this->width . '"', $code);
}
if($this->height !== false) {
$code = str_ireplace('<iframe', '<iframe height="' . $this->height . '"', $code);
}
return $code;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Kirby\Embed\Providers;
class TED extends Provider {
public function supportsLazyVideo() { return false; }
}

View file

@ -0,0 +1,23 @@
<?php
namespace Kirby\Embed\Providers;
class Ustream extends Provider {
public function code($code) {
$this->setAutoplay();
return $code;
}
// ================================================
// Autoplay
// ================================================
protected function setAutoplay() {
if($this->option('lazyvideo') || $this->option('autoplay')) {
$this->parameter('autoplay=true');
}
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Kirby\Embed\Providers;
class Vimeo extends Provider {
protected function init() {
$this->getBool('autopause');
$this->getBool('badge');
$this->getBool('byline');
$this->getAll('color');
$this->getBool('loop');
$this->getBool('portrait');
$this->getBool('title');
}
public function code($code) {
$this->setAutoplay();
$this->set('autopause');
$this->set('badge');
$this->set('byline');
$this->set('color');
$this->set('loop');
$this->set('portrait');
$this->set('title');
$this->setJsApi();
return $code;
}
public function urlParameters() {
return [
['autopause', 'Enables or disables pausing this video when another video is played (1/0)'],
['badge', 'Enables or disables the badge on the video (1/0)'],
['byline', 'Show the users byline on the video (1/0)'],
['color', 'Specify the color of the video controls (e.g. 00aa00)'],
['loop', 'Play the video again when it reaches the end (1/0)'],
['portrait', 'Show the users portrait on the video (1/0)'],
['title', 'Show the title on the video (1/0)'],
];
}
// ================================================
// JS API
// ================================================
protected function setJsApi() {
if($this->option('jsapi')) {
$this->parameter('api=1');
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Kirby\Embed\Providers;
class Vine extends Provider {
public function supportsLazyVideo() { return false; }
}

View file

@ -0,0 +1,65 @@
<?php
namespace Kirby\Embed\Providers;
class YouTube extends Provider {
protected function init() {
$this->getAll('t');
$this->getNumber('index');
}
public function code($code) {
$this->setAutoplay();
$this->setTimecode();
$this->set('index');
$this->setJsApi();
return $code;
}
// ================================================
// Parameters for Panel Field Cheatsheet
// ================================================
public function urlParameters() {
return [
['t', 'Timecode where the video should start (e.g. 1m4s)'],
];
}
// ================================================
// Timecode
// ================================================
protected function setTimecode() {
if($this->t !== false) {
$this->parameter('start=' . $this->calculateTimecode());
}
}
protected function calculateTimecode() {
$time = $this->disectTimecode('h') * 60 * 60;
$time += $this->disectTimecode('m') * 60 ;
$time += $this->disectTimecode('s');
return $time;
}
protected function disectTimecode($identifier) {
return preg_match('/([0-9]+)' . $identifier . '/i', $this->t, $match) ? $match[0] : 0;
}
// ================================================
// JS API
// ================================================
protected function setJsApi() {
if($this->option('jsapi')) {
$this->parameter('enablejsapi=1');
}
}
}

View file

@ -0,0 +1,14 @@
<?php if(is_array($entries)) : ?>
<table class="field-embed-cheatsheet__table">
<tr class="field-embed-cheatsheet__th">
<td>URL parameter</td>
<td>Description</td>
</tr>
<?php foreach($entries as $entry) : ?>
<tr>
<td><?= $entry[0] ?></td>
<td><?= $entry[1] ?></td>
</tr>
<?php endforeach ?>
</table>
<?php endif ?>

View file

@ -0,0 +1 @@
<figure class="embed embed--error"><?= $msg ?> <span><?= $url ?></span></figure>

View file

@ -0,0 +1 @@
<div class="embed__thumb" style="background-image:url('<?= $url ?>');" title="<?= $alt ?>"><img src="<?= url(c::get('plugin.embed.video.lazyload.btn', 'assets/plugins/embed/images/play.png')) ?>" alt="" width="175" height="110" /></div>

View file

@ -0,0 +1 @@
<div class="embed--link__fallback"><a href="<?= $url ?>"><?= $text ?></a></div>

View file

@ -0,0 +1 @@
<figure class="embed <?php e($class !== false, 'embed--' . $type) ?> embed--<?= $provider ?> <?= $class ?>" style="<?= $style ?>"><?= $code ?><?= $more ?></figure>

View file

@ -0,0 +1,60 @@
<?php
namespace Kirby\Embed;
class Url {
public $parameters = [];
// ================================================
// Extract URL from code
// ================================================
public function __construct($code) {
if(preg_match('/(src=")(.*)(")/U', $code, $match)) {
$this->url = $match[2];
} else {
$this->url = false;
}
}
// ================================================
// Get new URL with parameters
// ================================================
public function get() {
$newParameters = implode('&', $this->parameters);
$hasParameter = preg_match('/\?/', $this->url);
return $this->url . ($hasParameter ? '&' : '?') . $newParameters;
}
// ================================================
// Update code with new URL
// ================================================
public function update($code) {
if($this->url !== false) {
$pattern = '/(src|data-src)(=")(.*)(")/U';
$order = '$1$2' . $this->get() . '$4';
$code = preg_replace($pattern, $order, $code);
}
return $code;
}
// ================================================
// Add parameter(s)
// ================================================
public function parameter($parameter) {
if(!is_array($parameter)) $parameter = [$parameter];
$this->parameters = array_merge($this->parameters, $parameter);
}
}