2012-05-22 06:45:36 +02:00
< ? php
2012-05-24 06:04:21 +02:00
/**
* Format of the Oembed config . Autodiscover allows discovery of all URLs .
* Endpoint set to true means autodiscovery for this specific provider is allowed
* ( even if autodiscovery in general has been disabled ) .
*
* Oembed :
* providers :
* { pattern } :
* { endpoint } / true
* autodiscover :
* true / false
*/
2012-05-22 06:45:36 +02:00
class Oembed {
2012-06-08 18:30:02 +02:00
public static function is_enabled () {
return Config :: inst () -> get ( 'Oembed' , 'enabled' );
}
2012-05-24 06:04:21 +02:00
/**
* Gets the autodiscover setting from the config .
*/
2012-05-22 06:45:36 +02:00
public static function get_autodiscover () {
return Config :: inst () -> get ( 'Oembed' , 'autodiscover' );
}
2012-05-24 06:04:21 +02:00
/**
* Gets providers from config .
*/
2012-05-22 06:45:36 +02:00
public static function get_providers () {
return Config :: inst () -> get ( 'Oembed' , 'providers' );
}
2012-05-24 06:04:21 +02:00
/**
* Returns an endpoint ( a base Oembed URL ) from first matching provider .
*
* @ param $url Human - readable URL .
* @ returns string / bool URL of an endpoint , or false if no matching provider exists .
*/
protected static function find_endpoint ( $url ) {
2012-05-22 06:45:36 +02:00
foreach ( self :: get_providers () as $scheme => $endpoint ) {
2012-05-24 06:04:21 +02:00
if ( self :: matches_scheme ( $url , $scheme )) {
2012-05-22 06:45:36 +02:00
return $endpoint ;
}
}
return false ;
}
2012-05-24 06:04:21 +02:00
/**
* Checks the URL if it matches against the scheme ( pattern ) .
*
* @ param $url Human - readable URL to be checked .
* @ param $scheme Pattern to be matched against .
* @ returns bool Whether the pattern matches or not .
*/
protected static function matches_scheme ( $url , $scheme ) {
2012-05-22 06:45:36 +02:00
$urlInfo = parse_url ( $url );
$schemeInfo = parse_url ( $scheme );
foreach ( $schemeInfo as $k => $v ) {
if ( ! array_key_exists ( $k , $urlInfo )) {
return false ;
}
if ( strpos ( $v , '*' ) !== false ) {
$v = preg_quote ( $v , '/' );
$v = str_replace ( '\*' , '.*' , $v );
if ( $k == 'host' ) {
$v = str_replace ( '*\.' , '*' , $v );
}
if ( ! preg_match ( '/' . $v . '/' , $urlInfo [ $k ])) {
return false ;
}
} elseif ( strcasecmp ( $urlInfo [ $k ], $v )) {
return false ;
}
}
return true ;
}
2012-05-24 06:04:21 +02:00
/**
* Performs a HTTP request to the URL and scans the response for resource links
* that mention oembed in their type .
*
* @ param $url Human readable URL .
* @ returns string / bool Oembed URL , or false .
*/
2012-05-22 06:45:36 +02:00
protected static function autodiscover_from_url ( $url ) {
2012-06-08 18:28:55 +02:00
// Fetch the URL (cache for a week by default)
$service = new RestfulService ( $url , 60 * 60 * 24 * 7 );
2012-05-22 06:45:36 +02:00
$body = $service -> request ();
if ( ! $body || $body -> isError ()) {
return false ;
}
$body = $body -> getBody ();
2012-05-24 06:04:21 +02:00
// Look within the body for an oembed link.
2012-05-22 06:45:36 +02:00
if ( preg_match_all ( '#<link[^>]+?(?:href=[\'"](.+?)[\'"][^>]+?)?type=["\']application/json\+oembed["\'](?:[^>]+?href=[\'"](.+?)[\'"])?#' , $body , $matches , PREG_SET_ORDER )) {
$match = $matches [ 0 ];
if ( ! empty ( $match [ 1 ])) {
return html_entity_decode ( $match [ 1 ]);
}
if ( ! empty ( $match [ 2 ])) {
return html_entity_decode ( $match [ 2 ]);
}
}
return false ;
}
2012-05-24 06:04:21 +02:00
/**
* Takes the human - readable URL of an embeddable resource and converts it into an
* Oembed_Result descriptor ( which contains a full Oembed resource URL ) .
*
* @ param $url Human - readable URL
* @ param $type ?
* @ param $options array Options to be used for constructing the resulting descriptor .
* @ returns Oembed_Result / bool An Oembed descriptor , or false
*/
public static function get_oembed_from_url ( $url , $type = false , array $options = array ()) {
2012-06-08 18:30:02 +02:00
if ( ! self :: is_enabled ()) return false ;
2012-05-24 06:04:21 +02:00
// Find or build the Oembed URL.
$endpoint = self :: find_endpoint ( $url );
$oembedUrl = false ;
2012-05-22 06:45:36 +02:00
if ( ! $endpoint ) {
if ( self :: get_autodiscover ()) {
2012-05-24 06:04:21 +02:00
$oembedUrl = self :: autodiscover_from_url ( $url );
2012-05-22 06:45:36 +02:00
}
} elseif ( $endpoint === true ) {
2012-05-24 06:04:21 +02:00
$oembedUrl = self :: autodiscover_from_url ( $url );
2012-05-22 06:45:36 +02:00
} else {
2012-05-24 06:04:21 +02:00
// Build the url manually - we gave all needed information.
$oembedUrl = Controller :: join_links ( $endpoint , '?format=json&url=' . rawurlencode ( $url ));
2012-05-22 06:45:36 +02:00
}
2012-06-25 17:19:15 +02:00
// If autodescovery failed the resource might be a direct link to a file
if ( ! $oembedUrl ) {
2012-06-25 17:23:26 +02:00
if ( File :: get_app_category ( File :: get_file_extension ( $url )) == " image " ) {
2012-06-25 17:19:15 +02:00
return new Oembed_Result ( $url , $url , $type , $options );
}
}
2012-05-24 06:04:21 +02:00
if ( $oembedUrl ) {
// Inject the options into the Oembed URL.
2012-05-22 06:45:36 +02:00
if ( $options ) {
if ( isset ( $options [ 'width' ]) && ! isset ( $options [ 'maxwidth' ])) {
$options [ 'maxwidth' ] = $options [ 'width' ];
}
if ( isset ( $options [ 'height' ]) && ! isset ( $options [ 'maxheight' ])) {
$options [ 'maxheight' ] = $options [ 'height' ];
}
2012-05-24 06:04:21 +02:00
$oembedUrl = Controller :: join_links ( $oembedUrl , '?' . http_build_query ( $options , '' , '&' ));
2012-05-22 06:45:36 +02:00
}
2012-05-24 06:04:21 +02:00
return new Oembed_Result ( $oembedUrl , $url , $type , $options );
2012-05-22 06:45:36 +02:00
}
2012-05-24 06:04:21 +02:00
// No matching Oembed resource found.
2012-05-22 06:45:36 +02:00
return false ;
}
public static function handle_shortcode ( $arguments , $url , $parser , $shortcode ) {
if ( isset ( $arguments [ 'type' ])) {
$type = $arguments [ 'type' ];
unset ( $arguments [ 'type' ]);
} else {
$type = false ;
}
$oembed = self :: get_oembed_from_url ( $url , $type , $arguments );
if ( $oembed && $oembed -> exists ()) {
return $oembed -> forTemplate ();
} else {
return '<a href="' . $url . '">' . $url . '</a>' ;
}
}
}
class Oembed_Result extends ViewableData {
2012-05-24 06:04:21 +02:00
/**
* JSON data fetched from the Oembed URL .
* This data is accessed dynamically by getField and hasField .
*/
2012-05-22 06:45:36 +02:00
protected $data = false ;
2012-05-24 06:04:21 +02:00
/**
* Human readable URL
*/
2012-05-22 06:45:36 +02:00
protected $origin = false ;
2012-05-24 06:04:21 +02:00
/**
* ?
*/
2012-05-22 06:45:36 +02:00
protected $type = false ;
2012-05-24 06:04:21 +02:00
/**
* Oembed URL
*/
2012-05-22 06:45:36 +02:00
protected $url ;
2012-05-24 06:04:21 +02:00
/**
* Class to be injected into the resulting HTML element .
*/
2012-05-22 06:45:36 +02:00
protected $extraClass ;
public static $casting = array (
'html' => 'HTMLText' ,
);
2012-05-24 06:04:21 +02:00
public function __construct ( $url , $origin = false , $type = false , array $options = array ()) {
2012-05-22 06:45:36 +02:00
$this -> url = $url ;
$this -> origin = $origin ;
$this -> type = $type ;
if ( isset ( $options [ 'class' ])) {
$this -> extraClass = $options [ 'class' ];
}
parent :: __construct ();
}
2012-05-24 06:51:30 +02:00
public function getOembedURL () {
return $this -> url ;
}
2012-05-22 06:45:36 +02:00
2012-05-24 06:04:21 +02:00
/**
* Fetches the JSON data from the Oembed URL ( cached ) .
* Only sets the internal variable .
*/
2012-05-22 06:45:36 +02:00
protected function loadData () {
if ( $this -> data !== false ) {
return ;
}
2012-05-24 06:04:21 +02:00
2012-06-08 18:28:55 +02:00
// Fetch from Oembed URL (cache for a week by default)
$service = new RestfulService ( $this -> url , 60 * 60 * 24 * 7 );
2012-05-22 06:45:36 +02:00
$body = $service -> request ();
if ( ! $body || $body -> isError ()) {
$this -> data = array ();
return ;
}
$body = $body -> getBody ();
$data = json_decode ( $body , true );
if ( ! $data ) {
2012-06-25 17:19:15 +02:00
// if the response is no valid JSON we might have received a binary stream to an image
2012-05-22 06:45:36 +02:00
$data = array ();
2012-06-25 17:19:15 +02:00
$image = @ imagecreatefromstring ( $body );
if ( $image !== FALSE ) {
preg_match ( " /^(http: \ / \ /)?([^ \ /]+)/i " , $this -> url , $matches );
$protocoll = $matches [ 1 ];
$host = $matches [ 2 ];
$data [ 'type' ] = " photo " ;
$data [ 'title' ] = basename ( $this -> url ) . " ( $host ) " ;
$data [ 'url' ] = $this -> url ;
$data [ 'provider_url' ] = $protocoll . $host ;
$data [ 'width' ] = imagesx ( $image );
$data [ 'height' ] = imagesy ( $image );
2012-06-25 17:23:26 +02:00
$data [ 'info' ] = _t ( 'UploadField.HOTLINKINFO' , 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.' );
2012-06-25 17:19:15 +02:00
}
2012-05-22 06:45:36 +02:00
}
2012-05-24 06:04:41 +02:00
// Convert all keys to lowercase
$data = array_change_key_case ( $data , CASE_LOWER );
// Purge everything if the type does not match.
2012-05-22 06:45:36 +02:00
if ( $this -> type && $this -> type != $data [ 'type' ]) {
$data = array ();
}
2012-05-24 06:04:21 +02:00
2012-05-22 06:45:36 +02:00
$this -> data = $data ;
}
2012-05-24 06:04:21 +02:00
/**
* Wrap the check for looking into Oembed JSON within $this -> data .
*/
2012-05-22 06:45:36 +02:00
public function hasField ( $field ) {
$this -> loadData ();
return array_key_exists ( strtolower ( $field ), $this -> data );
}
2012-05-24 06:04:21 +02:00
/**
* Wrap the field calls to fetch data from Oembed JSON ( within $this -> data )
*/
2012-05-22 06:45:36 +02:00
public function getField ( $field ) {
$field = strtolower ( $field );
if ( $this -> hasField ( $field )) {
return $this -> data [ $field ];
}
}
public function forTemplate () {
$this -> loadData ();
switch ( $this -> Type ) {
case 'video' :
case 'rich' :
if ( $this -> extraClass ) {
return " <div class=' $this->extraClass '> $this->HTML </div> " ;
} else {
return $this -> HTML ;
}
break ;
case 'link' :
return '<a class="' . $this -> extraClass . '" href="' . $this -> origin . '">' . $this -> Title . '</a>' ;
break ;
case 'photo' :
return " <img src=' $this->URL ' width=' $this->Width ' height=' $this->Height ' class=' $this->extraClass ' /> " ;
break ;
}
}
public function exists () {
$this -> loadData ();
return count ( $this -> data ) > 0 ;
}
}