You are here

oembedcore.module in oEmbed 6.0

Core functionality for oEmbed

File

oembedcore.module
View source
<?php

/**
 * @file
 * Core functionality for oEmbed
 */

/**
 * Implementation of hook_help().
 */
function oembedcore_help($path, $arg) {
  switch ($path) {
    case 'admin/help#oembedcore':
      $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('Create <a href="@preset">presets</a> to customize the way oEmbed content appears on your site.', array(
        '@preset' => url('admin/build/oembed/preset'),
      )) . '</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>';
      return $output;
    case 'admin/build/oembed':
    case 'admin/build/oembed/preset':
      $output = '<p>' . t('Presets are the dimensions and other display properties of content embedded on your site.') . '</p>';
      return $output;
    case 'admin/build/oembed/provider':
      $output = '<p>' . t('Providers are other web sites with oEmbed endpoints whose content you can embed on your site.') . '</p>';
      return $output;
  }
}

/**
 * Implementation of hook_perm().
 */
function oembedcore_perm() {
  return array(
    'administer oembed presets',
  );
}

/**
 * Implementation of hook_menu_alter().
 *
 * Instead of rewriting ctools export UI's hook_menu implementations, alter
 * the callback items to have a common menu item.
 */
function oembedcore_menu_alter(&$items) {

  // Create a new menu item where all oembed export UIs will be local tasks by
  // copying the export UI's menu item that will become the default local task.
  $items['admin/build/oembed'] = $items['admin/build/oembed/preset'];
  $items['admin/build/oembed']['title'] = 'oEmbed';
  $items['admin/build/oembed']['description'] = 'Admin overview of oEmbed.';
  $items['admin/build/oembed/preset']['type'] = MENU_DEFAULT_LOCAL_TASK;
  $items['admin/build/oembed/provider']['type'] = MENU_LOCAL_TASK;
}

/**
 * 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_default_oembedcore_provider().
 */
function oembedcore_default_oembedcore_provider() {
  $providers = array();
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'viddler';
  $provider->title = 'Viddler';
  $provider->endpoint = 'http://lab.viddler.com/services/oembed/';
  $provider->scheme = 'http://*.viddler.com/*';
  $providers['viddler'] = $provider;
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'flickr';
  $provider->title = 'Flickr';
  $provider->endpoint = 'http://www.flickr.com/services/oembed/';
  $provider->scheme = 'http://*.flickr.com/*
http://flickr.com/*';
  $providers['flickr'] = $provider;
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'qik';
  $provider->title = 'Qik';
  $provider->endpoint = 'http://qik.com/api/oembed.json';
  $provider->scheme = 'http://qik.com/video/*
http://qik.com/*';
  $providers['qik'] = $provider;
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'revision3';
  $provider->title = 'Revision3';
  $provider->endpoint = 'http://revision3.com/api/oembed/';
  $provider->scheme = 'http://*.revision3.com/*';
  $providers['revision3'] = $provider;
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'vimeo';
  $provider->title = 'Vimeo';
  $provider->endpoint = 'http://www.vimeo.com/api/oembed.json';
  $provider->scheme = 'http://www.vimeo.com/groups/*/videos/*
http://www.vimeo.com/*
http://vimeo.com/groups/*/videos/*
http://vimeo.com/*';
  $providers['vimeo'] = $provider;
  $provider = new stdClass();
  $provider->disabled = TRUE;

  /* Edit this to true to make a default provider disabled initially */
  $provider->name = 'youtube';
  $provider->title = 'YouTube';
  $provider->endpoint = 'http://www.youtube.com/oembed';
  $provider->scheme = 'http://*.youtube.com/watch*';
  $providers['youtube'] = $provider;
  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
 *
 * @todo Make this more like drupal_match_path()
 */
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();

      // oEmbed providers are local services that return content over callback
      // function.
      $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, '/')) . '/i';
          $providers[$host][$regex_pattern] = $info;
        }
      }

      // oEmbed provider definitions are remote web services.
      $provider_definitions = oembedcore_provider_load_all();
      foreach ($provider_definitions as $provider_definition) {
        if (empty($provider_definition->disabled)) {
          $schemes = preg_split("/(\r\n?|\n)/", $provider_definition->scheme);
          foreach ($schemes as $scheme) {
            $host = _oembedcore_get_host($scheme);
            $regex_pattern = '/' . str_replace('\\*', '.*', preg_quote($scheme, '/')) . '/i';
            $providers[$host][$regex_pattern] = (array) $provider_definition;
          }
        }
      }
      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)) {
    $data =& $attributes;
    $data['__drupal_alter_by_ref'] = array(
      &$provider,
    );
    drupal_alter('oembed_request', $data, $url);
    return oembedcore_oembed_fetch($provider, $url, $matches, $attributes);
  }
  return FALSE;
}
function oembedcore_oembed_fetch($provider, $url, $matches, $attributes = array()) {
  $embed = FALSE;
  $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 = call_user_func($provider['callback'], $provider, $url, $matches, $attributes);
      if ($embed) {
        $embed = (object) $embed;
      }
    }
    else {
      $fetch_url = $provider['endpoint'] . '?' . $query;

      //TODO: Add alternative ways of fetching the content - like http client?
      $response = drupal_http_request($fetch_url);
      if (!isset($response->error)) {
        $embed = json_decode($response->data);
        if (!is_object($embed)) {
          try {
            $embed = @new SimpleXMLElement($response->data);
            $embed = (object) get_object_vars($embed);
            if (!is_string($embed->title)) {
              $embed->title = '';
            }
          } catch (Exception $e) {
            watchdog('oembed', 'Could not parse response from %url.', array(
              '%url' => $fetch_url,
            ), WATCHDOG_ERROR);
          }
        }
        if (empty($embed->version) || empty($embed->type) || intval($embed->version) != 1) {
          $embed = FALSE;
          watchdog('oembed', 'Response from %url not a valid oEmbed response.', array(
            '%url' => $fetch_url,
          ), WATCHDOG_ERROR);
        }
      }
      else {
        watchdog('oembed', 'Error fetching data from %url.', array(
          '%url' => $fetch_url,
        ), WATCHDOG_ERROR);
      }
    }
    if ($embed) {
      $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',
          ),
        ));
      }
      $img_attributes = array(
        'src' => check_url($embed->url),
        'alt' => empty($embed->title) ? t('Embedded image') : $embed->title,
      );
      if (!empty($embed->provider_name)) {
        $img_attributes['alt'] .= ', ' . t('on') . ' ' . $embed->provider_name;
      }
      $return .= ' ' . l('<img' . drupal_attributes($img_attributes) . ' />', $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();
  }
}

