You are here

emfield.module in Embedded Media Field 6.2

Same filename and directory in other branches
  1. 5 emfield.module
  2. 6.3 emfield.module
  3. 6 emfield.module
  4. 7 emfield.module

Embedded Media Field is a CCK-based framework for 3rd party media files.

File

emfield.module
View source
<?php

/**
 * @file
 *   Embedded Media Field is a CCK-based framework for 3rd party media files.
 */
define('EMFIELD_STATUS_UNAVAILABLE', 0x0);
define('EMFIELD_STATUS_AVAILABLE', 0x1);
define('EMFIELD_STATUS_PRIVATE', 0x2);

/**
 * Implementation of hook_menu().
 */
function emfield_menu() {
  $items['admin/content/emfield'] = array(
    'file' => 'emfield.admin.inc',
    'title' => 'Embedded Media Field configuration',
    'description' => 'Configure Embedded Media Field: Allow content types to use various 3rd party providers, enter API keys, etc.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'emfield_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  $items['admin/content/emfield/media'] = array(
    'file' => 'emfield.admin.inc',
    'title' => 'General',
    'description' => 'Configure Embedded Media Field: Allow content types to use various 3rd party providers, enter API keys, etc.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'emfield_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -3,
  );
  foreach (module_implements('emfield_info', TRUE) as $module) {
    $emfield_info = module_invoke($module, 'emfield_info');
    $items['admin/content/emfield/' . $module] = array(
      'title' => $emfield_info['#settings_title'],
      'description' => $emfield_info['#settings_description'],
      'file' => 'emfield.admin.inc',
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'emfield_module_settings',
        3,
      ),
      'access arguments' => array(
        'administer site configuration',
      ),
      'type' => MENU_LOCAL_TASK,
    );
  }
  $items['admin/content/emfield/obsolete'] = array(
    'file' => 'emfield.admin.inc',
    'title' => 'Disable obsolete provider',
    'page callback' => 'emfield_disable_obsolete_provider',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_CALLBACK,
  );
  if (module_exists('job_queue')) {
    $items['admin/content/emfield/reload'] = array(
      'file' => 'emfield.admin.inc',
      'title' => 'Reload data',
      'description' => 'Reload emfield fields in bulk',
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'emfield_settings_jobqueue',
      ),
      'access arguments' => array(
        'administer site configuration',
      ),
      'type' => MENU_LOCAL_TASK,
      'weight' => 3,
    );
  }
  return $items;
}
function emfield_field_columns($field) {
  module_load_include('inc', 'emfield', 'emfield.cck');
  return _emfield_field_columns($field);
}

/**
 *  A helper function for hook_field(), called by the various sub-modules.
 */
function emfield_emfield_field($op, &$node, $field, &$items, $teaser, $page, $module) {
  module_load_include('inc', 'emfield', 'emfield.cck');
  return _emfield_emfield_field($op, $node, $field, $items, $teaser, $page, $module);
}

/**
 *  Helper function to implement hook_content_is_empty.
 */
function emfield_emfield_content_is_empty($item, $field) {
  return empty($item['value']);
}

/**
 * Return a list of providers allowed for a specific field.
 */
function emfield_allowed_providers($field, $module) {
  $allowed_providers = emfield_system_list($module);

  // $field sometimes is the whole $field and sometimes just the $widget,
  // depending on where it is called from, so check which one we have.
  $providers = isset($field['widget']['providers']) ? $field['widget']['providers'] : (isset($field['providers']) ? $field['providers'] : array());
  $allow_all = TRUE;
  foreach ($allowed_providers as $test) {
    if (!variable_get('emfield_' . $module . '_allow_' . $test->name, TRUE)) {
      unset($allowed_providers[$test->name]);
    }
    else {
      $allow_all &= empty($providers[$test->name]);
    }
  }
  if (!$allow_all) {
    foreach ($allowed_providers as $test) {
      if (empty($providers[$test->name])) {
        unset($allowed_providers[$test->name]);
      }
    }
  }
  return $allowed_providers;
}

/**
 *  This returns a list of content types that are implemented by emfield.
 */
