You are here

emapi.module in Embedded Media Field 6.3

Provides an API for parsing, storage, and display of third party media.

File

emapi/emapi.module
View source
<?php

/**
 * @file
 * Provides an API for parsing, storage, and display of third party media.
 */

/* ***************************************** */

/* INCLUDES                                  */

/* ***************************************** */

// A registry of variable_get defaults.
include_once 'includes/emapi.variables.inc';

/**
 * Implementation of hook_menu().
 */
function emapi_menu() {
  $items = array();
  $items['admin/content/emapi'] = array(
    'page callback' => 'emapi_admin_list_page',
    'title' => 'Embedded media',
    'description' => 'Administration list of embedded media.',
    'access arguments' => array(
      'administer embedded media',
    ),
    'file' => 'includes/emapi.admin.inc',
  );
  $items['emapi/parse/json'] = array(
    'page callback' => 'emapi_parse_json',
    'title' => 'Parse media',
    'description' => 'Automatically derive a media object from a parsed URL or embed code.',
    'access arguments' => array(
      'administer embedded media',
    ),
    'file' => 'includes/emapi.parse.inc',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_init().
 */
function emapi_init() {
  emapi_get_provider_classes();

  // Ensure the proper files are loaded when a new media object is initiated.
  spl_autoload_register('emapi_autoload');
}

/**
 * Implementation of hook_flush_caches().
 */
function emapi_flush_caches() {
  return array(
    'cache_emapi_xml',
  );
}

/**
 * Builds a registry of Media provider classes.
 *
 * Each module supporting a Media provider will need to implement
 * hook_emapi_register, which will need to return an associated array keyed by
 * the scheme, with an array containing at least the following key => value
 * pairs. Note that the scheme portion of the URI this class supports is in the
 * form of scheme://identifier/id.
 *  'class_name' => The actual name of the class.
 * The following key => value pairs are optional, and will otherwise be
 * automatically derived:
 *  'name' => The human-readable name of the scheme.
 *  'description' => A description of the scheme.
 *  'path' => The path where the class file resides.
 *  'file' => The file containing the class definition.
 *  'module' => The module defining the class.
 *  'url' => The URL to the remote media provider, if applicable.
 * The following key => value pair will be automatically set to the association
 * and cannot be overridden:
 *   'scheme' => The scheme portion of the URI.
 *
 * @param string $scheme
 *  (Optional) The scheme of the specific class registration to return.
 * @param boolean $reset
 *  (Optional) If TRUE, then reset the registration.
 * @return array
 *  If $scheme is specified, then return only the specified class array, or NULL
 *  if there is no such registered class. Otherwise, return the entiry registry.
 */
function emapi_get_provider_classes($scheme = NULL, $reset = FALSE) {
  static $emapi_registered_classes;
  if ($reset || !isset($emapi_registered_classes)) {
    $emapi_registered_classes = array();

    // Build our media object class registry.
    foreach (module_implements('emapi_register') as $module) {
      foreach (module_invoke($module, 'emapi_register') as $scheme_name => $class) {
        $emapi_registered_classes[$scheme_name] = is_array($class) ? $class : array();
        $emapi_registered_classes[$scheme_name]['scheme'] = $scheme_name;
        if (!isset($emapi_registered_classes[$scheme_name]['name'])) {
          $emapi_registered_classes[$scheme_name]['name'] = t($scheme_name);
        }
        if (!isset($emapi_registered_classes[$scheme_name]['description'])) {
          $emapi_registered_classes[$scheme_name]['description'] = t('Class definition for @scheme.', array(
            '@scheme' => $scheme_name,
          ));
        }
        if (!isset($emapi_registered_classes[$scheme_name]['path'])) {
          $emapi_registered_classes[$scheme_name]['path'] = drupal_get_path('module', $module);
        }
        if (!isset($emapi_registered_classes[$scheme_name]['file'])) {
          $emapi_registered_classes[$scheme_name]['file'] = $class_name . '.inc';
        }
        if (!isset($emapi_registered_classes[$scheme_name]['module'])) {
          $emapi_registered_classes[$scheme_name]['module'] = $module;
        }
      }
    }
  }
  if (isset($scheme) && isset($emapi_registered_classes[$scheme])) {
    return $emapi_registered_classes[$scheme];
  }
  else {
    if (!isset($scheme)) {
      return $emapi_registered_classes;
    }
  }
}

/**
 * Autoload the media object classes when needed.
 */
function emapi_autoload($class_name) {
  if ($class_name == 'EmapiMedia') {
    module_load_include('inc', 'emapi', 'includes/emapi.class.media');
  }
  else {
    if ($class = emapi_get_provider_class_by_class_name($class_name)) {
      include_once $class['path'] . '/' . $class['file'];
    }
  }
}

/**
 * Return the registered EmAPI class specified by name.
 */
function emapi_get_provider_class_by_class_name($class_name = NULL, $reset = FALSE) {
  static $classes;
  if (!isset($classes) || $reset) {
    $classes = array();
    $provider_classes = emapi_get_provider_classes();
    foreach ($provider_classes as $scheme => $class) {
      $classes[$class['class_name']] = $class;
    }
  }
  if (isset($class_name)) {
    return $classes[$class_name];
  }
  return $classes;
}

/**
 *  A wrapper around simplexml to retrieve a given XML file.
 *
 *  @param $url
 *    The URL to the XML to retrieve.
 *  @param $display_errors
 *    Optional; if TRUE, then we'll display errors to the end user. They'll be
 *    logged to the watchdog in any case.
 *  @param $refresh
 *    Optional; if TRUE, then we'll force a new load of the XML. Otherwise,
 *    a cached version will be retrieved if possible.
 *  @return
 *    A fully populated object, or FALSE on an error.
 */
function emapi_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
  module_load_include('inc', 'emapi', 'includes/emapi.xml');
  return _emapi_retrieve_xml($url, $display_errors, $refresh);
}

/**
 * Returns the scheme of a URI (e.g. a stream).
 *
 * Note that this function is lifted from Drupal 7's file_uri_scheme().
 *
 * @param $uri
 *   A stream, referenced as "scheme://target".
 * @return
 *   A string containing the name of the scheme, or FALSE if none. For example,
 *   the URI "youtube://v/3gfh6asey" would return "youtube".
 */
function emapi_uri_scheme($uri) {
  $data = explode('://', $uri, 2);
  return count($data) == 2 ? $data[0] : FALSE;
}

/**
 * Returns the registered class for a specific provider.
 *
 * @param string $uri
 *  A stream, referenced as "scheme://target".
 * @return string
 *  The registered class, or NULL if it's an unsupported URI.
 */
function emapi_get_provider_class($uri) {
  $class = emapi_get_provider_classes(emapi_uri_scheme($uri));
  if (is_array($class) && $class['class_name']) {
    return $class['class_name'];
  }
}

/**
 * Parses a URL or embed code into a media object.
 *
 * @param string $url
 *  The URL or embed code to parse.
 * @return mixed
 *  The fully populated media object, or FALSE.
 */
function emapi_parse($url) {
  foreach (emapi_get_provider_classes() as $class) {
    if (class_exists($class['class_name'])) {
      $media = new $class['class_name']();
      if ($media
        ->parse($url)) {
        $media = emapi_media_from_uri($media->uri);
        return $media;
      }
    }
  }
  return FALSE;
}

/**
 * Loads a media object based on the given URI.
 *
 * @param string $uri
 *  A stream, referenced as "scheme://target".
 * @return mixed
 *  The fully populated media object, or FALSE.
 */
function emapi_media_from_uri($uri) {
  $media =& emapi_static('emapi_media', array());

  // First check to see if we've already loaded this media.
  foreach ($media as $emid => $item) {
    if ($item->uri == $uri) {
      return $item;
    }
  }

  // Next see if the item is in the db.
  $results = db_query("SELECT emid, uri, uid, status, timestamp FROM {emapi_media} WHERE uri = '%s'", $uri);
  if ($result = db_fetch_object($results)) {
    $media[$result->emid] = emapi_media_from_db_result($result);
    return $media[$result->emid];
  }

  // Finally we simply create a new media object.
  if (($class = emapi_get_provider_class($uri)) && class_exists($class)) {
    global $user;
    $item = new $class($uri);
    $item
      ->set_uid($user->uid);
    $item
      ->set_status(EMAPI_STATUS_PERMANENT);
    $item
      ->set_timestamp(time());
    drupal_alter('emapi_media', $item);
    return $item;
  }

  // No media matches the given URI.
  return FALSE;
}

/**
 * Create a new media object from a db result.
 *
 * @param object $result
 *  The db object returned from the db.
 * @return mixed
 *  The media object, or NULL if there's no class available for the scheme.
 */
function emapi_media_from_db_result($result) {
  if (($class = emapi_get_provider_class($result->uri)) && class_exists($class)) {
    $media = new $class($result->uri);
    $media
      ->set_emid($result->emid);
    $media
      ->set_uid($result->uid);
    $media
      ->set_status($result->status);
    $media
      ->set_timestamp($result->timestamp);
    drupal_alter('emapi_media', $media);
  }
  return $media;
}

/**
 * Load one or more fully populated media objects.
 *
 * @param array $emids
 *  An array of unique integers corresponding to the media ID's.
 * @return mixed
 *  The fully populated media object, or FALSE.
 */
function emapi_media_load_multiple($emids) {
  $media =& emapi_static('emapi_media', array());
  if (!empty($emids)) {

    // If any media were previously loaded, remove from the ids still to load.
    $emids = array_flip($emids);
    $ids_to_load = array_keys(array_diff_key($emids, $media));
    if (!empty($ids_to_load)) {
      $results = db_query("SELECT emid, uri, uid, status, timestamp FROM {emapi_media} WHERE emid IN (" . db_placeholders($ids_to_load) . ")", $ids_to_load);
      while ($result = db_fetch_object($results)) {
        $media[$result->emid] = emapi_media_from_db_result($result);
        module_invoke_all("emapi_media_load", $media[$result->emid]);
      }
    }

    // Ensure that the returned array is ordered the same as the original
    // $ids array if this was passed in and remove any invalid ids.
    // Remove any invalid ids from the array.
    $requested_ids = array_intersect_key($media, $emids);
    $returned_ids = array();
    foreach ($emids as $id => $item) {
      if ($requested_ids[$id]) {
        $returned_ids[$id] = $requested_ids[$id];
      }
    }
  }
  return $returned_ids;
}

/**
 * Load a single media object.
 *
 * @param integer $emid
 *  The unique ID for the media.
 * @return mixed
 *  Either the desired media object, or NULL if not found.
 */
function emapi_media_load($emid) {
  $media = emapi_media_load_multiple(array(
    $emid,
  ));
  return $media[$emid];
}

/**
 * Save a media object to the database.
 *
 * @param object &$media
 *  The media object to save.
 */
function emapi_media_save(&$media) {
  module_invoke_all('emapi_media_presave', $media);
  if ($media->emid) {
    drupal_write_record('emapi_media', $media, 'emid');
    module_invoke_all('emapi_media_update', $media);
  }
  else {
    if ($media) {
      drupal_write_record('emapi_media', $media);
      module_invoke_all('emapi_media_insert', $media);
    }
  }
}

/**
 * Delete a media object from the database.
 *
 * @param integer $emid
 *  The unique identifier of the media to delete.
 */
function emapi_media_delete($emid) {
  $media =& emapi_static('emapi_media', array());
  $item = emapi_media_load($emid);
  if ($item) {
    unset($media[$emid]);
    db_query("DELETE FROM {emapi_media} WHERE emid = %d", $emid);
    module_invoke_all('emapi_media_delete', $item);
    $link = l($item
      ->get_uri(), $item
      ->url());
    $class = emapi_get_provider_classes(emapi_uri_scheme($item
      ->get_uri()));
    watchdog('emapi', '@provider: deleted !link.', array(
      '@provider' => $class['name'],
      '!link' => $link,
    ));
    drupal_set_message(t('@provider !link has been deleted.', array(
      '@provider' => $class['name'],
      '!link' => $link,
    )));
  }
}

/**
 * Backport of d7's drupal_static().
 */
function &emapi_static($name, $default_value = NULL, $reset = FALSE) {
  static $data = array(), $default = array();
  if (!isset($name)) {

    // All variables are reset. This needs to be done one at a time so that
    // references returned by earlier invocations of drupal_static() also get
    // reset.
    foreach ($default as $name => $value) {
      $data[$name] = $value;
    }

    // As the function returns a reference, the return should always be a
    // variable.
    return $data;
  }
  if ($reset) {

    // The reset means the default is loaded.
    if (array_key_exists($name, $default)) {
      $data[$name] = $default[$name];
    }
    else {

      // Reset was called before a default is set and yet a variable must be
      // returned.
      return $data;
    }
  }
  elseif (!array_key_exists($name, $data)) {

    // Store the default value internally and also copy it to the reference to
    // be returned.
    $default[$name] = $data[$name] = $default_value;
  }
  return $data[$name];
}

Functions

Namesort descending Description
emapi_autoload Autoload the media object classes when needed.
emapi_flush_caches Implementation of hook_flush_caches().
emapi_get_provider_class Returns the registered class for a specific provider.
emapi_get_provider_classes Builds a registry of Media provider classes.
emapi_get_provider_class_by_class_name Return the registered EmAPI class specified by name.
emapi_init Implementation of hook_init().
emapi_media_delete Delete a media object from the database.
emapi_media_from_db_result Create a new media object from a db result.
emapi_media_from_uri Loads a media object based on the given URI.
emapi_media_load Load a single media object.
emapi_media_load_multiple Load one or more fully populated media objects.
emapi_media_save Save a media object to the database.
emapi_menu Implementation of hook_menu().
emapi_parse Parses a URL or embed code into a media object.
emapi_retrieve_xml A wrapper around simplexml to retrieve a given XML file.
emapi_static Backport of d7's drupal_static().
emapi_uri_scheme Returns the scheme of a URI (e.g. a stream).