/[drupal]/contributions/modules/emfield/emfield.module
ViewVC logotype

Contents of /contributions/modules/emfield/emfield.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.26 - (show annotations) (download) (as text)
Wed Mar 11 14:49:00 2009 UTC (8 months, 2 weeks ago) by aaron
Branch: MAIN
CVS Tags: HEAD
Changes since 1.25: +3 -3 lines
File MIME type: text/x-php
 * Pass $options array through formatter (allowing for future inline functionality) (aaron).
1 <?php
2 // $Id: emfield.module,v 1.25 2008/06/20 17:04:54 aaron Exp $
3
4 /**
5 * Implement hook_menu
6 */
7 function emfield_menu($may_cache) {
8 $items = array();
9 if ($may_cache) {
10 $items[] = array(
11 'path' => 'admin/content/emfield',
12 'title' => t('Embedded Media Field configuration'),
13 'description' => t('Configure Embedded Media Field: Allow content types to use various 3rd party providers, enter API keys, etc.'),
14 'callback' => 'drupal_get_form',
15 'callback arguments' => 'emfield_settings',
16 'access' => user_access('administer site configuration'),
17 );
18 $items[] = array(
19 'path' => 'admin/content/emfield/media',
20 'title' => t('Media settings'),
21 'description' => t('Configure Embedded Media Field: Allow content types to use various 3rd party providers, enter API keys, etc.'),
22 'callback' => 'drupal_get_form',
23 'callback arguments' => 'emfield_settings',
24 'access' => user_access('administer site configuration'),
25 'type' => MENU_DEFAULT_LOCAL_TASK,
26 );
27 }
28 return $items;
29 }
30
31 /**
32 * Callback for admin/content/emfield
33 */
34 function emfield_settings() {
35 if (!module_exists('video_cck') && !module_exists('image_ncck') && !module_exists('emaudio')) {
36 drupal_set_message(t('The Embedded Media Field module does nothing on its own. You should also install the Embedded Video Field, Embedded Image Field, and/or Embedded Audio Field modules from the !modules. (If you do not see them listed there, under the CCK section, you may need to !download from its project page. They are all in the same package.)', array('!download' => l(t('download the module'), 'http://drupal.org/project/emfield'), '!modules' => l(t('modules administration page'), 'admin/build/modules'))), 'error');
37 }
38 $form = array();
39 $form['general'] = array(
40 '#type' => 'fieldset',
41 '#title' => t('General Settings'),
42 '#description' => t('These features will be generally available for use by related modules as needed.'),
43 '#collapsible' => TRUE,
44 '#collapsed' => TRUE,
45 );
46 if (module_exists('swfobject_api')) {
47 $swfobject_desc = t('As you have the !swfobject_api module installed, Embedded Media Field will use those settings, assuming you\'ve configured them properly. Visit its !settings for more information.', array('!swfobject_api' => l(t('SWFObject API'), 'http://drupal.org/project/swfobject_api', array('target' => '_blank')), l(t('settings page'), 'admin/settings/swfobject_api')));
48 }
49 else {
50 $swfobject_desc = t('If you have the swfobject.js file installed on your system, you can make it available to Embedded Media Field and its related modules by entering the information here. You can download and find out more about !here. You may also choose to install the !swfobject_api module, which will integrate automatically with this module..', array('!here' => l(t('SWFObject here'), 'http://code.google.com/p/swfobject/', array('target' => '_blank')), '!swfobject_api' => l(t('SWFObject API'), 'http://drupal.org/project/swfobject_api', array('target' => '_blank'))));
51 }
52 $form['general']['swfobject'] = array(
53 '#type' => 'fieldset',
54 '#title' => t('SWF Object'),
55 '#description' => $swfobject_desc,
56 '#collapsible' => TRUE,
57 );
58 $form['general']['swfobject']['emfield_swfobject'] = array(
59 '#type' => 'checkbox',
60 '#title' => t('Use SWFObject'),
61 '#default_value' => variable_get('emfield_swfobject', FALSE),
62 '#description' => t('When checked, then Embedded Media Field will use the SWFObject javascript library when it is able.'),
63 );
64 if (!module_exists('swfobject_api')) {
65 $form['general']['swfobject']['emfield_swfobject_location'] = array(
66 '#type' => 'textfield',
67 '#title' => t('SWFObject location'),
68 '#default_value' => variable_get('emfield_swfobject_location', ''),
69 '#description' => t('Enter the relative path to the swfobject.js file, without the preceding slash (/).'),
70 );
71 }
72 $header = array(t('Feature'), t('Supported'), t('Notes'));
73 foreach (module_implements('emfield_info', TRUE) as $module) {
74 $emfield_info = module_invoke($module, 'emfield_info');
75 $providers = emfield_system_list($module);
76 $form[$module] = array(
77 '#type' => 'fieldset',
78 '#title' => t('@neighborhood', array('@neighborhood' => $emfield_info['#name'])),
79 '#description' => $emfield_info['#settings_description'],
80 '#collapsible' => TRUE,
81 '#collapsed' => TRUE,
82 );
83 $form[$module]['providers'] = array(
84 '#type' => 'fieldset',
85 '#title' => t('Providers'),
86 '#description' => t('The following settings determine what providers are allowed, and what provider-specific options, if any, are set.'),
87 '#collapsible' => TRUE,
88 '#collapsed' => FALSE,
89 );
90 foreach ($providers as $provider) {
91 $info = emfield_include_invoke($module, $provider->name, 'info');
92 $form[$module]['providers'][$provider->name] = array(
93 '#type' => 'fieldset',
94 '#title' => t('@provider configuration', array('@provider' => $info['name'])),
95 '#description' => $info['settings_description'],
96 '#collapsible' => TRUE,
97 '#collapsed' => TRUE,
98 );
99 if (is_array($info['supported_features']) && !empty($info['supported_features'])) {
100 $form[$module]['providers'][$provider->name]['supported_features'] = array(
101 '#type' => 'fieldset',
102 '#title' => t('Supported features'),
103 '#description' => t('This is a list of the current state of support for the following features by %provider:', array('%provider' => $info['name'])),
104 '#collapsible' => TRUE,
105 '#collapsed' => TRUE,
106 '#weight' => 7,
107 );
108 $form[$module]['providers'][$provider->name]['supported_features']['features'] = array(
109 '#type' => 'markup',
110 '#value' => theme('table', $header, $info['supported_features']),
111 );
112 }
113 $form[$module]['providers'][$provider->name]['emfield_'. $module .'_allow_'. $provider->name] = array( '#type' => 'checkbox',
114 '#title' => t('Allow content from %provider', array('%provider' => $info['name'])),
115 '#description' => t('If checked, then content types may be created that allow content to be provided by %provider.', array('%provider' => $info['name'])),
116 '#weight' => -10,
117 '#default_value' => variable_get('emfield_'. $module .'_allow_'. $provider->name, TRUE),
118 );
119 $form[$module]['providers'][$provider->name][] = emfield_include_invoke($module, $provider->name, 'settings');
120 }
121 $form[$module] = array_merge($form[$module], module_invoke($module, 'emfield_settings'));
122 }
123
124 return system_settings_form($form);
125 }
126
127 function emfield_field_columns() {
128 $columns = array(
129 'embed' => array('type' => 'longtext', 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE),
130 'value' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE),
131 'provider' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => "''", 'sortable' => TRUE),
132 'data' => array('type' => 'longtext', 'not null' => TRUE, 'default' => "''", 'sortable' => FALSE),
133 );
134 foreach (module_implements('emfield_field_columns_extra') as $module) {
135 $extra = module_invoke($module, 'emfield_field_columns_extra');
136 if (is_array($extra)) {//print_r($extra);
137 $columns = array_merge($columns, $extra);
138 }
139 }
140 return $columns;
141 }
142
143 /** Implementation of hook_emfield_field **/
144 function emfield_emfield_field($op, &$node, $field, &$items, $teaser, $page, $module) {
145 switch ($op) {
146 // TODO: nothing to validate at the moment. we need to have different provider api's have a chance to validate
147 case 'validate':
148 foreach ($items as $delta => $item) {
149 $error_field = $field['multiple'] ? $field['field_name'] .']['. $delta .'][embed' : $field['field_name'];
150 _emfield_field_validate_id($field, $item, $error_field, $module, $delta);
151 }
152 break;
153
154 case 'submit':
155 foreach ($items as $delta => $item) {
156 $list = _emfield_field_submit_id($field, $item, $module);
157 $items[$delta]['provider'] = $list['provider'];
158 $items[$delta]['value'] = $list['value'];
159 $items[$delta]['data'] = $list['data'];
160 }
161 break;
162
163 case 'load':
164 // We need to unserialize the 'data' column manually
165 $field_name = $field['field_name'];
166 foreach ($items as $delta => $item) {
167 $data = (array)unserialize($items[$delta]['data']);
168 $items[$delta]['data'] = $data;
169 $node->{$field_name}[$delta]['data'] = $data;
170 }
171 $return = array();
172 $return[$field_name] = $items;
173 break;
174 case 'delete':
175 break;
176 }
177 // allow modules (such as emthumb) to alter our data.
178 foreach (module_implements('emfield_field_extra') as $module) {
179 $args = array($op, &$node, $field, &$items, $teaser, $page, $module);
180 $ret = call_user_func_array($module .'_emfield_field_extra', $args);
181 if (is_array($return) && is_array($ret)) {
182 $return = array_merge($return, $ret);
183 }
184 }
185 // drupal_set_message($op .' node field: <pre>'. print_r($items, true) .'</pre>');
186 if (in_array($op, array('insert', 'update'))) {
187 // we need to manually serialize the 'data' array
188 foreach ($items as $delta => $item) {
189 $items[$delta]['data'] = serialize($items[$delta]['data']);
190 }
191 }
192 return $return;
193 }
194
195 /**
196 * return a list of providers allowed for a specific field
197 */
198 function emfield_allowed_providers($field, $module) {
199 // $module = $field['widget']['helper_module'];
200 $allowed_providers = emfield_system_list($module);
201 $providers = array();
202 $allow_all = TRUE;
203 foreach ($allowed_providers as $test) {
204 if (!variable_get('emfield_' . $module . '_allow_' . $test->name, TRUE)) {
205 unset($allowed_providers[$test->name]);
206 }
207 else {
208 $allow_all &= !$field['widget']['providers'][$test->name];
209 }
210 }
211 if (!$allow_all) {
212 foreach ($allowed_providers as $test) {
213 if (!$field['widget']['providers'][$test->name]) {
214 unset($allowed_providers[$test->name]);
215 }
216 }
217 }
218 return $allowed_providers;
219 }
220
221 /**
222 * this returns a list of content types that are implemented by emfield
223 */
224 function emfield_implement_types($cached = TRUE) {
225 static $types;
226
227 if (!isset($types) || !$cached) {
228 // if it's a cachable request, try to load a cached value
229 if ($cached && $cache = cache_get('emfield_implement_types', 'cache')) {
230 $types = unserialize($cache->data);
231 }
232 else {
233 $system_types = _content_type_info();
234 $content_types = $system_types['content types'];
235 $field_types = $system_types['field types'];
236
237 // the array that will store type/field information for provider import
238 $types = array();
239 $modules = array();
240
241 foreach (module_implements('emfield_info', TRUE) as $module) {
242 $modules[$module] = $module;
243 }
244
245 // settings per content type for the module
246 foreach ($content_types as $content_type => $type) {
247 // determine which content types implement this module
248 foreach ($type['fields'] as $field_type => $field) {
249
250 // if this field type is defined by this module, then include it here
251 if ($module = $modules[$field_types[$field['type']]['module']]) {
252 // settings per content type per module
253 $types[$module][$content_type][$field_type] = $field;
254 }
255 }
256 }
257 }
258 }
259
260 cache_set('emfield_implement_types', 'cache', serialize($types), CACHE_PERMANENT);
261 return $types;
262 }
263
264 /**
265 * This will parse the url or embedded code pasted by the node submitter.
266 * returns either an empty array (if no match), or an array of provider and value.
267 */
268 function emfield_parse_embed($field, $embed = '', $module) {
269 // if ($embed) {
270 // $module = $field['widget']['helper_module'];
271 $providers = emfield_allowed_providers($field, $module);
272 foreach ($providers as $provider) {
273 $success = emfield_include_invoke($module, $provider->name, 'extract', $embed, $field);
274 // we've been given an array of regex strings, so let's see if we can find a match
275 if (is_array($success)) {
276 foreach ($success as $regex) {
277 $matches = NULL;
278 if (preg_match($regex, $embed, $matches)) {
279 return array('provider' => $provider->name, 'value' => $matches[1]);
280 }
281 }
282 }
283 // the invoked include module did its own parsing and found a match
284 else if ($success) {
285 return array('provider' => $provider->name, 'value' => $success);
286 }
287 }
288 // }
289 // we found no match
290 return array();
291 }
292
293 /**
294 * extract the id from embedded code or url
295 */
296 function _emfield_field_validate_id($field, $item, $error_field, $module, $delta = 0) {
297 // load the provider info
298 $item = _emfield_field_submit_id($field, $item, $module, $error_field);
299 // ensure we have proper provider info
300 if ($item['embed'] && !$item['value']) {
301 form_set_error($error_field, t('You have specified an invalid media URL or embed code.'));
302 }
303 return $item;
304 }
305
306 /**
307 * replace embedded code with the extracted id. this goes in the field 'value'
308 * also allows you to grab directly from the URL to display the content from field 'provider'
309 */
310 function _emfield_field_submit_id($field, $item, $module) {
311 // $module = $field['widget']['helper_module'];
312 $item = array_merge($item, emfield_parse_embed($field, $item['embed'], $module));
313 $item['data'] = (array)emfield_include_invoke($module, $item['provider'], 'data', $field, $item);
314 return $item;
315 }
316
317 function emfield_emfield_field_formatter($field, $item, $formatter, $node, $module, $options = array()) {
318 // if we're coming from a preview, we need to extract our new embedded value...
319 if ($node->in_preview) {
320 $item = emfield_parse_embed($field, $item['embed'], $module);
321 }
322
323 // if we have no value, then return an empty string
324 if (!isset($item['value'])) {
325 return '';
326 }
327
328 // unfortunately, when we come from a view, we don't get all the widget fields
329 if (!$node->type) {
330 $type = content_types($field['type_name']);
331 $field['widget'] = $type['fields'][$field['field_name']]['widget'];
332 }
333
334 // and sometimes our data is still unserialized, again from views
335 if (!is_array($item['data'])) {
336 $item['data'] = (array)unserialize($item['data']);
337 }
338
339 // $module = $field['widget']['helper_module'];
340
341 $output .= theme($module .'_'. $formatter, $field, $item, $formatter, $node, $options);
342
343 return $output;
344 }
345
346 /** Widgets **/
347 function emfield_emfield_widget_settings($op, $widget, $module) {
348 switch ($op) {
349 case 'form':
350 // make sure to register the new type as supported by this module
351 emfield_implement_types(FALSE);
352
353 $form = array();
354 $options = array();
355 $providers = emfield_system_list($module);
356 foreach ($providers as $provider) {
357 if (variable_get('emfield_allow_'. $module .'_'. $provider->name, TRUE)) {
358 $info = emfield_include_invoke($module, $provider->name, 'info');
359 $options[$provider->name] = $info['name'];
360 }
361 }
362 $form['provider_list'] = array(
363 '#type' => 'fieldset',
364 '#title' => t('Providers Supported'),
365 '#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.'),
366 '#collapsible' => TRUE,
367 '#collapsed' => FALSE,
368 );
369 $form['provider_list']['providers'] = array(
370 '#type' => 'checkboxes',
371 '#title' => t('Providers'),
372 '#default_value' => $widget['providers'] ? $widget['providers'] : array(),
373 '#options' => $options,
374 );
375
376 foreach (module_implements('emfield_widget_settings_extra') as $module) {
377 $form[$module] = module_invoke($module, 'emfield_widget_settings_extra', 'form', $widget);
378 }
379
380 return $form;
381
382 case 'save':
383 $columns = array('providers'); //, 'helper_module', );
384 foreach (module_implements('emfield_widget_settings_extra') as $module) {
385 $columns = array_merge($columns, module_invoke($module, 'emfield_widget_settings_extra', 'save', $widget));
386 }
387 return $columns;
388 }
389 }
390
391 function emfield_emfield_widget($op, &$node, $field, &$node_field, $module) {
392 // $module = $field['widget']['helper_module'];
393 switch ($op) {
394 case 'prepare form values':
395 // Don't save empty fields except the first value
396 foreach ($node_field as $delta => $item) {
397 if ($item['value'] == '' && $delta > 0) {
398 unset($node_field[$delta]);
399 }
400 }//print 'nodefield:' ; print_r($node_field);
401 foreach (module_implements('emfield_widget_extra') as $module) {
402 $args = array($op, &$node, $field, &$node_field, $module);
403 call_user_func_array($module .'_emfield_widget_extra', $args);
404 }
405 break;
406 case 'form':
407 $form = array();
408
409 $form[$field['field_name']] = array('#tree' => TRUE);
410 $textfield = 'embed';
411 $providers = emfield_allowed_providers($field, $module);
412 $urls = array();
413 $additional_form_elements = array();
414 foreach ($providers as $provider) {
415 // don't check providers not allowed
416 if (variable_get('emfield_allow_'. $module .'_'. $provider->name, TRUE)) {
417 $info = emfield_include_invoke($module, $provider->name, 'info');
418 $urls[] = l($info['name'], $info['url'], array('target' => '_blank'));
419 $additional_element = emfield_include_invoke($module, $provider->name, 'form');
420 if ($additional_element) {
421 $additional_form_elements[$provider->name] = $additional_element;
422 }
423 }
424 }
425 $textfield_title = t($field['widget']['label']);
426 if (!(empty($field['widget']['description']))) {
427 $textfield_description = $field['widget']['description'];
428 }
429 else {
430 $textfield_description = t('Enter the URL or Embed Code here. The embedded third party content will be parsed and displayed appropriately from this.');
431 }
432 $textfield_description .= '<br />'. t('The following services are provided: !urls', array('!urls' => implode(', ', $urls)));
433
434 if ($field['multiple']) {
435 $form[$field['field_name']]['#type'] = 'fieldset';
436 $form[$field['field_name']]['#title'] = t($field['widget']['label']);
437 $delta = 0;
438 foreach ($node_field as $data) {
439 if (isset($data[$textfield])) {
440 $form[$field['field_name']][$delta][$textfield] = array(
441 '#type' => 'textfield',
442 '#title' => $textfield_title,
443 '#description' => $textfield_description,
444 '#default_value' => $data[$textfield],
445 '#required' => ($delta == 0) ? $field['required'] : FALSE,
446 '#maxlength' => 2048,
447 );
448 if (!empty($additional_form_elements)) {
449 foreach ($additional_form_elements as $key => $element) {
450 $form[$field['field_name']][$delta][$key] = $element;
451 }
452 }
453 $form[$field['field_name']][$delta]['value'] = array(
454 '#type' => 'value',
455 '#value' => $data['value'],
456 );
457 if ($data['value']) {
458 $info = emfield_include_invoke($module, $data['provider'], 'info');
459 $form[$field['field_name']][$delta]['value_markup'] = array(
460 '#type' => 'item',
461 '#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')))),
462 );
463 }
464 foreach (module_implements('emfield_widget_extra') as $module) {
465 $form[$field['field_name']][$delta][$module] = module_invoke($module, 'emfield_widget_extra', 'form', $node, $field, $data);
466 }
467 $delta++;
468 }
469 }
470 foreach (range($delta, $delta + 2) as $delta) {
471 $form[$field['field_name']][$delta][$textfield] = array(
472 '#type' => 'textfield',
473 '#title' => $textfield_title,
474 '#description' => $textfield_description,
475 '#default_value' => '',
476 '#required' => ($delta == 0) ? $field['required'] : FALSE,
477 '#maxlength' => 2048,
478 );
479 if (!empty($additional_form_elements)) {
480 foreach ($additional_form_elements as $key => $element) {
481 $form[$field['field_name']][$delta][$key] = $element;
482 }
483 }
484 $form[$field['field_name']][$delta]['value'] = array(
485 '#type' => 'value',
486 '#title' => '',
487 );
488 foreach (module_implements('emfield_widget_extra') as $module) {
489 $form[$field['field_name']][$delta][$module] = module_invoke($module, 'emfield_widget_extra', 'form', $node, $field, $data);
490 }
491 }
492 }
493 else {
494 $form[$field['field_name']][0][$textfield] = array(
495 '#type' => 'textfield',
496 '#title' => $textfield_title, //t($field['widget']['label']),
497 '#description' => $textfield_description,
498 '#default_value' => isset($node_field[0][$textfield]) ? $node_field[0][$textfield] : '',
499 '#required' => $field['required'],
500 '#maxlength' => 2048,
501 );
502 if ($textfield == 'embed') {
503 $value = isset($node_field[0]['value']) ? $node_field[0]['value'] : '';
504 $form[$field['field_name']][0]['value'] = array(
505 '#type' => 'value',
506 '#value' => $value,
507 );
508 if (!empty($additional_form_elements)) {
509 foreach ($additional_form_elements as $key => $element) {
510 $form[$field['field_name']][0][$key] = $element;
511 }
512 }
513 if ($value) {
514 $info = emfield_include_invoke($module, $node_field[0]['provider'], 'info');
515 $form[$field['field_name']][0]['value_markup'] = array(
516 '#type' => 'item',
517 '#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')))),
518 );
519 }
520 foreach (module_implements('emfield_widget_extra') as $module) {
521 $form[$field['field_name']][$delta][$module] = module_invoke($module, 'emfield_widget_extra', 'form', $node, $field, $node_field[0]);
522 }
523 }
524 }
525 return $form;
526 default:
527 break;
528 }
529 }
530
531 /**
532 * Implementation of hook_node_operations().
533 */
534 function emfield_node_operations() {
535 $operations = array(
536 'emfield_reload' => array(
537 'label' => t('Reload Embedded Media Data'),
538 'callback' => 'emfield_operations_reload',
539 ),
540 );
541 return $operations;
542 }
543
544 /**
545 * This reloads and saves the data for all the nid's in the array.
546 */
547 function emfield_operations_reload($nids = array()) {
548 foreach ($nids as $nid) {
549 if ($node = node_load($nid)) {
550 $type = content_types($node->type);
551 foreach ($type['fields'] as $field) {
552 if (module_hook($field['type'], 'emfield_info')) {
553 drupal_set_message(t("Reloading %node-title's %field.", array('%node-title' => $node->title, '%field' => $field['type_name'])));
554 $items = $node->{$field['field_name']};
555 emfield_emfield_field('submit', $node, $field, $items, FALSE, FALSE, $field['type']);
556 $node->{$field['field_name']} = $items;
557 node_save($node);
558 }
559 }
560 }
561 }
562 }
563
564 /**
565 * Implementation of hook_nodeapi().
566 */
567 function emfield_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
568 switch ($op) {
569 case 'rss item':
570 $files = array();
571 $type = content_types($node->type);
572 foreach ($type['fields'] as $field) {
573 if (module_hook($field['type'], 'emfield_info')) {
574 $items = (array)$node->{$field['field_name']};
575 $rss_encl = module_invoke($field['type'], 'emfield_rss', $node, $items, $teaser);
576 // about here let extras change the data
577 // allow modules (such as emthumb, or a future emmetadata) to alter our data.
578 foreach (module_implements('emfield_field_extra') as $module) {
579 $args = array($op, &$node, $field, &$items, $teaser, $page, $module);
580 $ret = call_user_func_array($module .'_emfield_field_extra', $args);
581 if (is_array($rss_encl) && is_array($ret)) {
582 while (list($delta, ) = each($rss_encl)) {
583 if (is_array($ret[$delta]) && is_array($rss_encl[$delta])) {
584 $rss_encl[$delta] = $ret[$delta] + $rss_encl[$delta];
585 }
586 }
587 }
588 }
589 if (is_array($rss_encl)) {
590 // by now the delta doesn't matter we have enough data in the individual arrays
591 $files = array_merge($files, $rss_encl);
592 }
593 }
594 }
595
596 $enclosure = array();
597 $rss = array();
598 foreach ($files as $file) {
599 // RRS2 enclosure http://cyber.law.harvard.edu/rss/rss.html#ltenclosuregtSubelementOfLtitemgt
600 if (
601 count($enclosure) < 1 && // only one <enclosure> per <item>
602 isset($file['filepath']) && isset($file['filesize']) && isset($file['filemime']) &&
603 $file['filepath'] != '' && $file['filesize'] > 0 && $file['filemime'] != ''
604 ) {
605 $enclosure = array(
606 'key' => 'enclosure',
607 'attributes' => array(
608 'url' => check_url($file['filepath']),
609 'length' => (int) $file['filesize'],
610 'type' => check_plain($file['filemime']),
611 )
612 );
613 }
614
615 // MRSS media:content http://search.yahoo.com/mrss
616 $media = array();
617 if (isset($file['filepath']) && $file['filepath'] != '') {
618 // actually optional if media:player were to be specified
619 $media['url'] = check_url($file['filepath']);
620 // the rest of these are optional
621 if (isset($file['filesize']) && $file['filesize'] > 1) {
622 $media['fileSize'] = (int) $file['filesize'];
623 }
624 if ($file['filemime']) {
625 $media['type'] = check_plain($file['filemime']);
626 }
627 if (isset($file['medium']) && $file['medium'] != '') {
628 $media['medium'] = check_plain($file['medium']);
629 }
630 // media:isDefault, may be good for multiple CCK fields - ignored for now
631 if (isset($file['expression']) && $file['expression'] != '') {
632 $media['expression'] = check_plain($file['expression']);
633 }
634 if (isset($file['bitrate']) && $file['bitrate'] > 0) {
635 $media['bitrate'] = (int) $file['bitrate'];
636 }
637 if (isset($file['framerate']) && $file['framerate'] > 0) {
638 $media['framerate'] = (int) $file['framerate'];
639 }
640 if (isset($file['sampling_rate']) && $file['sampling_rate'] > 0) {
641 $media['samplingrate'] = (int) $file['sampling_rate'];
642 }
643 if (isset($file['channels']) && $file['channels'] > 0) {
644 $media['channels'] = check_plain($file['channels']);
645 }
646 if (isset($file['duration']) && $file['duration'] > 0) {
647 $media['duration'] = (int) $file['duration'];
648 }
649 if (
650 isset($file['width']) && isset($file['height']) &&
651 $file['width'] > 0 && $file['height'] > 0
652 ) {
653 $media['width'] = (int) $file['width'];
654 $media['height'] = (int) $file['height'];
655 }
656 // media:lang will be interesting
657
658 $mrss_thumbnail = array();
659 if (is_array($file['thumbnail']) && $file['thumbnail']['filepath'] != '') {
660 $thumbnail = array();
661 $thumbnail['url'] = check_url($file['thumbnail']['filepath']);
662 if (isset($file['thumbnail']['height']) && $file['thumbnail']['height'] > 0) {
663 $thumbnail['height'] = (int) $file['thumbnail']['height'];
664 }
665 if (isset($file['thumbnail']['width']) && $file['thumbnail']['width'] > 0) {
666 $thumbnail['width'] = (int) $file['thumbnail']['width'];
667 }
668 if (isset($file['thumbnail']['time']) && $file['thumbnail']['time'] > 0) {
669 $thumbnail['time'] = (int) $file['thumbnail']['time'];
670 }
671 $mrss_thumbnail = array('key' => 'media:thumbnail', 'attributes' => $thumbnail);
672 }
673 $rss[] = array('key' => 'media:content', 'attributes' => $media, 'value' => array($mrss_thumbnail));
674 }
675 }
676
677 // work around for http://drupal.org/node/157709
678 static $been_here = FALSE;
679 if (! $been_here) {
680 $rss[] = array('namespace' => array('xmlns:media="http://search.yahoo.com/mrss/"'));
681 $been_here = TRUE;
682 }
683
684 $rss[] = $enclosure;
685
686 return $rss;
687 }
688 }
689
690 /**
691 * When an include file requires to read an xml to receive information, such as for thumbnails,
692 * this script can be used to request the xml and return it as an array.
693 * i suspect it could be done easier (and more quickly) in php 5.
694 * @param $provider
695 * the string of the third party provider, such as 'youtube', 'flikr', or 'google'
696 * @param $url
697 * the url for the xml request
698 * @param $args
699 * an array of args to pass to the xml url
700 * @param $cached
701 * optional; if TRUE, the result of this xml request will be cached. good to play nice w/
702 * the third party folks so they don't stop providing service to your site...
703 * @return
704 * the xml results returned as an array
705 */
706 function emfield_request_xml($provider, $url, $args = array(), $cached = TRUE, $return_errors = FALSE, $hash_extra = FALSE, $serialized = FALSE) {
707 ksort($args);
708
709 // build an argument hash that we'll use for the cache id and api signing
710 $arghash = $provider .':';
711 foreach ($args as $k => $v) {
712 $arghash .= $k . $v;
713 }
714
715 // build the url
716 foreach ( $args as $k => $v) {
717 $encoded_params[] = urlencode($k) .'='. urlencode($v);
718 }
719 if (!empty($encoded_params)) {
720 $url .= '?'. implode('&', $encoded_params);
721 }
722
723 // some providers, such as bliptv, actually change the url, and not just the queries.
724 // we provide an extra section for a unique identifier in that case
725 if (isset($hash_extra)) {
726 $arghash .= ':'. $hash_extra;
727 }
728
729 // if it's a cachable request, try to load a cached value
730 if ($cached && $cache = cache_get($arghash, 'cache')) {
731 return unserialize($cache->data);
732 }
733
734 // connect and fetch a value
735 $result = drupal_http_request($url);
736
737 if ($result->code != 200) {
738 if ($return_errors) {
739 return array(
740 'stat' => 'error',
741 'code' => $result->code,
742 'message' => 'HTTP Error: '. $result->error,
743 );
744 }
745 emfield_set_error(t("Could not connect to @provider, HTTP error @error", array('@error' => $result->code, '@provider' => $provider)));
746 return array();
747 }
748
749 if ($serialized) {
750 // Flickr gives us a serialized php array. Make sure it unserializes.
751 $response = unserialize($result->data);
752 if (!$response) {
753 if ($return_errors) {
754 return array(
755 'stat' => 'error',
756 'code' => '-1',
757 'message' => 'The response was corrupted, it could not be unserialized.',
758 );
759 }
760 emfield_set_error(t("The response from @provider was corrupted and could not be unserialized.", array('@provider' => $provider)));
761 return array();
762 }
763 }
764 else {
765 $parser = drupal_xml_parser_create($result->data);
766 $vals = array();
767 $index = array();
768 xml_parse_into_struct($parser, $result->data, $vals, $index);
769 xml_parser_free($parser);
770
771 $response = array();
772 $response['_emfield_arghash'] = $arghash;
773 $level = array();
774 $start_level = 1;
775 foreach ($vals as $xml_elem) {
776 if ($xml_elem['type'] == 'open') {
777 if (array_key_exists('attributes', $xml_elem)) {
778 list($level[$xml_elem['level']], $extra) = array_values($xml_elem['attributes']);
779 }
780 else {
781 $level[$xml_elem['level']] = $xml_elem['tag'];
782 }
783 }
784 if ($xml_elem['type'] == 'complete') {
785 $php_stmt = '$response';
786 while ($start_level < $xml_elem['level']) {
787 $php_stmt .= '[$level['. $start_level .']]';
788 $start_level++;
789 }
790 $php_stmt .= '[$xml_elem[\'tag\']][] = $xml_elem[\'value\'];'. $php_stmt .'[$xml_elem[\'tag\']][] = $xml_elem[\'attributes\'];';
791 eval($php_stmt);
792 $start_level--;
793 }
794 }
795 }
796
797 cache_set($arghash, 'cache', serialize($response), time() + variable_get('emfield_cache_duration', 3600));
798 return $response;
799 }
800
801 /**
802 * Get the HTTP Header of media, for mime-type and length
803 *
804 * @param $provider
805 * the string of the third party provider, such as 'youtube', 'flikr', or 'google'
806 * @param $url
807 * the url for the media
808 * @param $cached
809 * optional; if TRUE, the result of this xml request will be cached. good to play nice w/
810 * the third party folks so they don't stop providing service to your site...
811 * @return
812 * the header results returned as an array
813 */
814 function emfield_request_header($provider, $url, $cached = TRUE, $return_errors = TRUE) {
815 // if it's cacheable, try to load a cached value
816 if ($cached && $cache = cache_get($url, 'cache')) {
817 return unserialize($cache->data);
818 }
819
820 $result = _emfield_http_request_header($url);
821
822 if ($result->code != 200) {
823 if ($return_errors) {
824 return $result;
825 }
826
827 emfield_set_error(t("Could not connect to @provider, HTTP error @error", array('@error' => $result->code, '@provider' => $provider)));
828 return array();
829 }
830
831 // @todo does this not want to be changing 'cache' to 'cache_emfield' or similar
832 cache_set($url, 'cache', serialize($result), time() + variable_get('emfield_cache_duration', 3600));
833 return $result;
834 }
835
836 /**
837 * HTTP HEAD - just request the header.
838 */
839 function _emfield_http_request_header($url, $retry = 4) {
840 $result = drupal_http_request($url, array(), 'HEAD', NULL, 0);
841
842 switch ($result->code) {
843 // the intention here is to retry if the correct information isn't available
844 // so far it's just tuned for YouTube
845 // it's possible/probable that Moved Temporarily will give the headers required elsewhere
846 // and it maybe best to test on the content of the header
847 case 200: // OK
848 case 304: // Not modified - this shouldn't happen here
849 break;
850 case 301: // Moved permanently
851 case 302: // Moved temporarily
852 case 303: // See Other <-- drupal_http_request doesn't deal with this we need this for youtube
853 case 307: // Moved temporarily
854 $location = $result->headers['Location'];
855 if ($retry) {
856 $result = _emfield_http_request_header($result->headers['Location'], --$retry);
857 $result->redirect_code = $result->code;
858 }
859 $result->redirect_url = $location;
860
861 break;
862 default:
863 }
864
865 return $result;
866 }
867
868 function emfield_set_error($error) {
869 watchdog('emfield', $error, WATCHDOG_WARNING);
870 }
871
872 /**
873 * Return an array of installed .inc files and/or loads them upon request.
874 * This routine is modeled after drupal_system_listing() (and also depends on it).
875 * It's major difference, however, is that it loads .inc files by default.
876 *
877 * @param $provider
878 * Optional; name of the passed $provider to find (e.g. "youtube", "google", etc.).
879 * @param $load
880 * Defaults to TRUE; whether to include matching files into memory.
881 * @return
882 * An array of file objects optionally matching $provider.
883 */
884 function emfield_system_list($module, $provider = NULL, $load = TRUE) {
885 $override_files = module_invoke_all('emfield_providers', $module, $provider);
886 $files = drupal_system_listing("$provider\.inc", drupal_get_path('module', $module) ."/providers", 'name', 0);
887 $files = array_merge($files, $override_files);
888
889 ksort($files);
890
891 if ($load) {
892 foreach ($files as $file) {
893 emfield_include_list($file);
894 }
895 }
896
897 return $files;
898 }
899
900 /**
901 * Maintains a list of all loaded include files.
902 *
903 * @param $file
904 * Optional; a file object (from emfield_system_list()) to be included.
905 * @return
906 * An array of all loaded includes (without the .inc extension).
907 */
908 function emfield_include_list($file = NULL) {
909 static $list = array();
910
911 if ($file && !isset($list[$file->filename])) {
912 include_once('./'. $file->filename);
913 $list[$file->filename] = $file->name;
914 }
915
916 return $list;
917 }
918
919 /**
920 * Determine whether an include implements a hook, cf. module_hook.
921 *
922 * @param $provider
923 * The name of the provider file (without the .inc extension), such as 'youtube' or 'google'.
924 * @param $hook
925 * The name of the hook (e.g. "thumbnail", "settings", etc.).
926 * @return
927 * TRUE if the provider is loaded and the hook is implemented.
928 */
929 function emfield_include_hook($module, $provider, $hook) {
930 return function_exists($module .'_'. $provider .'_'. $hook);
931 }
932
933 /**
934 * Invoke hook in a particular include.
935 *
936 * @param $module
937 * the helper module
938 * @param $provider
939 * The name of the provider (without the .inc extension).
940 * @param $hook
941 * The name of the hook (e.g. "settings", "thumbnail", etc.).
942 * @param ...
943 * Arguments to pass to the hook implementation.
944 * @return
945 * The return value of the hook implementation.
946 */
947 function emfield_include_invoke() {
948 $args = func_get_args();
949 $module = array_shift($args);
950 $provider = array_shift($args);
951 $hook = array_shift($args);
952 $function = $module .'_'. $provider .'_'. $hook;
953 emfield_system_list($module, $provider);
954 return emfield_include_hook($module, $provider, $hook) ? call_user_func_array($function, $args) : NULL;
955 }
956
957 /**
958 * Custom filter for provider NOT NULL
959 */
960 function emfield_views_handler_filter_is_not_null($op, $filter, $filterinfo, &$query) {
961 if ($op == 'handler') {
962 $query->ensure_table($filterinfo['table']);
963 if ($filter['value']) {
964 $qs = "%s.%s <> '' AND %s.%s IS NOT NULL";
965 }
966 else {
967 $qs = "%s.%s = '' OR %s.%s IS NULL";
968 }
969 $query->add_where($qs, $filterinfo['table'], $filterinfo['content_db_info']['columns']['provider']['column'], $filterinfo['table'], $filterinfo['content_db_info']['columns']['provider']['column']);
970 }
971 }
972
973 /**
974 * Create a list of providers.
975 */
976 function emfield_views_handler_filter_provider_list($op) {
977 $providers = array();
978 foreach (emfield_system_list('video_cck') as $provider => $info) {
979 $providers[$provider] = $info->name;
980 }
981 return $providers;
982 }
983
984 /**
985 * Views handler for the provider list filter.
986 */
987 function emfield_views_handler_filter_provider($op, $filter, $filterinfo, &$query) {
988 if ($op == 'handler') {
989 $query->ensure_table($filterinfo['table']);
990 if ($filter['operator'] == 'OR') {
991 foreach ($filter['value'] as $provider) {
992 $items[] = "%s.%s = '$provider'";
993 $where[] = $filterinfo['table'];
994 $where[] = $filterinfo['content_db_info']['columns']['provider']['column'];
995 }
996 $qs = implode(' OR ', $items);
997 }
998 else {
999 foreach ($filter['value'] as $provider) {
1000 $items[] = "%s.%s <> '$provider'";
1001 $where[] = $filterinfo['table'];
1002 $where[] = $filterinfo['content_db_info']['columns']['provider']['column'];
1003 }
1004 $qs = implode(' AND ', $items);
1005 }
1006 $query->add_where($qs, $where);
1007 }
1008 }
1009
1010 /**
1011 * Handle the provider argument. This is called from a wrapper that includes the module.
1012 */
1013 function _emfield_handler_arg_provider($op, &$query, $argtype, $arg = '', $module = '') {
1014 if ($op == 'filter') {
1015 $field_name = substr($argtype['type'], 10);