function emfield_implement_types($cached = TRUE) {
  static $types;
  if (!isset($types) || !$cached) {

    // If it's a cachable request, try to load a cached value.
    if ($cached && ($cache = cache_get('emfield_implement_types', 'cache'))) {
      $types = $cache->data;
    }
    else {
      $system_types = _content_type_info();
      $content_types = $system_types['content types'];
      $field_types = $system_types['field types'];

      // The array that will store type/field information for provider import.
      $types = array();
      $modules = array();
      foreach (module_implements('emfield_info', TRUE) as $module) {
        $modules[$module] = $module;
      }

      // Settings per content type for the module.
      foreach ($content_types as $content_type => $type) {

        // Determine which content types implement this module.
        foreach ($type['fields'] as $field_type => $field) {

          // If this field type is defined by this module, then include it here.
          if (!empty($modules[$field_types[$field['type']]['module']])) {

            // Settings per content type per module.
            $module = $modules[$field_types[$field['type']]['module']];
            $types[$module][$content_type][$field_type] = $field;
          }
        }
      }
    }
  }
  cache_set('emfield_implement_types', $types, 'cache', CACHE_PERMANENT);
  return $types;
}

/**
 * This will parse the url or embedded code pasted by the node submitter.
 *
 * @return
 *   Either an empty array (if no match), or an array of 'provider' and 'value'.
 */
function emfield_parse_embed($field, $embed = '', $module) {
  $providers = emfield_allowed_providers($field, $module);
  foreach ($providers as $provider) {
    $success = emfield_include_invoke($module, $provider->name, 'extract', trim($embed), $field);

    // We've been given an array of regex strings, so try to find a match.
    if (is_array($success)) {
      foreach ($success as $regex) {
        $matches = NULL;
        if (preg_match($regex, trim($embed), $matches)) {
          return array(
            'provider' => $provider->name,
            'value' => $matches[1],
          );
        }
      }
    }
    else {
      if ($success) {
        return array(
          'provider' => $provider->name,
          'value' => $success,
        );
      }
    }
  }

  // We found no match.
  return array();
}

/**
 *  Extract the id from embedded code or url.
 */
function _emfield_field_validate_id($field, $item, $error_field, $module, $delta = 0) {

  // Load the provider info.
  $item = _emfield_field_submit_id($field, $item, $module, $error_field);

  // Ensure we have proper provider info.
  if ($item['embed'] && !$item['provider']) {
    form_set_error($error_field, t('You have specified an invalid media URL or embed code.'));
  }
  else {
    emfield_include_invoke($module, $item['provider'], 'validate', $item['value'], $error_field);
  }
  return $item;
}

/**
 *  Replace embedded code with the extracted id. this goes in the field 'value'.
 *  This also allows you to grab directly from the URL to display the content from field 'provider'.
 */
function _emfield_field_submit_id($field, $item, $module) {

  // @TODO Find the right fix for this.
  // When the embedded media field is used in the default value widget
  // when editing field settings, the $item has no value for 'provider',
  // causing an error message.
  // This is not the right way to fix this, but I don't know why that
  // value has been left out of the form element returned by hook_widget_settings
  // and this will at least keep the error message from being displayed.
  if (!isset($item['provider'])) {
    $item['provider'] = '';
  }
  $item = array_merge($item, emfield_parse_embed($field, $item['embed'], $module));
  $item['data'] = (array) emfield_include_invoke($module, $item['provider'], 'data', $field, $item);
  $item['version'] = intval(emfield_include_invoke($module, $item['provider'], 'data_version', $field, $item));
  $info = emfield_include_invoke($module, $item['provider'], 'info');
  if ($info['module'] && module_hook($info['module'], 'emfield_status')) {
    $item['status'] = module_invoke($info['module'], 'emfield_status', $item, $field, $module);
  }
  return $item;
}

/**
 *  Format our field for display. This is actually called originally by each helper module
 *  that implements hook_field_formatter, which then call this.
 */
function emfield_emfield_field_formatter($field, $item, $formatter, $node, $module, $options = array()) {

  // If we're coming from a preview, we need to extract our new embedded value.
  if (isset($node->in_preview)) {
    $item = emfield_parse_embed($field, $item['embed'], $module);
  }

  // If we have no value, then return an empty string.
  if (!isset($item['value'])) {
    return '';
  }

  // Unfortunately, when we come from a view, we don't get all the widget fields.
  if (!isset($node->type) && isset($field['type_name'])) {
    $type = content_types($field['type_name']);
    $field['widget'] = $type['fields'][$field['field_name']]['widget'];
  }

  // Sometimes our data is still unserialized, again from views.
  if (!is_array($item['data'])) {
    $item['data'] = (array) unserialize($item['data']);
  }

  // The individual modules actually define the theme for the formatter.
  $output = '';
  $output .= theme($module . '_' . $formatter, $field, $item, $formatter, $node, $options);
  return $output;
}

