<?php
// $Id$
/**
* @file
* Core functionality for oEmbed
*/
/**
* Implementation of hook_perm().
*/
function oembedcore_perm() {
return array('administer oembed presets');
}
/**
* Implementation of hook_menu().
*/
function oembedcore_menu() {
$items = array();
$base = array(
'access arguments' => array('administer oembed presets'),
'file' => 'oembedcore.admin.inc',
);
$items['admin/build/oembed'] = array(
'title' => 'OEmbed',
'description' => 'Add, edit and remove oembed preset from the system.',
'page callback' => 'oembedcore_list_preset',
) + $base;
$items['admin/build/oembed/list'] = array(
'title' => 'List',
'page callback' => 'oembedcore_list_preset',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
) + $base;
$items['admin/build/oembed/add'] = array(
'title' => 'Add preset',
'page callback' => 'oembedcore_add_preset',
'type' => MENU_LOCAL_TASK,
) + $base;
$items['admin/build/oembed/%oembedcore_preset/edit'] = array(
'title' => 'Edit preset',
'page callback' => 'oembedcore_edit_preset',
'page arguments' => array(3),
'type' => MENU_LOCAL_TASK,
) + $base;
$items['admin/build/oembed/%oembedcore_preset/export'] = array(
'title' => 'Export preset',
'page callback' => 'drupal_get_form',
'page arguments' => array('oembedcore_export_preset', 3),
'type' => MENU_LOCAL_TASK,
) + $base;
$items['admin/build/oembed/%oembedcore_preset/delete'] = array(
'title' => 'Delete preset',
'page callback' => 'drupal_get_form',
'page arguments' => array('oembedcore_delete_confirm_preset', 3),
'type' => MENU_CALLBACK,
) + $base;
$items['admin/build/oembed/%oembedcore_preset/disable'] = array(
'page callback' => 'oembedcore_disable_preset',
'page arguments' => array(3),
'type' => MENU_CALLBACK,
) + $base;
$items['admin/build/oembed/%oembedcore_preset/enable'] = array(
'page callback' => 'oembedcore_enable_preset',
'page arguments' => array(3),
'type' => MENU_CALLBACK,
) + $base;
return $items;
}
/**
* Implementation of hook_theme().
*/
function oembedcore_theme() {
$path = drupal_get_path('module', 'oembedcore') . '/theme';
return array(
'oembed' => array(
'template' => 'oembed',
'file' => 'oembedcore_theme.inc',
'path' => $path,
'arguments' => array('embed' => NULL),
),
);
}
/**
* Implementation of hook_oembedproviders().
*/
function oembedcore_oembedprovider() {
$oembeddable = array(
'flickr' => array(
'scheme' => array('http://*.flickr.com/*', 'http://flickr.com/*'),
'endpoint' => 'http://www.flickr.com/services/oembed/',
),
'viddler' => array(
'scheme' => 'http://*.viddler.com/*',
'endpoint' => 'http://lab.viddler.com/services/oembed/',
),
'qik' => array(
'scheme' => array('http://qik.com/video/*', 'http://qik.com/*'),
'endpoint' => 'http://qik.com/api/oembed.json',
),
'revision3' => array(
'scheme' => 'http://*.revision3.com/*',
'endpoint' => 'http://revision3.com/api/oembed/',
),
'vimeo' => array(
'scheme' => array('http://www.vimeo.com/*', 'http://www.vimeo.com/groups/*/*'),
'endpoint' => 'http://www.vimeo.com/api/oembed.json',
),
'youtube' => array(
'scheme' => 'http://*.youtube.com/watch*',
'endpoint' => 'http://www.youtube.com/oembed',
),
'oohembed' => array(
'scheme' => array(
'http://*.wikipedia.org/wiki/*',
'http://*.slideshare.net/*',
'http://*.imdb.com/title/tt*/',
'http://*.collegehumor.com/video:*',
),
'endpoint' => 'http://oohembed.com/oohembed/',
),
);
$providers = array();
foreach ($oembeddable as $site) {
foreach ((array)$site['scheme'] as $scheme) {
$providers[$scheme] = array(
'endpoint' => $site['endpoint'],
);
}
}
return $providers;
}
/**
* Returns the provider for a url.
*
* @param string $url
* Teh url to get the provider for.
* @return mixed
* A valid callback or FALSE
*/
function oembedcore_get_provider($url, &$matches) {
$host = _oembedcore_get_host($url);
if ($host) {
$providers = oembedcore_providers($host);
foreach ($providers as $regex => $info) {
if (preg_match($regex, $url, $matches)) {
return $info;
}
}
}
return FALSE;
}
/**
* A utility function to get the base domain from a url.
*
* @param string $uri
* The uri to get the domain form
* @return string
* The domain or NULL
*/
function _oembedcore_get_host($uri) {
$matches = array();
if (preg_match('/^https?\:\/\/([^\/]+)/', $uri, $matches)) {
$matches = explode('.', $matches[1]);
$match_count = count($matches);
if ($match_count > 1) {
return $matches[$match_count - 2] . '.' . $matches[$match_count - 1];
}
else {
return $matches[0];
}
}
return NULL;
}
/**
* Returns all the registered providers, or the providers for a specific host.
*
* @param string $host
* Optional. Supply a hostname if you only want the provider patterns for a specific host.
* @return array
*/
function oembedcore_providers($url_host = NULL) {
static $providers;
if (!$providers) {
$cache_key = 'oembedcore:providers';
if (($cache = cache_get($cache_key)) && isset($cache->data)) {
$providers = $cache->data;
}
else {
$providers = array();
$modules = module_implements('oembedprovider');
foreach ($modules as $module) {
$ps = call_user_func($module . '_oembedprovider');
foreach ($ps as $pattern => $info) {
$host = _oembedcore_get_host($pattern);
$regex_pattern = '/' . str_replace('\*', '(.+)', preg_quote($pattern, '/')) . '/';
$providers[$host][$regex_pattern] = $info;
}
}
drupal_alter('oembedprovider', $providers);
foreach ($providers as $host => &$patterns) {
uksort($patterns, '_oembedcore_specificity_compare');
}
}
cache_set($cache_key, $providers);
}
if ($url_host) {
return isset($providers[$url_host]) ? $providers[$url_host] : array();
}
return $providers;
}
/**
* Helper function that compares the length of match expressions.
*/
function _oembedcore_specificity_compare($a, $b) {
return strlen($b) - strlen($a);
}
/**
* Fetch data for an embeddable URL.
*
* @param string $url
* An external URL for the content to embed.
* @param array $attributes
* An associative array of attributes, with the following keys:
* - 'maxwidth'
* The maximum width of the embed, in pixels.
* - 'maxheight'
* The maximum height of the embed, in pixels.
* @return
* False or an object representing the embeddable data of the URL.
*/
function oembedcore_oembed_data($url, $attributes = array()) {
$matches = array();
if ($provider = oembedcore_get_provider($url, $matches)) {
return oembedcore_oembed_fetch($provider, $url, $matches, $attributes);
}
return FALSE;
}
function oembedcore_oembed_fetch($provider, $url, $matches, $attributes = array()) {
global $_oembed_default_attributes;
if (!empty($_oembed_default_attributes)) {
$attributes = array_merge($_oembed_default_attributes, $attributes);
}
$attributes['url'] = $url;
$query = http_build_query($attributes, NULL, '&');
$source = isset($provider['callback']) ? $provider['callback'] : $provider['endpoint'];
$cache_key = 'oembedcore:embed:' . md5($source . $url . $query);
$cache = cache_get($cache_key);
if ($cache && isset($cache->data)) {
$embed = $cache->data;
}
else {
if (!empty($provider['callback'])) {
$embed = (object)call_user_func($provider['callback'], $provider, $url, $matches, $attributes);
}
else {
$fetch_url = $provider['endpoint'] . '?' . $query;
$response = @file_get_contents($fetch_url);
if (!$response) {
return NULL;
}
$embed = json_decode($response);
if (!is_object($embed)) {
$embed = new SimpleXMLElement($response);
$embed = (object)get_object_vars($embed);
if (!is_string($embed->title)) {
$embed->title = '';
}
}
}
$embed->original_url = $url;
$max_age = isset($embed->cache_age) ? intval($embed->cache_age) : empty($provider['callback']) ? 600 : 60;
cache_set($cache_key, $embed, 'cache', time() + $max_age);
}
return $embed;
}
/**
* To be used for HTML in cases where the HTML is cached independent of the theme - like in the case of input filters.
*/
function oembedcore_oembed_html($embed, $url) {
//TODO: Maybe refactor into something that uses drupal_render()?
$return = '';
switch ($embed->type) {
case 'photo':
$return = '<span class="oembed">';
if (!empty($embed->title)) {
$return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title')));
}
$return .= ' ' . l('<img src="' . check_url($embed->url) . '" />', $url, array('html' => TRUE, 'absolute' => TRUE, 'attributes' => array('class' => 'oembed-photo oembed-content')));
$return .= '</span>';
break;
case 'rich':
case 'video':
$return = '<div class="oembed">';
if (!empty($embed->title)) {
$return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title')));
}
$return .= ' <span class="oembed-content oembed-' . ($embed->type == 'video' ? 'video' : 'rich') . '">' . $embed->html . '</span>';
$return .= '</div>';
break;
case 'link':
$return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title oembed-link')));
break;
default:
}
return $return;
}
// --------------------------------------------------------------------------
// Preset database info.
/**
* Clear presets cache on admin/build/modules form.
*/
function oembedcore_form_system_modules_alter(&$form, $form_state) {
//Copied from imagecache - needed because other modules might contain presets which oembedfield uses and cck caches
//TODO: Extract this and move to oembedfield
if (module_exists('content')) {
content_clear_type_cache();
}
}
/**
* Create a new preset with defaults appropriately set from schema.
*/
function oembedcore_preset_new() {
ctools_include('export');
return ctools_export_new_object('oembedcore_preset');
}
/**
* Load a single preset
*/
function oembedcore_preset_load($name) {
ctools_include('export');
$result = ctools_export_load_object('oembedcore_preset', 'names', array($name));
if (isset($result[$name])) {
return $result[$name];
}
else {
return FALSE;
}
}
/**
* Load all presets.
*/
function oembedcore_preset_load_all() {
ctools_include('export');
return ctools_export_load_object('oembedcore_preset');
}
/**
* Write a preset to the database.
*/
function oembedcore_preset_save(&$preset) {
$update = (isset($preset->pid)) ? array('pid') : array();
drupal_write_record('oembedcore_preset', $preset, $update);
// Clear the content.module cache (refreshes the list of formatters provided by oembedfield.module).
//TODO: Extract this and move to oembedfield
if (module_exists('content')) {
content_clear_type_cache();
drupal_rebuild_theme_registry();
}
return $preset;
}
/**
* Remove a preset.
*/
function oembedcore_preset_delete($preset) {
db_query("DELETE FROM {oembedcore_preset} WHERE name = '%s' AND pid = %d", $preset->name, $preset->pid);
// Clear the content.module cache (refreshes the list of formatters provided by oembedfield.module).
//TODO: Extract this and move to oembedfield
if (module_exists('content')) {
content_clear_type_cache();
drupal_rebuild_theme_registry();
}
}
/**
* Export a preset
*/
function oembedcore_preset_export($preset, $indent = '') {
ctools_include('export');
$output = ctools_export_object('oembedcore_preset', $preset, $indent);
return $output;
}
/**
* Lists all available presets
*/
function oembedcore_preset_list() {
$return = array();
$presets = oembedcore_preset_load_all();
foreach ($presets as $preset) {
$return[$preset->name] = $preset->name;
}
return $return;
}