You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
8.4 KiB
Plaintext

<?php
/**
* ProcessWire Video Embedding Textformatter
*
* Looks for Youtube or Vimeo URLs and automatically converts them to embeds
*
* ProcessWire 2.x
* Copyright (C) 2012 by Ryan Cramer
* Licensed under GNU/GPL v2, see LICENSE.TXT
*
* http://processwire.com
*
* 1.0.4: Added 'responsive' option and made text translatable.
*
*
*/
class TextformatterVideoEmbed extends Textformatter implements ConfigurableModule {
public static function getModuleInfo() {
return array(
'title' => __('Video embed for YouTube/Vimeo', __FILE__),
'version' => 109,
'summary' => __('Enter a full YouTube or Vimeo URL by itself in any paragraph (example: http://www.youtube.com/watch?v=Wl4XiYadV_k) and this will automatically convert it to an embedded video. This formatter is intended to be run on trusted input. Recommended for use with TinyMCE textarea fields.', __FILE__),
'author' => 'Ryan Cramer',
'href' => 'http://modules.processwire.com/modules/textformatter-video-embed/'
);
}
const dbTableName = 'textformatter_video_embed';
protected static $configDefaults = array(
'maxWidth' => 640,
'maxHeight' => 480,
'responsive' => 0,
);
/**
* Data as used by the get/set functions
*
*/
protected $data = array();
/**
* Either http or https, depending on $config->https
*
*/
protected $http = '';
/**
* Set our configuration defaults
*
*/
public function __construct() {
foreach(self::$configDefaults as $key => $value) {
$this->set($key, $value);
}
$this->http = $this->config->https ? 'https' : 'http';
}
/**
* Given a service oembed URL and video ID, return the corresponding embed code.
*
* A cached version of the embed code will be used if possible. When not possible,
* it will be retrieved from the service's oembed URL, and then cached.
*
*/
protected function getEmbedCode($oembedURL, $videoID) {
$db = wire('db');
$videoID = $db->escape_string($videoID);
$result = $db->query("SELECT embed_code FROM " . self::dbTableName . " WHERE video_id='$videoID'");
if($result->num_rows) {
list($embedCode) = $result->fetch_row();
} else {
$data = file_get_contents($oembedURL);
if($data) $data = json_decode($data, true);
if(is_array($data) && isset($data['html'])) {
$embedCode = $data['html'];
$sql = "INSERT INTO " . self::dbTableName . " SET " .
"video_id='$videoID', " .
"embed_code='" . $db->escape_string($embedCode) . "', " .
"created=NOW() ";
$db->query($sql);
}
$result->free();
}
// account for possibility that stored embed code contains http version while requested on https
if($this->http == 'https') $embedCode = str_replace('http://', 'https://', $embedCode);
return $embedCode;
}
/**
* Make an iframe-based embed code responsive
*
*/
protected function makeResponsive($out) {
$out = str_ireplace('<iframe ', "<iframe style='position:absolute;top:0;left:0;width:100%;height:100%;' ", $out);
$out = "<div class='TextformatterVideoEmbed' style='position:relative;padding:30px 0 56.25% 0;height:0;overflow:hidden;'>$out</div>";
return $out;
}
/**
* Text formatting function as used by the Textformatter interface
*
* Here we look for video codes on first pass using a fast strpos() function.
* When found, we do our second pass with preg_match_all and replace the video URLs
* with the proper embed codes obtained from each service's oembed web service.
*
*/
public function format(&$str) {
$this->embedYoutube($str);
$this->embedVimeo($str);
}
/**
* Check for Youtube URLS and embed when found
*
*/
protected function embedYoutube(&$str) {
// perform a strpos fast check before performing regex check
if(strpos($str, '://www.youtube.com/watch') === false && strpos($str, '://www.youtube.com/v/') === false && strpos($str, '://youtu.be/') === false) return;
// 1: full URL 2:video id 3: query string (optional)
$regex = '#<p>\s*(https?://(?:www\.)?youtu(?:.be|be.com)+/(?:watch/?\?v=|v/)?([^\s&<\'"]+))(&[-_,.=&;a-zA-Z0-9]*)?.*?</p>#';
if(!preg_match_all($regex, $str, $matches)) return;
foreach($matches[0] as $key => $line) {
$oembedURL =
"$this->http://www.youtube.com/oembed?url=" . urlencode($matches[1][$key]) .
"&format=json&maxwidth={$this->maxWidth}&maxheight={$this->maxHeight}";
$videoID = $matches[2][$key];
$queryString = isset($matches[3][$key]) ? $matches[3][$key] : '';
$embedCode = $this->getEmbedCode($oembedURL, $videoID);
if($embedCode) {
if(strlen($queryString)) {
$queryString = str_replace('&amp;', '&', $queryString);
$queryString = trim($queryString, '&');
$embedCode = str_replace("?", "?$queryString&", $embedCode);
}
if($this->responsive) $embedCode = $this->makeResponsive($embedCode);
$str = str_replace($line, $embedCode, $str);
}
}
}
/**
* Check for Vimeo URLS and embed when found
*
*/
protected function embedVimeo(&$str) {
if(strpos($str, '://vimeo.com/') === false) return;
if(!preg_match_all('#<p>\s*(https?://vimeo.com/(\d+)).*?</p>#', $str, $matches)) return;
foreach($matches[0] as $key => $line) {
$oembedURL =
"$this->http://vimeo.com/api/oembed.json?url=" . urlencode($matches[1][$key]) .
"&maxwidth={$this->maxWidth}&maxheight={$this->maxHeight}";
$videoID = $matches[2][$key];
$embedCode = $this->getEmbedCode($oembedURL, $videoID);
if($this->responsive) $embedCode = $this->makeResponsive($embedCode);
if($embedCode) $str = str_replace($line, $embedCode, $str);
}
}
/**
* Module configuration screen
*
*/
public static function getModuleConfigInputfields(array $data) {
foreach(self::$configDefaults as $key => $value) {
if(!isset($data[$key])) $data[$key] = $value;
}
unset($data['cacheClear']);
$inputfields = new InputfieldWrapper();
$f = wire('modules')->get('InputfieldInteger');
$f->attr('name', 'maxWidth');
$f->attr('value', $data['maxWidth']);
$f->label = __('Max Video Width');
$inputfields->add($f);
$f = wire('modules')->get('InputfieldInteger');
$f->attr('name', 'maxHeight');
$f->attr('value', $data['maxHeight']);
$f->label = __('Max Video Height');
$inputfields->add($f);
$f = wire('modules')->get('InputfieldCheckbox');
$f->attr('name', 'responsive');
$f->attr('value', 1);
if($data['responsive']) $f->attr('checked', 'checked');
$f->label = __('Use Responsive Embed Method?');
$f->description = __('When checked, videos will be embedded in a manner that allows them to be presented in a width-flexible format.');
$inputfields->add($f);
if(wire('input')->post('clearCache')) {
wire('db')->query("DELETE FROM " . self::dbTableName);
wire('modules')->message(__('Cleared video embed cache'));
} else {
$result = wire('db')->query("SELECT COUNT(*) FROM " . self::dbTableName);
list($n) = $result->fetch_row();
$f = wire('modules')->get('InputfieldCheckbox');
$f->attr('name', 'clearCache');
$f->attr('value', 1);
$f->label = __('Clear video cache?');
$f->description = __('This will clear out cached embed codes. There is no harm in doing this, other than that it will force them to be re-pulled from YouTube/Vimeo as needed.');
$f->notes = sprintf(__('There are currently %d video(s) cached'), $n);
$inputfields->add($f);
}
return $inputfields;
}
/**
* Installation routine
*
*/
public function ___install() {
if(!ini_get('allow_url_fopen')) {
throw new WireException("Your PHP has allow_url_fopen disabled, which is required by this module.");
}
$sql = "CREATE TABLE " . self::dbTableName . " (" .
"video_id VARCHAR(128) NOT NULL PRIMARY KEY, " .
"embed_code VARCHAR(1024) NOT NULL DEFAULT '', " .
"created TIMESTAMP NOT NULL " .
")";
wire('db')->query($sql);
}
/**
* Uninstallation routine
*
*/
public function ___uninstall() {
try { wire('db')->query("DROP TABLE " . self::dbTableName); } catch(Exception $e) { }
}
/**
* The following functions are to support the ConfigurableModule interface
* since Textformatter does not originate from WireData
*
*/
public function set($key, $value) {
$this->data[$key] = $value;
return $this;
}
public function get($key) {
$value = Wire::getFuel($key);
if($value) return $value;
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function __set($key, $value) {
$this->set($key, $value);
}
public function __get($key) {
return $this->get($key);
}
}