/** Widgets **/
function emfield_emfield_widget_settings($op, $widget, $module) {
  module_load_include('inc', 'emfield', 'emfield.cck');
  return _emfield_emfield_widget_settings($op, $widget, $module);
}
function emfield_emfield_widget(&$form, &$form_state, $field, $items, $delta = 0, $module) {
  module_load_include('inc', 'emfield', 'emfield.cck');
  return _emfield_emfield_widget($form, $form_state, $field, $items, $delta, $module);
}

/**
 * Implementation of hook_node_operations().
 */
function emfield_node_operations() {
  $operations = array(
    'emfield_reload' => array(
      'label' => t('Reload Embedded Media Data'),
      'callback' => 'emfield_operations_reload',
    ),
  );
  return $operations;
}

/**
 *  This reloads and saves the data for all the nid's in the array.
 */
function emfield_operations_reload($nids = array(), $show_message = TRUE) {
  foreach ($nids as $nid) {
    if ($node = node_load($nid)) {
      $type = content_types($node->type);
      foreach ($type['fields'] as $field) {
        if (module_hook($field['type'], 'emfield_info')) {
          $message = "Reloaded %node-title's %field.";
          $message_variables = array(
            '%node-title' => $node->title,
            '%field' => $field['type_name'],
          );
          watchdog('emfield reload data', $message, $message_variables, WATCHDOG_NOTICE, l($node->title, 'node/' . $node->nid));
          if ($show_message) {
            drupal_set_message(t($message, $message_variables));
          }
          $items = $node->{$field['field_name']};
          emfield_emfield_field('submit', $node, $field, $items, FALSE, FALSE, $field['type']);
          $node->{$field['field_name']} = $items;
          node_save($node);
        }
      }
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function emfield_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  switch ($op) {
    case 'rss item':
      module_load_include('inc', 'emfield', 'emfield.rss');
      return _emfield_nodeapi_rss($node, $op, $teaser, $page);
  }
}

/**
 * When an include file requires to read an xml to receive information, such as
 * for thumbnails, this script can be used to request the xml and return it as
 * an array.
 *
 * Note that emfield_retrieve_xml() in most cases will be better, as it's a
 * wrapper around simplexml.
 *
 *  @param $provider
 *    The string of the third party provider, such as 'youtube' or 'flickr'.
 *  @param $url
 *    The url for the xml request.
 *  @param $args
 *    An array of args to pass to the xml url.
 *  @param $cached
 *    (Optional) If TRUE, the result of this xml request will be cached. good to
 *    play nice with the third party folks so they don't stop providing service
 *    to your site...
 *  @param $return_errors
 *    (Optional) If TRUE and an error is encountered, a descriptive error array
 *    will be returned with elements for code, message and stat => 'error'.
 *  @param $hash_extra
 *    (Optional) The key for caching is created from the arguments.  If your
 *    provider does not use arguments (or uses the same arguments for each media
 *    item, you must pass a unique string as $hash_extra.  Currently this is
 *    only used by bliptv and archive.org
 *  @param $serialized
 *    (Optional) Most uses of this function are expecting an XML file to be
 *    returned.  However some providers (Flickr) can instead return a serialized
 *    PHP array.  In this case set $serialized to TRUE.
 *  @param $json
 *   If TRUE, then the result will be a json encoded string.
 *  @return
 *    The xml results returned as an array.
 */
function emfield_request_xml($provider, $url, $args = array(), $cached = TRUE, $return_errors = FALSE, $hash_extra = FALSE, $serialized = FALSE, $json = FALSE) {
  ksort($args);

  // Build an argument hash that we'll use for the cache id and api signing.
  $arghash = $provider . ':';
  foreach ($args as $k => $v) {
    $arghash .= $k . $v;
  }

  // Build the url.
  foreach ($args as $k => $v) {
    $encoded_params[] = urlencode($k) . '=' . urlencode($v);
  }
  if (!empty($encoded_params)) {
    $url .= '?' . implode('&', $encoded_params);
  }

  // Some providers, such as bliptv, actually change the url, and not just the
  // queries. We provide an extra section for a unique identifier in that case.
  if (isset($hash_extra)) {
    $arghash .= ':' . $hash_extra;
  }

  // If it's a cachable request, try to load a cached value.
  if ($cached && ($cache = cache_get($arghash, 'cache'))) {
    return $cache->data;
  }

  // Connect and fetch a value.
  $result = drupal_http_request($url);
  if (!empty($result->error)) {
    if ($return_errors) {
      return array(
        'stat' => 'error',
        'code' => $result->code,
        'message' => 'HTTP Error: ' . $result->error,
      );
    }
    emfield_set_error(t("Could not connect to @provider to request XML: HTTP error @error.", array(
      '@error' => $result->code,
      '@provider' => $provider,
    )));
    return array();
  }
  if ($json) {
    $response = (array) json_decode($result->data);
  }
  else {
    if ($serialized) {

      // Flickr gives us a serialized php array. Make sure it unserializes.
      $response = unserialize($result->data);
      if (!$response) {
        if ($return_errors) {
          return array(
            'stat' => 'error',
            'code' => '-1',
            'message' => 'The response was corrupted, it could not be unserialized.',
          );
        }
        emfield_set_error(t("The response from @provider was corrupted and could not be unserialized.", array(
          '@provider' => $provider,
        )));
        return array();
      }
    }
    else {
      $parser = drupal_xml_parser_create($result->data);
      $vals = array();
      $index = array();
      xml_parse_into_struct($parser, $result->data, $vals, $index);
      xml_parser_free($parser);
      $response = array();
      $response['_emfield_arghash'] = $arghash;
      $level = array();
      $start_level = 1;
      foreach ($vals as $xml_elem) {
        if ($xml_elem['type'] == 'open') {
          if (array_key_exists('attributes', $xml_elem)) {
            list($level[$xml_elem['level']], $extra) = array_values($xml_elem['attributes']);
          }
          else {
            $level[$xml_elem['level']] = $xml_elem['tag'];
          }
        }
        if ($xml_elem['type'] == 'complete') {
          $php_stmt = '$response';
          while ($start_level < $xml_elem['level']) {
            $php_stmt .= '[$level[' . $start_level . ']]';
            $start_level++;
          }
          $php_stmt .= '[$xml_elem[\'tag\']][] = $xml_elem[\'value\'];' . $php_stmt . '[$xml_elem[\'tag\']][] = $xml_elem[\'attributes\'];';
          eval($php_stmt);
          $start_level--;
        }
      }
    }
  }
  if ($cached) {
    cache_set($arghash, $response, 'cache', time() + variable_get('emfield_cache_duration', 3600));
  }
  return $response;
}

/**
 * Get the HTTP Header of media, for mime-type and length.
 *
 *  @param $provider
 *    The string of the third party provider, such as 'youtube' or 'flickr'.
 *  @param $url
 *    The url for the media.
 *  @param $cached
 *    (Optional) If TRUE, the result of this xml request will be cached. good to
 *    play nice with the third party folks so they don't stop providing service
 *    to your site...
 *  @return
 *   The header results, returned as an array.
 */
function emfield_request_header($provider, $url, $cached = TRUE, $return_errors = TRUE) {

  // if it's cacheable, try to load a cached value
  if ($cached && ($cache = cache_get('emfield:request-header:' . $url, 'cache_emfield_xml'))) {
    return $cache->data;
  }
  $result = _emfield_http_request_header($url);
  if (!empty($result->error)) {
    if ($return_errors) {
      return $result;
    }
    emfield_set_error(t("Could not connect to @provider: HTTP error @error.", array(
      '@error' => $result->code,
      '@provider' => $provider,
    )));
    return array();
  }

  // Cache the result.
  cache_set('emfield:request-header:' . $url, $result, 'cache_emfield_xml', time() + variable_get('emfield_cache_duration', 3600));
  return $result;
}

/**
 * HTTP HEAD - just request the header.
 */
function _emfield_http_request_header($url, $retry = 4) {
  $result = drupal_http_request($url, array(), 'HEAD', NULL, 0);
  switch ($result->code) {

    // The intention is to retry if the correct information isn't available;
    // so far it's just tuned for YouTube. It's possible/probable that Moved
    // Temporarily will give the headers required elsewhere, and it maybe best
    // to test on the content of the header.
    case 200:

    // OK
    case 304:

      // Not modified - this shouldn't happen here.
      break;
    case 301:

    // Moved permanently.
    case 302:

    // Moved temporarily.
    case 303:

    // See Other <-- drupal_http_request doesn't deal with this; we
    // need this for YouTube.
    case 307:

      // Moved temporarily.
      $location = $result->headers['Location'];
      if ($retry > 0) {
        $result = _emfield_http_request_header($result->headers['Location'], --$retry);
        $result->redirect_code = $result->code;
      }
      $result->redirect_url = $location;
      break;
    default:
  }
  return $result;
}
function emfield_set_error($error) {
  watchdog('emfield', '!error', array(
    '!error' => $error,
  ), WATCHDOG_WARNING);
}

/**
 * Return an array of installed .inc files and/or loads them upon request.
 * This routine is modeled after drupal_system_listing() (and also depends on
 * it). It's major difference, however, is that it loads .inc files by default.
 *
 *  @param $provider
 *   (Optional) Name of the passed $provider to find (e.g. "youtube", "google").
 *  @return
 *   An array of file objects optionally matching $provider.
 */
function emfield_system_list($module, $provider = NULL, $options = array()) {
  $files = $override_files = module_invoke_all('emfield_providers', $module, $provider);

  //   $files = drupal_system_listing("$provider\.inc$", drupal_get_path('module', $module) ."/providers", 'name', 0);
  //   $files = array_merge($files, $override_files);
  ksort($files);
  foreach ($files as $provider => $file) {
    emfield_include_list($file);
    if (!function_exists($module . '_' . $provider . '_info')) {
      emfield_include_list($file, TRUE);
      unset($files[$provider]);
      if (function_exists($module . '_' . $provider . '_obsolete') && variable_get('emfield_' . $module . '_allow_' . $provider, TRUE)) {
        if (isset($options['suppress_errors']) && $options['suppress_errors']) {
          continue;
        }
        $error = t('Attempted to load obsolete provider: %provider at @file.', array(
          '%provider' => $provider,
          '@file' => $file->filename,
        ));
        emfield_set_error($error);
        if (user_access('administer site configuration')) {
          drupal_set_message($error, error);
          call_user_func($module . '_' . $provider . '_obsolete');
          drupal_set_message(t('You may also choose to !turn_off this warning about the missing or obsolete Embedded Media Field %provider provider file.', array(
            '%provider' => $provider,
            '!turn_off' => l(t('turn off'), 'admin/content/emfield/obsolete/' . $module . '/' . $provider, array(
              'query' => 'destination=' . $_GET['q'],
            )),
          )), 'error');
        }
      }
    }
  }
  return $files;
}

/**
 * Maintains a list of all loaded include files.
 *
 * @param $file
 *   Optional; a file object (from emfield_system_list()) to be included.
 * @param $remove
 *   Optional boolean; if TRUE, then remove the file from the list.
 * @return
 *   An array of all loaded includes (without the .inc extension).
 */
function emfield_include_list($file = NULL, $remove = FALSE) {
  static $list = array();
  if ($file && $file->filename && !isset($list[$file->filename])) {
    include_once './' . $file->filename;
    $list[$file->filename] = $file->name;
  }
  if ($file && $remove) {
    unset($list[$file->filename]);
  }
  return $list;
}

/**
 * Determine whether an include implements a hook, cf. module_hook.
 *
 * @param $provider
 *  The name of the provider file (without the .inc extension), such as
 *  'youtube' or 'google'.
 * @param $hook
 *  The name of the hook (e.g. "thumbnail", "settings", etc.).
 * @return
 *  TRUE if the provider is loaded and the hook is implemented.
 */
function emfield_include_hook($module, $provider, $hook) {
  return function_exists($module . '_' . $provider . '_' . $hook);
}

/**
 * Invoke hook in a particular include.
 *
 * @param $module
 *  the helper module
 * @param $provider
 *   The name of the provider (without the .inc extension).
 * @param $hook
 *   The name of the hook (e.g. "settings", "thumbnail", etc.).
 * @param ...
 *   Arguments to pass to the hook implementation.
 * @return
 *   The return value of the hook implementation.
 */
function emfield_include_invoke() {
  $args = func_get_args();
  $module = array_shift($args);
  $provider = array_shift($args);
  $hook = array_shift($args);
  $function = $module . '_' . $provider . '_' . $hook;
  emfield_system_list($module, $provider);
  return emfield_include_hook($module, $provider, $hook) ? call_user_func_array($function, $args) : NULL;
}

/**
 * Custom filter for provider NOT NULL.
 */
function emfield_views_handler_filter_is_not_null($op, $filter, $filterinfo, &$query) {
  if ($op == 'handler') {
    $query
      ->ensure_table($filterinfo['table']);
    if ($filter['value']) {
      $qs = "%s.%s <> '' AND %s.%s IS NOT NULL";
    }
    else {
      $qs = "%s.%s = '' OR %s.%s IS NULL";
    }
    $query
      ->add_where($qs, $filterinfo['table'], $filterinfo['content_db_info']['columns']['provider']['column'], $filterinfo['table'], $filterinfo['content_db_info']['columns']['provider']['column']);
  }
}

/**
 * Create a list of providers.
 *  @TODO: Is this legacy? Not sure if it's called, and seems specific to
 *  emvideo. However, it's possible this was never extended beyond emvideo.
 *  @TODO: Allow views listings of other supported modules.
 */
function emfield_views_handler_filter_provider_list($op) {
  $providers = array();
  foreach (emfield_system_list('emvideo') as $provider => $info) {
    $providers[$provider] = $info->name;
  }
  return $providers;
}

/**
 * Views handler for the provider list filter.
 */
function emfield_views_handler_filter_provider($op, $filter, $filterinfo, &$query) {
  if ($op == 'handler') {
    $query
      ->ensure_table($filterinfo['table']);
    if ($filter['operator'] == 'OR') {
      foreach ($filter['value'] as $provider) {
        $items[] = "%s.%s = '{$provider}'";
        $where[] = $filterinfo['table'];
        $where[] = $filterinfo['content_db_info']['columns']['provider']['column'];
      }
      $qs = implode(' OR ', $items);
    }
    else {
      foreach ($filter['value'] as $provider) {
        $items[] = "%s.%s <> '{$provider}'";
        $where[] = $filterinfo['table'];
        $where[] = $filterinfo['content_db_info']['columns']['provider']['column'];
      }
      $qs = implode(' AND ', $items);
    }
    $query
      ->add_where($qs, $where);
  }
}

/**
 * Handle the provider argument. This is called from a wrapper that includes the
 * module.
 */
function _emfield_handler_arg_provider($op, &$query, $argtype, $arg = '', $module = '') {
  if ($op == 'filter') {
    $field_name = drupal_substr($argtype['type'], 10);
  }
  else {
    $field_name = drupal_substr($argtype, 10);
  }
  $field = content_fields($field_name);

  // @TODO: This hook no longer exists in D6.
  $db_info = content_database_info($field);
  $main_column = $db_info['columns']['provider'];

  // The table name used here is the Views alias for the table, not the actual
  // table name.
  $table = 'node_data_' . $field['field_name'];
  switch ($op) {
    case 'summary':
      $query
        ->ensure_table($table);
      $query
        ->add_field($main_column['column'], $table);
      $query
        ->add_groupby($table . '.' . $main_column['column']);
      return array(
        'field' => $table . '.' . $main_column['column'],
      );
    case 'sort':
      $query
        ->ensure_table($table);
      $query
        ->add_orderby($table, $main_column['column'], $argtype);
      break;
    case 'filter':
      $query
        ->ensure_table($table);
      $where = db_escape_string($arg);
      $query
        ->add_where($table . '.' . $main_column['column'] . " = '%s'", $where);
      break;
    case 'link':
      $provider = emfield_system_list($module, $query->{$main_column}['column']);
      $info = emfield_include_invoke($module, $provider[$query]->name, 'info');
      $title = $info['name'];
      return l($title, $arg . '/' . $query->{$main_column}['column']);
    case 'title':
      $provider = emfield_system_list($module, $query);
      $info = emfield_include_invoke($module, $provider[$query]->name, 'info');
      $title = $info['name'];
      return $title ? $title : check_plain($query);
  }
}

/**
 * Allow providers to create their own themes.
 *
 * @TODO: Remove this once we've moved all providers outside emfield, as their
 * wrapping modules can use standard Drupal hooks.
 */
function emfield_provider_themes($module, $provider = NULL) {
  $themes = array();
  if ($provider && ($subthemes = emfield_include_invoke($module, $provider, 'subtheme'))) {
    $themes = $subthemes;
  }
  $providers = emfield_system_list($module);
  foreach ($providers as $provider) {
    if ($subthemes = emfield_include_invoke($module, $provider->name, 'emfield_subtheme')) {
      $themes += $subthemes;
    }
  }
  return $themes;
}

/**
 * Allow providers to create their own menus.
 *
 * @TODO: Remove this once we've moved all providers outside emfield, as their
 * wrapping modules can use standard Drupal hooks.
 */
function emfield_provider_menus($module, $provider = NULL) {
  $menus = array();
  if ($provider && ($submenus = emfield_include_invoke($module, $provider, 'submenu'))) {
    $menus = $submenus;
  }
  $providers = emfield_system_list($module);
  foreach ($providers as $provider) {
    if ($submenus = emfield_include_invoke($module, $provider->name, 'emfield_submenu')) {
      $menus = array_merge($menus, (array) $submenus);
    }
  }
  return $menus;
}

/**
 * Returns the URL to the registered JW Flash Media Player, if such exists.
 */
function emfield_flvmediaplayer_url($reset = FALSE) {
  static $path;
  if (!isset($path) || $reset) {
    if (module_exists('flvmediaplayer')) {
      $path = variable_get('flvmediaplayer_path', drupal_get_path('module', 'flvmediaplayer') . '/mediaplayer.swf');
    }
    else {
      if ($reset || ($path = variable_get('emfield_flvmediaplayer_url', NULL) && !$path)) {
        $path = _emfield_autodiscover_path('(^player-viral\\.swf$|^player\\.swf$|^mediaplayer\\.swf$)');
        variable_set('emfield_flvmediaplayer_url', $path);
      }
    }
  }
  return $path;
}

/**
 * Returns the URL to the registered JW Image Rotator, if such exists.
 */
function emfield_imagerotator_url($reset = FALSE) {
  static $path;
  if (is_null($path) || $reset) {
    if (is_null($path = variable_get('emfield_imagerotator_url', NULL)) || !$path && $reset) {
      $path = _emfield_autodiscover_path('(^imagerotator\\.swf$)');
      variable_set('emfield_imagerotator_url', $path);
    }
  }
  return $path;
}

/**
 * Search site library paths for the specific pattern.
 */
function _emfield_autodiscover_path($pattern) {
  foreach (array(
    'libraries',
    'plugins',
    'modules',
  ) as $folder) {
    $files = drupal_system_listing($pattern, $folder, 'basename', 0);
    if (!empty($files)) {
      $file = array_pop($files);
      return $file->filename;
    }
  }
}

/**
 * Implementation of Devel module's hook_content_generate().
 */
function emfield_content_generate($node, $field) {
  $item = $urls = array();
  $module = $field['module'];
  foreach (emfield_allowed_providers($field, $module) as $provider) {
    $provider_urls = emfield_include_invoke($module, $provider->name, 'content_generate', $node, $field);
    if (is_array($provider_urls)) {
      $urls[$provider->name] = $provider_urls;
    }
  }
  if (!empty($urls)) {
    $provider = array_rand($urls);
    $item['provider'] = $provider;
    $key = array_rand($urls[$provider]);
    $item['embed'] = $urls[$provider][$key];
    $item = _emfield_field_submit_id($field, $item, $module);
  }
  return $item;
}

/**
 *  Helper function for hook_field_settings().
 */
function emfield_emfield_field_settings($op, $field) {
  module_load_include('inc', 'emfield', 'emfield.cck');
  return _emfield_emfield_field_settings($op, $field);
}

/**
 * Implementation of hook_views_api().
 */
function emfield_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'emfield') . '/includes/views',
  );
}