/**
 * Implement hook_ctools_plugin_directory().
 */
function oembedcore_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && $plugin == 'export_ui') {
    return 'plugins/' . $plugin;
  }
}

/**
 * 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;
}

// --------------------------------------------------------------------------
// Preset database info.

/**
 * Create a new provider with defaults appropriately set from schema.
 */
function oembedcore_provider_new() {
  ctools_include('export');
  return ctools_export_new_object('oembedcore_provider');
}

/**
 * Load a single provider.
 */
function oembedcore_provider_load($name) {
  ctools_include('export');
  $result = ctools_export_load_object('oembedcore_provider', 'names', array(
    $name,
  ));
  if (isset($result[$name])) {
    return $result[$name];
  }
  else {
    return FALSE;
  }
}

/**
 * Load all providers.
 */
function oembedcore_provider_load_all() {
  ctools_include('export');
  return ctools_export_load_object('oembedcore_provider');
}

/**
 * Write a provider to the database.
 */
function oembedcore_provider_save(&$provider) {
  $update = isset($provider->pid) ? array(
    'pid',
  ) : array();
  drupal_write_record('oembedcore_provider', $provider, $update);
  cache_clear_all('oembedcore:providers', 'cache');
  return $preset;
}

/**
 * Remove a provider.
 */
function oembedcore_provider_delete($provider) {
  db_query("DELETE FROM {oembedcore_provider} WHERE name = '%s' AND pid = %d", $provider->name, $provider->pid);
  cache_clear_all('oembedcore:providers', 'cache');
}

/**
 * Export a provider.
 */
function oembedcore_provider_export($provider, $indent = '') {
  ctools_include('export');
  $output = ctools_export_object('oembedcore_provider', $provider, $indent);
  return $output;
}

/**
 * Lists all available providers.
 */
function oembedcore_provider_list() {
  $return = array();
  $providers = oembedcore_provider_load_all();
  foreach ($providers as $provider) {
    $return[$provider->name] = $provider->name;
  }
  return $return;
}

Functions

Namesort descending Description
oembedcore_ctools_plugin_directory Implement hook_ctools_plugin_directory().
oembedcore_default_oembedcore_provider Implementation of hook_default_oembedcore_provider().
oembedcore_form_system_modules_alter Clear presets cache on admin/build/modules form.
oembedcore_get_provider Returns the provider for a url.
oembedcore_help Implementation of hook_help().
oembedcore_menu_alter Implementation of hook_menu_alter().
oembedcore_oembed_data Fetch data for an embeddable URL.
oembedcore_oembed_fetch
oembedcore_oembed_html To be used for HTML in cases where the HTML is cached independent of the theme - like in the case of input filters.
oembedcore_perm Implementation of hook_perm().
oembedcore_preset_delete Remove a preset.
oembedcore_preset_export Export a preset
oembedcore_preset_list Lists all available presets
oembedcore_preset_load Load a single preset
oembedcore_preset_load_all Load all presets.
oembedcore_preset_new Create a new preset with defaults appropriately set from schema.
oembedcore_preset_save Write a preset to the database.
oembedcore_providers Returns all the registered providers, or the providers for a specific host.
oembedcore_provider_delete Remove a provider.
oembedcore_provider_export Export a provider.
oembedcore_provider_list Lists all available providers.
oembedcore_provider_load Load a single provider.
oembedcore_provider_load_all Load all providers.
oembedcore_provider_new Create a new provider with defaults appropriately set from schema.
oembedcore_provider_save Write a provider to the database.
oembedcore_theme Implementation of hook_theme().
_oembedcore_get_host A utility function to get the base domain from a url.
_oembedcore_specificity_compare Helper function that compares the length of match expressions.