oembed.module in oEmbed 8
Same filename and directory in other branches
Core functionality for oEmbed
File
oembed.moduleView source
<?php
/**
* @file
* Core functionality for oEmbed
*/
/**
* Implements hook_hook_info().
*/
function oembed_hook_info() {
$hooks = array(
'oembed_request_alter',
'oembed_response_alter',
);
return array_fill_keys($hooks, array(
'group' => 'oembed',
));
}
/**
* Implements hook_help().
*/
function oembed_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.oembed':
$output = '<p>' . t('oEmbed module will allow your Drupal site to embed content from <a href="@oembed">oEmbed</a>-providers as well as for the site to become an oEmbed-provider itself so that other oEmbed-enabled websites can easily embed your content.', array(
'@oembed' => 'http://www.oembed.com/',
)) . '</p>';
$output .= '<p>' . t('Add or enable <a href="@provider">providers</a> to embed content from other sites.', array(
'@provider' => url('admin/build/oembed/provider'),
)) . '</p>';
$output .= '<p>' . t('Adds an input filter for replacing oEmbed enabled URLs with embedded content') . '</p>';
return $output;
case 'oembed.settings':
$output = '<p>' . t('These settings affect how your site behaves when it makes requests as an oEmbed consumer.') . '</p>';
return $output;
case 'entity.oembed_provider.collection':
$output = '<p>' . t('Providers are other web sites with oEmbed endpoints whose content you can embed on your site.') . '</p>';
return $output;
case 'oembed.sandbox.form':
$output = '<p>' . t('Use this form to test your configuration of provider plugins and endpoints.') . '</p>';
return $output;
}
}
/**
* Implements hook_cache_flush().
*/
function oembed_cache_flush() {
// Because some oEmbed providers (e.g., http://embed.ly) charge per request,
// allow cache_oembed to opt out of drupal_flush_all_caches() clearing.
if (\Drupal::config('oembed.settings')
->get('cache.flush')) {
\Drupal::cache('oembed')
->deleteAll();
}
}
/**
* Implements hook_cron().
*/
function oembed_cron() {
// If cache_oembed opts out of oembed_flush_caches(), then system_cron()
// doesn't clear its expired records, so do so here.
if (!\Drupal::config('oembed.settings')
->get('cache.flush')) {
\Drupal::cache('oembed')
->deleteAll();
}
}
/**
* Implements of hook_theme().
*/
function oembed_theme($existing, $type, $theme, $path) {
return array(
'oembed' => array(
'file' => 'oembed.theme.inc',
'path' => $path . '/theme',
'variables' => array(
'embed' => NULL,
),
),
'oembed__photo' => array(
'variables' => array(
'embed' => NULL,
),
'base hook' => 'oembed',
),
'oembed__rich' => array(
'variables' => array(
'embed' => NULL,
),
'base hook' => 'oembed',
),
'oembed__video' => array(
'variables' => array(
'embed' => NULL,
),
'base hook' => 'oembed',
),
);
}
/**
* Fetch data for an embeddable URL.
*
* @param string $url
* An external URL for the content to embed.
* @param array $parameters
* An associative array of request parameters, with the following keys:
* - 'maxwidth'
* The maximum width of the embed, in pixels.
* - 'maxheight'
* The maximum height of the embed, in pixels.
* Other keys may be supported by some providers (twitter, youtube, wordpress).
* @return bool|array
* False or an array representing the embeddable data of the URL.
*/
function oembed_get_data($url, $parameters = array()) {
$parameters = array_filter($parameters);
/** @var \Bangpound\oEmbed\Consumer $consumer */
$consumer = \Drupal::service('oembed.consumer');
try {
$data = $consumer
->get($url, $parameters);
return $data;
} catch (\RuntimeException $e) {
return false;
}
}
/**
* Prepare an element based on a oEmbed request.
*
* @param $type
* Element type.
* @param $url
* URL to embed.
* @param $parameters
* oEmbed request parameters.
*
* @return array
* A renderable array with the following keys and values:
* - #type: The passed-in element $type.
* - #url: The passed-in $url.
* - #parameters: The passed-in $parameters.
*/
function oembed_render_element($type, $url, $parameters = array()) {
return array(
'#type' => $type,
'#url' => $url,
'#parameters' => $parameters,
);
}
/**
* Generate a string for use as ALT attribute.
*/
function oembed_alt_attr(\Bangpound\oEmbed\Response\Response $embed) {
$options = array(
'@type' => $embed
->getType(),
);
// alt attribute using hopefully available title and provider name.
if (!empty($embed
->getTitle())) {
$string = '@title';
$options['@title'] = $embed
->getTitle();
}
else {
$string = 'Embedded @type';
}
if (!empty($embed
->getProviderName())) {
$string .= ' on @provider_name';
$options['@provider_name'] = $embed
->getProviderName();
}
return t($string, $options);
}
/**
* Implements hook_ctools_plugin_api().
*/
function oembed_ctools_plugin_api($module, $api) {
if ($module == 'file_entity' && $api == 'file_type') {
return array(
'version' => 1,
);
}
if ($module == 'file_entity' && $api == 'file_default_displays') {
return array(
'version' => 1,
);
}
}
/**
* Implement hook_preprocess_file_entity().
*/
function oembed_preprocess_file_entity(&$vars, $hook) {
if (isset($vars['file']->metadata['oembed'])) {
$vars['oembed_response'] = $embed = $vars['file']->metadata['oembed'];
$vars['classes_array'][] = 'oembed-' . $embed['type'];
if (strpos($embed['provider'], ':')) {
list($parent, $child) = explode(':', $embed['provider'], 2);
$vars['classes_array'][] = 'oembed-' . $parent;
$vars['classes_array'][] = 'oembed-' . $child;
}
else {
$vars['classes_array'][] = 'oembed-' . $embed['provider'];
}
$vars['title_attributes_array']['class'][] = 'oembed-title';
// This conflicts with default file_entity.tpl.php which hardcodes a class attribute.
$vars['content_attributes_array']['class'][] = 'oembed-content';
}
}
/**
* Implements hook_file_formatter_FORMATTER_view().
*/
function oembed_file_formatter_view($file, $display, $langcode) {
$scheme = file_uri_scheme($file->uri);
if ($scheme == 'oembed') {
$wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
// Build render attributes array. Prefer file-specific overrides to display settings.
$attributes = (isset($file->override) ? $file->override : array()) + $display['settings'];
unset($attributes['attributes']);
unset($attributes['wmode']);
$parameters = array();
if (!empty($display['settings']['wmode'])) {
$parameters['mode'] = $display['settings']['wmode'];
}
// The oEmbed spec defines `maxwidth` and `maxheight` parameters, but some providers
// support `width` and `height`. Precise dimensions supercede maximums.
if ($file->type != 'image' && $display['type'] != 'oembed_thumbnail') {
if (isset($attributes['width'])) {
$parameters['maxwidth'] = $parameters['width'] = $attributes['width'];
}
if (isset($attributes['height'])) {
$parameters['maxheight'] = $parameters['height'] = $attributes['height'];
}
}
$element = oembed_render_element($display['type'], $wrapper
->getExternalUrl(), $parameters);
$element['#attributes'] = $attributes;
// Unfortunately, it's necessary to validate the oEmbed response before rendering
// so that file_view_file() can continue to the next formatter.
$output = drupal_render($element);
if ($output) {
return show($element);
}
}
}
/**
* Implements hook_file_formatter_FORMATTER_view().
*/
function oembed_remote_file_formatter_view($file, $display, $langcode) {
$scheme = file_uri_scheme($file->uri);
if ($scheme == 'oembed') {
// URI of local copy of remote file must be stored because it may be
// different from the oEmbed URLs. If the URL does not have a valid
// extension, it will redirect to a URL that does.
if (!isset($file->metadata['oembed_remote_file_image']) || !file_exists($file->metadata['oembed_remote_file_image'])) {
$embed = $file->metadata['oembed'];
if ($embed['type'] == 'photo' && !empty($embed['url'])) {
$url = $embed['url'];
}
else {
if (isset($embed['thumbnail_url'])) {
$url = $embed['thumbnail_url'];
}
}
if (isset($url)) {
$result = drupal_http_request($url);
// Using the redirect URL's basename might guarantee a path with an
// appropriate file extension.
if (isset($result->redirect_url)) {
// If the redirect and original basenames are identical, do nothing.
if (drupal_basename($result->redirect_url) != drupal_basename($url)) {
$url .= '/' . drupal_basename($result->redirect_url);
}
}
$parsed_url = parse_url($url);
// Store local copies of images using hostname, path and filename of source.
$path = $parsed_url['host'];
$path .= drupal_dirname($parsed_url['path']);
if (substr($path, -1) != '/') {
$path .= '/';
}
$filename = drupal_basename($parsed_url['path']);
if (strpos($filename, '.') !== FALSE) {
$filename = file_munge_filename($filename, 'jpg jpeg gif png', FALSE);
}
$path .= $filename;
$local_uri = file_stream_wrapper_uri_normalize(file_default_scheme() . '://oembed/' . $path);
if (!file_exists($local_uri)) {
// Drupal dislikes protocol relative URL schemes. Everything should
// be accessible without HTTPS.
if (strpos($url, '//') === 0) {
$url = 'http:' . $url;
}
/// Ensure filesystem has directories for new file.
$dirname = drupal_dirname($local_uri);
file_prepare_directory($dirname, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
// Save the file data to the local directory.
$files = entity_load('file', FALSE, array(
'uri' => $local_uri,
));
if (!empty($files)) {
file_unmanaged_save_data($result->data, $local_uri);
}
else {
file_save_data($result->data, $local_uri);
}
}
$file->metadata['oembed_remote_file_image'] = $local_uri;
// Redundantish. See file_entity_file_insert and related hooks.
foreach (array(
'oembed_remote_file_image',
) as $name) {
if (!empty($file->metadata[$name])) {
$value = $file->metadata[$name];
db_merge('file_metadata')
->fields(array(
'value' => serialize($value),
))
->key(array(
'fid' => $file->fid,
'name' => $name,
))
->execute();
}
}
}
}
if (isset($file->metadata['oembed_remote_file_image'])) {
$local_uri = $file->metadata['oembed_remote_file_image'];
$image_file = file_uri_to_object($local_uri);
$image_file->metadata = array();
// Forcing the image file's type is perhaps no longer necessary.
if (!isset($image_file->type) || $image_file->type === FILE_TYPE_NONE) {
$image_file->type = 'image';
}
if ($image_file->filesize) {
$image_file->metadata = image_get_info($image_file->uri);
$image_file->filemime = $image_file->metadata['mime_type'];
return file_entity_file_formatter_file_image_view($image_file, $display, $langcode);
}
}
}
}
/**
* Implements hook_file_formatter_FORMATTER_settings().
*/
function oembed_file_formatter_oembed_settings($form, &$form_state, $settings) {
$element = array();
$element['width'] = array(
'#title' => t('Width'),
'#type' => 'textfield',
'#default_value' => $settings['width'],
);
$element['height'] = array(
'#title' => t('Height'),
'#type' => 'textfield',
'#default_value' => $settings['height'],
);
$element['wmode'] = array(
'#title' => t('Flash window mode (wmode)'),
'#type' => 'select',
'#empty_option' => t('None (do not request a specific wmode from the provider)'),
'#options' => drupal_map_assoc(array(
'window',
'transparent',
'opaque',
'direct',
'gpu',
)),
'#description' => t('Controls layering, transparency, and playback performance of content rendered by the Flash player. For more information, view <a href="http://kb2.adobe.com/cps/127/tn_12701.html#main_Using_Window_Mode__wmode__values_">Adobe\'s documentation</a>.'),
'#default_value' => $settings['wmode'],
);
return $element;
}
/**
* Implements hook_file_formatter_FORMATTER_settings().
*/
function oembed_file_formatter_oembed_thumbnail_settings($form, &$form_state, $settings) {
$element = array();
$element['width'] = array(
'#title' => t('Width'),
'#type' => 'textfield',
'#default_value' => $settings['width'],
);
$element['height'] = array(
'#title' => t('Height'),
'#type' => 'textfield',
'#default_value' => $settings['height'],
);
return $element;
}
/**
* Clear the cached oEmbed content for the selected files.
*/
function oembed_cache_clear($fids) {
$fids = array_keys($fids);
$query = new EntityFieldQuery();
$results = $query
->entityCondition('entity_type', 'file')
->propertyCondition('uri', 'oembed:', 'STARTS_WITH')
->propertyCondition('fid', $fids)
->execute();
$files = file_load_multiple(array_keys($results['file']));
foreach ($files as $file) {
$wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
$url = $wrapper
->getExternalUrl();
$cid = hash('sha256', $url);
cache_clear_all($cid, 'cache_oembed', TRUE);
}
}
/**
* Checks that the oEmbed response has required standard properties for its type.
*
* @param \stdClass $file
* A Drupal file object.
*
* @return array
* If the oEmbed response is invalid, the array will contain at least one error message.
*/
function oembed_file_validator_type(stdClass $file) {
return oembed_validate_response($file->metadata['oembed']);
}
/**
* Validates oEmbed responses.
*/
function oembed_validate_response($embed) {
$errors = array();
if (!$embed) {
$errors[] = t('Unable to fetch oEmbed data or it is not a valid URL.');
}
else {
if (empty($embed['version']) || empty($embed['type']) || intval($embed['version']) != 1) {
$errors[] = t('oEmbed data for is invalid.');
}
}
// Validate that response has required properties for its type.
$message = t('oEmbed response is missing required properties for @type.', array(
'@type' => $embed['type'],
));
// Video, rich and photo all must have width and height.
// This validation causes lots of legitimate responses to be rejected. To retain access
// to Twitter, Scribd and others, we allow responses that do not have height and width.
if (in_array($embed['type'], array(
'video',
'rich',
'photo',
))) {
if (!isset($embed['width']) || empty($embed['width']) || (!isset($embed['height']) || empty($embed['height']))) {
//$errors[] = $message;
}
}
// Video and rich type must have html content.
if (in_array($embed['type'], array(
'video',
'rich',
))) {
if (!isset($embed['html']) || empty($embed['html'])) {
$errors[] = $message;
}
}
// Image type must have a URL.
if ($embed['type'] == 'photo') {
if (!isset($embed['url']) || empty($embed['url'])) {
$errors[] = $message;
}
}
return $errors;
}
/**
* Return a file entity for a URL. Create the file if necessary.
*
* @param string $url
* URL of oEmbed request.
* @param boolean $create
* Flag to create the file entity if it does not already exist.
*
* @return \stdClass
*/
function oembed_url_to_file($url, $create = FALSE) {
$uri = 'oembed://' . drupal_encode_path($url);
$file = file_uri_to_object($uri);
$file->metadata = array();
if (!isset($file->metadata['oembed'])) {
$file->metadata['oembed'] = oembed_get_data($url);
}
// New URLs need to be validated before being saved.
if ($create && !isset($file->fid)) {
// Save the new file.
file_save($file);
}
return $file;
}
Functions
Name | Description |
---|---|
oembed_alt_attr | Generate a string for use as ALT attribute. |
oembed_cache_clear | Clear the cached oEmbed content for the selected files. |
oembed_cache_flush | Implements hook_cache_flush(). |
oembed_cron | Implements hook_cron(). |
oembed_ctools_plugin_api | Implements hook_ctools_plugin_api(). |
oembed_file_formatter_oembed_settings | Implements hook_file_formatter_FORMATTER_settings(). |
oembed_file_formatter_oembed_thumbnail_settings | Implements hook_file_formatter_FORMATTER_settings(). |
oembed_file_formatter_view | Implements hook_file_formatter_FORMATTER_view(). |
oembed_file_validator_type | Checks that the oEmbed response has required standard properties for its type. |
oembed_get_data | Fetch data for an embeddable URL. |
oembed_help | Implements hook_help(). |
oembed_hook_info | Implements hook_hook_info(). |
oembed_preprocess_file_entity | Implement hook_preprocess_file_entity(). |
oembed_remote_file_formatter_view | Implements hook_file_formatter_FORMATTER_view(). |
oembed_render_element | Prepare an element based on a oEmbed request. |
oembed_theme | Implements of hook_theme(). |
oembed_url_to_file | Return a file entity for a URL. Create the file if necessary. |
oembed_validate_response | Validates oEmbed responses. |