/**
 * Implementation of hook_theme().
 */
function emfield_theme() {
  return array(
    'emfield_handler_field_provider' => array(
      'arguments' => array(
        'item' => NULL,
        'field' => NULL,
        'node' => NULL,
        'values' => NULL,
        'format' => NULL,
      ),
    ),
    'emfield_handler_field_status' => array(
      'arguments' => array(
        'item' => NULL,
        'field' => NULL,
        'node' => NULL,
        'values' => NULL,
        'format' => NULL,
      ),
    ),
    'emfield_swfobject' => array(
      'arguments' => array(
        "url" => NULL,
        '$div_id' => NULL,
        'width' => NULL,
        'height' => NULL,
        'version' => "8",
      ),
      'path' => drupal_get_path('module', 'emfield') . '/includes/themes',
      'file' => 'emfield.themes.inc',
    ),
  );
}

/**
 *  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 emfield_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
  module_load_include('inc', 'emfield', 'includes/emfield.xml');
  return _emfield_retrieve_xml($url, $display_errors, $refresh);
}

/**
 * This will audit all nodes to ensure all emfield providers are present.
 *
 * The function will pass through all emfield fields to see if there is any
 * content in the system without a provider.
 *
 * @return array
 *  An array of all missing providers. Ideally the array will be empty.
 */
