'admin/content/emfield', 'title' => t('Embedded Media Field configuration'), 'description' => t('Configure Embedded Media Field: Allow content types to use various 3rd party providers, enter API keys, etc.'), 'callback' => 'drupal_get_form', 'callback arguments' => 'emfield_settings', 'access' => user_access('administer site configuration')); } return $items; } /** * Implement hook_settings */ function emfield_settings() { $form = array(); $header = array(t('Feature'), t('Supported'), t('Notes')); foreach (module_implements('emfield_info', true) as $module) { $emfield_info = module_invoke($module, 'emfield_info'); $providers = emfield_system_list($module); $form[$module] = array( '#type' => 'fieldset', '#title' => t('@neighborhood', array('@neighborhood' => $emfield_info['#name'])), '#description' => $emfield_info['#settings_description'], '#collapsible' => true, '#collapsed' => true, ); $form[$module]['providers'] = array( '#type' => 'fieldset', '#title' => t('Providers'), '#description' => t('The following settings determine what providers are allowed, and what provider-specific options, if any, are set.'), '#collapsible' => true, '#collapsed' => false, ); foreach ($providers as $provider) { $info = emfield_include_invoke($module, $provider->name, 'info'); $form[$module]['providers'][$provider->name] = array( '#type' => 'fieldset', '#title' => t('@provider configuration', array('@provider' => $info['name'])), '#description' => $info['settings_description'], '#collapsible' => true, '#collapsed' => true, ); if (is_array($info['supported_features']) && !empty($info['supported_features'])) { $form[$module]['providers'][$provider->name]['supported_features'] = array( '#type' => 'fieldset', '#title' => t('Supported features'), '#description' => t('This is a list of the current state of support for the following features by %provider:', array('%provider' => $info['name'])), '#collapsible' => true, '#collapsed' => true, '#weight' => 7, ); $form[$module]['providers'][$provider->name]['supported_features']['features'] = array( '#type' => 'markup', '#value' => theme('table', $header, $info['supported_features']), ); } $form[$module]['providers'][$provider->name]['emfield_' . $module . '_allow_' . $provider->name] = array( '#type' => 'checkbox', '#title' => t('Allow content from %provider', array('%provider' => $info['name'])), '#description' => t('If checked, then content types may be created that allow content to be provided by %provider.', array('%provider' => $info['name'])), '#weight' => -10, '#default_value' => variable_get('emfield_' . $module . '_allow_' . $provider->name, true), ); $form[$module]['providers'][$provider->name][] = emfield_include_invoke($module, $provider->name, 'settings'); } $form[$module] = array_merge($form[$module], module_invoke($module, 'emfield_settings')); } return system_settings_form($form); } function emfield_field_columns() { $columns = array( 'embed' => array('type' => 'longtext', 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE), 'value' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE), 'provider' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE), 'data' => array('type' => 'longtext', 'not null' => TRUE, 'default' => "''", 'sortable' => false), ); return $columns; } /** Implementation of hook_emfield_field **/ function emfield_emfield_field($op, &$node, $field, &$items, $teaser, $page, $module) { switch ($op) { // TODO: nothing to validate at the moment. we need to have different api's have a chance to validate case 'validate': if ($field['multiple']) { foreach ($items as $delta => $item) { $error_field = $field['field_name'].']['.$delta.'][embed'; _emfield_field_validate_id($field, $item, $error_field, $module); } } else { $error_field = $field['field_name']; _emfield_field_validate_id($field, $items[0], $error_field, $module); } break; case 'submit': if ($field['multiple']) { foreach ($items as $delta => $item) { $list = _emfield_field_submit_id($field, $item, $module); $items[$delta]['provider'] = $list['provider']; $items[$delta]['value'] = $list['value']; $items[$delta]['data'] = $list['data']; } } else { $list = _emfield_field_submit_id($field, $items[0], $module); $items[0]['provider'] = $list['provider']; $items[0]['value'] = $list['value']; $items[0]['data'] = $list['data']; } break; case 'insert': case 'update': // we need to manually serialize the 'data' array if ($field['multiple']) { foreach ($items as $delta => $item) { $items[$delta]['data'] = serialize($items[$delta]['data']); } } else { $items[0]['data'] = serialize($items[0]['data']); } break; case 'load': // We need to unserialize the 'data' column manually $field_name = $field['field_name']; $node_field = $node->$field_name; if ($field['multiple']) { foreach ($items as $delta => $item) { $data = (array)unserialize($items[$delta]['data']); $items[$delta]['data'] = $data; $node_field[$delta]['data'] = $data; } } else { $data = (array)unserialize($items[0]['data']); $items[0]['data'] = $data; $node_field[0]['data'] = $data; } $node->$field_name = $node_field; $return = array(); $return[$field_name] = $items; return $return; break; } } /** * return a list of providers allowed for a specific field */ function emfield_allowed_providers($field, $module) { // $module = $field['widget']['helper_module']; $allowed_providers = emfield_system_list($module); $providers = array(); $allow_all = true; foreach ($allowed_providers as $test) { if (!variable_get('emfield_allow_' . $module . '_' . $test->name, true)) { unset($allowed_providers[$test->name]); } else { $allow_all &= !$field['widget']['providers'][$test->name]; } } if (!$allow_all) { foreach ($allowed_providers as $test) { if (!$field['widget']['providers'][$test->name]) { unset($allowed_providers[$test->name]); } } } return $allowed_providers; } /** * This will parse the url or embedded code pasted by the node submitter. * returns either an empty array (if no match), or an array of provider and value. */ function emfield_parse_embed($field, $embed = '', $module) { if ($embed) { // $module = $field['widget']['helper_module']; $providers = emfield_allowed_providers($field, $module); foreach ($providers as $provider) { $success = emfield_include_invoke($module, $provider->name, 'extract', $embed); // we've been given an array of regex strings, so let's see if we can find a match if (is_array($success)) { foreach ($success as $regex) { $matches = NULL; if (preg_match($regex, $embed, $matches)) { return array('provider' => $provider->name, 'value' => $matches[1]); } } } // the invoked include module did its own parsing and found a match 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) { // does nothing at this time... TODO: fix this up return _emfield_field_submit_id($field, $item, $module); } /** * replace embedded code with the extracted id. this goes in the field 'value' * also allows you to grab directly from the URL to display the content from field 'provider' */ function _emfield_field_submit_id($field, $item, $module) { // $module = $field['widget']['helper_module']; $item = array_merge($item, emfield_parse_embed($field, $item['embed'], $module)); $item['data'] = (array)emfield_include_invoke($module, $item['provider'], 'data', $field, $item); return $item; } function emfield_emfield_field_formatter($field, $item, $formatter, $node, $module) { // if we're coming from a preview, we need to extract our new embedded value... if ($item['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 (!$node->type) { $type = content_types($field['type_name']); $field['widget'] = $type['fields'][$field['field_name']]['widget']; } // and sometimes our data is still unserialized, again from views if (!is_array($item['data'])) { $item['data'] = (array)unserialize($item['data']); } // $module = $field['widget']['helper_module']; $output .= theme($module . '_' . $formatter, $field, $item, $formatter, $node); return $output; } /** Widgets **/ function emfield_emfield_widget_settings($op, $widget, $module) { switch ($op) { case 'form': $form = array(); // $form['helper_module'] = array( // '#type' => 'value', // '#value' => $module, // ); $options = array(); $providers = emfield_system_list($module); foreach ($providers as $provider) { if (variable_get('emfield_allow_' . $module . '_' . $provider->name, true)) { $info = emfield_include_invoke($module, $provider->name, 'info'); $options[$provider->name] = $info['name']; } } $form['provider_list'] = array( '#type' => 'fieldset', '#title' => t('Providers Supported'), '#description' => t('Select which third party providers you wish to allow for this content type from the list below. If no checkboxes are checked, then all providers will be supported. When a user submits new content, the URL they enter will be matched to the provider, assuming that provider is allowed here.'), '#collapsible' => true, '#collapsed' => false, ); $form['provider_list']['providers'] = array( '#type' => 'checkboxes', '#title' => t('Providers'), '#default_value' => $widget['providers'] ? $widget['providers'] : array(), '#options' => $options, ); return $form; case 'save': $columns = array('providers'); //, 'helper_module', ); return $columns; } } function emfield_emfield_widget($op, &$node, $field, &$node_field, $module) { // $module = $field['widget']['helper_module']; switch ($op) { case 'form': $form = array(); $form[$field['field_name']] = array('#tree' => TRUE); $textfield = 'embed'; $field['required'] = FALSE; $providers = emfield_allowed_providers($field, $module); $urls = array(); foreach ($providers as $provider) { // don't check providers not allowed if (variable_get('emfield_allow_' . $module . '_' . $provider->name, true)) { $info = emfield_include_invoke($module, $provider->name, 'info'); $urls[] = l($info['name'], $info['url'], array('target' => '_blank')); } } $textfield_title = t($field['widget']['label']); $textfield_description = t('Enter the URL or Embed Code here. The embedded third party content will be parsed and displayed appropriately from this.'); $textfield_description .= '
' . t('The following services are provided: !urls', array('!urls' => implode(', ', $urls))); if ($field['multiple']) { $form[$field['field_name']]['#type'] = 'fieldset'; $form[$field['field_name']]['#title'] = t($field['widget']['label']); $delta = 0; foreach ($node_field as $data) { if (isset($data[$textfield])) { $form[$field['field_name']][$delta][$textfield] = array( '#type' => 'textfield', '#title' => $textfield_title, '#description' => $textfield_description, '#default_value' => $data[$textfield], '#required' => ($delta == 0) ? $field['required'] : FALSE, '#maxlength' => 1024, ); $form[$field['field_name']][$delta]['value'] = array( '#type' => 'value', '#value' => $data['value'], ); if ($data['value']) { $info = emfield_include_invoke($module, $data['provider'], 'info'); $form[$field['field_name']][$delta]['markup_value'] = array( '#type' => 'item', '#value' => t('(@provider ID: !value)', array('@provider' => $info['name'], '!value' => l($data['value'], emfield_include_invoke($module, $info['provider'], 'embedded_link', $data['value'], $data['data']), array('target' => '_blank')))), ); } $delta++; } } foreach (range($delta, $delta + 2) as $delta) { $form[$field['field_name']][$delta][$textfield] = array( '#type' => 'textfield', '#title' => $textfield_title, '#description' => $textfield_description, '#default_value' => '', '#required' => ($delta == 0) ? $field['required'] : FALSE, '#maxlength' => 1024, ); $form[$field['field_name']][$delta]['value'] = array( '#type' => 'value', '#title' => '', ); } } else { $form[$field['field_name']][0][$textfield] = array( '#type' => 'textfield', '#title' => $textfield_title, //t($field['widget']['label']), '#description' => $textfield_description, '#default_value' => isset($node_field[0][$textfield]) ? $node_field[0][$textfield] : '', '#required' => $field['required'], '#maxlength' => 1024, ); if ($textfield == 'embed') { $value = isset($node_field[0]['value']) ? $node_field[0]['value'] : ''; $form[$field['field_name']][0]['value'] = array( '#type' => 'value', '#value' => $value, ); if ($value) { $info = emfield_include_invoke($module, $node_field[0]['provider'], 'info'); $form[$field['field_name']][0]['value_markup'] = array( '#type' => 'item', '#value' => t('(@provider ID: !value)', array('@provider' => $info['provider'], '!value' => l($value, emfield_include_invoke($module, $info['provider'], 'embedded_link', $value, $node_field[0]['data']), array('target' => '_blank')))), ); } } } return $form; default: break; } } /** * 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. * i suspect it could be done easier (and more quickly) in php 5. * @param $provider * the string of the third party provider, such as 'youtube', 'flikr', or 'google' * @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 w/ * the third party folks so they don't stop providing service to your site... * @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) { 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 unserialize($cache->data); } // connect and fetch a value $result = drupal_http_request($url); if ($result->code != 200) { if ($return_errors) { return array( 'stat' => 'error', 'code' => $result->code, 'message' => 'HTTP Error: '. $result->error, ); } emfield_set_error(t("Could not connect to @provider, HTTP error @error", array('@error' => $result->code, '@provider' => $provider))); return array(); } 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(); $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--; } } } cache_set($arghash, 'cache', serialize($response), time() + variable_get('emfield_cache_duration', 3600)); return $response; } function emfield_set_error($error) { watchdog('emfield', $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", etc.). * @param $load * Defaults to true; whether to include matching files into memory. * @return * An array of file objects optionally matching $provider. */ function emfield_system_list($module, $provider = NULL, $load = true) { $files = drupal_system_listing("$provider\.inc", drupal_get_path('module', $module) . "/providers", 'name', 0); ksort($files); if ($load) { foreach ($files as $file) { emfield_include_list($file); } } return $files; } /** * Maintains a list of all loaded include files. * * @param $file * Optional; a file object (from emfield_system_list()) to be included. * @return * An array of all loaded includes (without the .inc extension). */ function emfield_include_list($file = NULL) { static $list = array(); if ($file && !isset($list[$file->name])) { include_once('./' . $file->filename); $list[$file->name] = $file->name; } 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; }