function emfield_audit() {
  module_load_include('inc', 'emfield', 'includes/emfield.audit');
  return _emfield_audit();
}

/**
 * Implementation of hook_init().
 */
function emfield_init() {
  global $emfield_registered_classes;

  // Build our media object class registry.
  foreach (module_implements('emfield_register') as $module) {
    foreach (module_invoke($module, 'emfield_register') as $class_name => $class) {
      $emfield_registered_classes[$class_name] = is_array($class) ? $class : array();
      if (!isset($emfield_registered_classes[$class_name]['class_name'])) {
        $emfield_registered_classes[$class_name]['class_name'] = $class_name;
      }
      if (!isset($emfield_registered_classes[$class_name]['name'])) {
        $emfield_registered_classes[$class_name]['name'] = $class_name;
      }
      if (!isset($emfield_registered_classes[$class_name]['path'])) {
        $emfield_registered_classes[$class_name]['path'] = drupal_get_path('module', $module);
      }
      if (!isset($emfield_registered_classes[$class_name]['file'])) {
        $emfield_registered_classes[$class_name]['file'] = $class_name . '.inc';
      }
    }
  }

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

/**
 * Autoload the media object classes when needed.
 */
function emfield_autoload($class_name) {
  global $emfield_registered_classes;
  if (isset($emfield_registered_classes[$class_name])) {
    include_once $emfield_registered_classes[$class_name]['path'] . '/' . $emfield_registered_classes[$class_name]['file'];
  }
}

/**
 * Builds and returns a media object based on the parsed URL.
 */
function emfield_media($url, $overrides = array()) {
  global $emfield_registered_classes;

  // First parse the URL.
  foreach ($emfield_registered_classes as $provider => $class) {
    if ($class['parse callback'] && $class['class_name']) {
      if ($parsed = $class['parse callback']($url, $overrides)) {

        // If we have a provider class, then implement and return it.
        return new $class['class_name']($parsed['code'], $overrides);
      }
    }
  }
}

Functions

Namesort descending Description
emfield_allowed_providers Return a list of providers allowed for a specific field.
emfield_audit This will audit all nodes to ensure all emfield providers are present.
emfield_autoload Autoload the media object classes when needed.
emfield_content_generate Implementation of Devel module's hook_content_generate().
emfield_emfield_content_is_empty Helper function to implement hook_content_is_empty.
emfield_emfield_field A helper function for hook_field(), called by the various sub-modules.
emfield_emfield_field_formatter Format our field for display. This is actually called originally by each helper module that implements hook_field_formatter, which then call this.
emfield_emfield_field_settings Helper function for hook_field_settings().
emfield_emfield_widget
emfield_emfield_widget_settings Widgets *
emfield_field_columns
emfield_flvmediaplayer_url Returns the URL to the registered JW Flash Media Player, if such exists.
emfield_imagerotator_url Returns the URL to the registered JW Image Rotator, if such exists.
emfield_implement_types This returns a list of content types that are implemented by emfield.
emfield_include_hook Determine whether an include implements a hook, cf. module_hook.
emfield_include_invoke Invoke hook in a particular include.
emfield_include_list Maintains a list of all loaded include files.
emfield_init Implementation of hook_init().
emfield_media Builds and returns a media object based on the parsed URL.
emfield_menu Implementation of hook_menu().
emfield_nodeapi Implementation of hook_nodeapi().
emfield_node_operations Implementation of hook_node_operations().
emfield_operations_reload This reloads and saves the data for all the nid's in the array.
emfield_parse_embed This will parse the url or embedded code pasted by the node submitter.
emfield_provider_menus Allow providers to create their own menus.
emfield_provider_themes Allow providers to create their own themes.
emfield_request_header Get the HTTP Header of media, for mime-type and length.
emfield_request_xml When an include file requires to read an xml to receive information, such as for thumbnails, this script can be used to request the xml and return it as an array.
emfield_retrieve_xml A wrapper around simplexml to retrieve a given XML file.
emfield_set_error
emfield_system_list Return an array of installed .inc files and/or loads them upon request. This routine is modeled after drupal_system_listing() (and also depends on it). It's major difference, however, is that it loads .inc files by default.
emfield_theme Implementation of hook_theme().
emfield_views_api Implementation of hook_views_api().
emfield_views_handler_filter_is_not_null Custom filter for provider NOT NULL.
emfield_views_handler_filter_provider Views handler for the provider list filter.
emfield_views_handler_filter_provider_list Create a list of providers. @TODO: Is this legacy? Not sure if it's called, and seems specific to emvideo. However, it's possible this was never extended beyond emvideo. @TODO: Allow views listings of other supported modules.
_emfield_autodiscover_path Search site library paths for the specific pattern.
_emfield_field_submit_id Replace embedded code with the extracted id. this goes in the field 'value'. This also allows you to grab directly from the URL to display the content from field 'provider'.
_emfield_field_validate_id Extract the id from embedded code or url.
_emfield_handler_arg_provider Handle the provider argument. This is called from a wrapper that includes the module.
_emfield_http_request_header HTTP HEAD - just request the header.

Constants

Namesort descending Description
EMFIELD_STATUS_AVAILABLE
EMFIELD_STATUS_PRIVATE
EMFIELD_STATUS_UNAVAILABLE @file Embedded Media Field is a CCK-based framework for 3rd party media files.