voxpelli / drupal-oembed

An oEmbed client and provider for Drupal

drupal-oembed / oembedcore.module
100644 425 lines (389 sloc) 12.477 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
<?php
// $Id$
 
/**
 * @file
 * Core functionality for oEmbed
 */
 
/**
 * Implementation of hook_perm().
 */
function oembedcore_perm() {
  return array('administer oembed presets');
}
 
/**
 * Implementation of hook_menu().
 */
function oembedcore_menu() {
  $items = array();
 
  $base = array(
    'access arguments' => array('administer oembed presets'),
    'file' => 'oembedcore.admin.inc',
  );
 
  $items['admin/build/oembed'] = array(
    'title' => 'OEmbed',
    'description' => 'Add, edit and remove oembed preset from the system.',
    'page callback' => 'oembedcore_list_preset',
  ) + $base;
  $items['admin/build/oembed/list'] = array(
    'title' => 'List',
    'page callback' => 'oembedcore_list_preset',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  ) + $base;
  $items['admin/build/oembed/add'] = array(
    'title' => 'Add preset',
    'page callback' => 'oembedcore_add_preset',
    'type' => MENU_LOCAL_TASK,
  ) + $base;
  $items['admin/build/oembed/%oembedcore_preset/edit'] = array(
    'title' => 'Edit preset',
    'page callback' => 'oembedcore_edit_preset',
    'page arguments' => array(3),
    'type' => MENU_LOCAL_TASK,
  ) + $base;
  $items['admin/build/oembed/%oembedcore_preset/export'] = array(
    'title' => 'Export preset',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('oembedcore_export_preset', 3),
    'type' => MENU_LOCAL_TASK,
  ) + $base;
  $items['admin/build/oembed/%oembedcore_preset/delete'] = array(
    'title' => 'Delete preset',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('oembedcore_delete_confirm_preset', 3),
    'type' => MENU_CALLBACK,
  ) + $base;
  $items['admin/build/oembed/%oembedcore_preset/disable'] = array(
    'page callback' => 'oembedcore_disable_preset',
    'page arguments' => array(3),
    'type' => MENU_CALLBACK,
  ) + $base;
  $items['admin/build/oembed/%oembedcore_preset/enable'] = array(
    'page callback' => 'oembedcore_enable_preset',
    'page arguments' => array(3),
    'type' => MENU_CALLBACK,
  ) + $base;
 
  return $items;
}
 
/**
 * Implementation of hook_theme().
 */
function oembedcore_theme() {
  $path = drupal_get_path('module', 'oembedcore') . '/theme';
  return array(
    'oembed' => array(
      'template' => 'oembed',
      'file' => 'oembedcore_theme.inc',
      'path' => $path,
      'arguments' => array('embed' => NULL),
    ),
  );
}
 
/**
 * Implementation of hook_oembedproviders().
 */
function oembedcore_oembedprovider() {
  $oembeddable = array(
    'flickr' => array(
      'scheme' => array('http://*.flickr.com/*', 'http://flickr.com/*'),
      'endpoint' => 'http://www.flickr.com/services/oembed/',
    ),
    'viddler' => array(
      'scheme' => 'http://*.viddler.com/*',
      'endpoint' => 'http://lab.viddler.com/services/oembed/',
    ),
    'qik' => array(
      'scheme' => array('http://qik.com/video/*', 'http://qik.com/*'),
      'endpoint' => 'http://qik.com/api/oembed.json',
    ),
    'revision3' => array(
      'scheme' => 'http://*.revision3.com/*',
      'endpoint' => 'http://revision3.com/api/oembed/',
    ),
    'vimeo' => array(
      'scheme' => array('http://www.vimeo.com/*', 'http://www.vimeo.com/groups/*/*'),
      'endpoint' => 'http://www.vimeo.com/api/oembed.json',
    ),
    'youtube' => array(
      'scheme' => 'http://*.youtube.com/watch*',
      'endpoint' => 'http://www.youtube.com/oembed',
    ),
    'oohembed' => array(
      'scheme' => array(
        'http://*.wikipedia.org/wiki/*',
        'http://*.slideshare.net/*',
        'http://*.imdb.com/title/tt*/',
        'http://*.collegehumor.com/video:*',
      ),
      'endpoint' => 'http://oohembed.com/oohembed/',
    ),
  );
 
  $providers = array();
  foreach ($oembeddable as $site) {
    foreach ((array)$site['scheme'] as $scheme) {
      $providers[$scheme] = array(
        'endpoint' => $site['endpoint'],
      );
    }
  }
 
  return $providers;
}
 
/**
 * Returns the provider for a url.
 *
 * @param string $url
 * Teh url to get the provider for.
 * @return mixed
 * A valid callback or FALSE
 */
function oembedcore_get_provider($url, &$matches) {
  $host = _oembedcore_get_host($url);
  if ($host) {
    $providers = oembedcore_providers($host);
    foreach ($providers as $regex => $info) {
      if (preg_match($regex, $url, $matches)) {
        return $info;
      }
    }
  }
  return FALSE;
}
 
/**
 * A utility function to get the base domain from a url.
 *
 * @param string $uri
 * The uri to get the domain form
 * @return string
 * The domain or NULL
 */
function _oembedcore_get_host($uri) {
  $matches = array();
  if (preg_match('/^https?\:\/\/([^\/]+)/', $uri, $matches)) {
    $matches = explode('.', $matches[1]);
    $match_count = count($matches);
    if ($match_count > 1) {
      return $matches[$match_count - 2] . '.' . $matches[$match_count - 1];
    }
    else {
      return $matches[0];
    }
  }
  return NULL;
}
 
/**
 * Returns all the registered providers, or the providers for a specific host.
 *
 * @param string $host
 * Optional. Supply a hostname if you only want the provider patterns for a specific host.
 * @return array
 */
function oembedcore_providers($url_host = NULL) {
  static $providers;
 
  if (!$providers) {
    $cache_key = 'oembedcore:providers';
 
    if (($cache = cache_get($cache_key)) && isset($cache->data)) {
      $providers = $cache->data;
    }
    else {
      $providers = array();
      $modules = module_implements('oembedprovider');
      foreach ($modules as $module) {
        $ps = call_user_func($module . '_oembedprovider');
        foreach ($ps as $pattern => $info) {
          $host = _oembedcore_get_host($pattern);
 
          $regex_pattern = '/' . str_replace('\*', '(.+)', preg_quote($pattern, '/')) . '/';
          $providers[$host][$regex_pattern] = $info;
        }
      }
      drupal_alter('oembedprovider', $providers);
      foreach ($providers as $host => &$patterns) {
        uksort($patterns, '_oembedcore_specificity_compare');
      }
    }
    cache_set($cache_key, $providers);
  }
 
  if ($url_host) {
    return isset($providers[$url_host]) ? $providers[$url_host] : array();
  }
  return $providers;
}
 
/**
 * Helper function that compares the length of match expressions.
 */
function _oembedcore_specificity_compare($a, $b) {
  return strlen($b) - strlen($a);
}
 
/**
 * Fetch data for an embeddable URL.
 *
 * @param string $url
 * An external URL for the content to embed.
 * @param array $attributes
 * An associative array of attributes, with the following keys:
 * - 'maxwidth'
 * The maximum width of the embed, in pixels.
 * - 'maxheight'
 * The maximum height of the embed, in pixels.
 * @return
 * False or an object representing the embeddable data of the URL.
 */
function oembedcore_oembed_data($url, $attributes = array()) {
  $matches = array();
  if ($provider = oembedcore_get_provider($url, $matches)) {
    return oembedcore_oembed_fetch($provider, $url, $matches, $attributes);
  }
  return FALSE;
}
 
function oembedcore_oembed_fetch($provider, $url, $matches, $attributes = array()) {
  global $_oembed_default_attributes;
  if (!empty($_oembed_default_attributes)) {
    $attributes = array_merge($_oembed_default_attributes, $attributes);
  }
  $attributes['url'] = $url;
  $query = http_build_query($attributes, NULL, '&');
 
  $source = isset($provider['callback']) ? $provider['callback'] : $provider['endpoint'];
  $cache_key = 'oembedcore:embed:' . md5($source . $url . $query);
  $cache = cache_get($cache_key);
 
  if ($cache && isset($cache->data)) {
    $embed = $cache->data;
  }
  else {
    if (!empty($provider['callback'])) {
      $embed = (object)call_user_func($provider['callback'], $provider, $url, $matches, $attributes);
    }
    else {
      $fetch_url = $provider['endpoint'] . '?' . $query;
 
      $response = @file_get_contents($fetch_url);
      if (!$response) {
        return NULL;
      }
      $embed = json_decode($response);
      if (!is_object($embed)) {
        $embed = new SimpleXMLElement($response);
        $embed = (object)get_object_vars($embed);
        if (!is_string($embed->title)) {
          $embed->title = '';
        }
      }
    }
 
    $embed->original_url = $url;
 
    $max_age = isset($embed->cache_age) ? intval($embed->cache_age) : empty($provider['callback']) ? 600 : 60;
    cache_set($cache_key, $embed, 'cache', time() + $max_age);
  }
  return $embed;
}
 
/**
 * To be used for HTML in cases where the HTML is cached independent of the theme - like in the case of input filters.
 */
function oembedcore_oembed_html($embed, $url) {
  //TODO: Maybe refactor into something that uses drupal_render()?
  $return = '';
  switch ($embed->type) {
    case 'photo':
      $return = '<span class="oembed">';
        if (!empty($embed->title)) {
          $return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title')));
        }
        $return .= ' ' . l('<img src="' . check_url($embed->url) . '" />', $url, array('html' => TRUE, 'absolute' => TRUE, 'attributes' => array('class' => 'oembed-photo oembed-content')));
      $return .= '</span>';
      break;
    case 'rich':
    case 'video':
      $return = '<div class="oembed">';
        if (!empty($embed->title)) {
          $return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title')));
        }
        $return .= ' <span class="oembed-content oembed-' . ($embed->type == 'video' ? 'video' : 'rich') . '">' . $embed->html . '</span>';
      $return .= '</div>';
      break;
    case 'link':
      $return .= l($embed->title, $url, array('absolute' => TRUE, 'attributes' => array('class' => 'oembed-title oembed-link')));
      break;
    default:
  }
 
  return $return;
}
 
// --------------------------------------------------------------------------
// Preset database info.
 
/**
 * Clear presets cache on admin/build/modules form.
 */
function oembedcore_form_system_modules_alter(&$form, $form_state) {
  //Copied from imagecache - needed because other modules might contain presets which oembedfield uses and cck caches
  //TODO: Extract this and move to oembedfield
  if (module_exists('content')) {
    content_clear_type_cache();
  }
}
 
/**
 * Create a new preset with defaults appropriately set from schema.
 */
function oembedcore_preset_new() {
  ctools_include('export');
  return ctools_export_new_object('oembedcore_preset');
}
 
/**
 * Load a single preset
 */
function oembedcore_preset_load($name) {
  ctools_include('export');
  $result = ctools_export_load_object('oembedcore_preset', 'names', array($name));
  if (isset($result[$name])) {
    return $result[$name];
  }
  else {
    return FALSE;
  }
}
 
/**
 * Load all presets.
 */
function oembedcore_preset_load_all() {
  ctools_include('export');
  return ctools_export_load_object('oembedcore_preset');
}
 
/**
 * Write a preset to the database.
 */
function oembedcore_preset_save(&$preset) {
  $update = (isset($preset->pid)) ? array('pid') : array();
  drupal_write_record('oembedcore_preset', $preset, $update);
  // Clear the content.module cache (refreshes the list of formatters provided by oembedfield.module).
  //TODO: Extract this and move to oembedfield
  if (module_exists('content')) {
    content_clear_type_cache();
    drupal_rebuild_theme_registry();
  }
  return $preset;
}
 
/**
 * Remove a preset.
 */
function oembedcore_preset_delete($preset) {
  db_query("DELETE FROM {oembedcore_preset} WHERE name = '%s' AND pid = %d", $preset->name, $preset->pid);
  // Clear the content.module cache (refreshes the list of formatters provided by oembedfield.module).
  //TODO: Extract this and move to oembedfield
  if (module_exists('content')) {
    content_clear_type_cache();
    drupal_rebuild_theme_registry();
  }
}
 
/**
 * Export a preset
 */
function oembedcore_preset_export($preset, $indent = '') {
  ctools_include('export');
  $output = ctools_export_object('oembedcore_preset', $preset, $indent);
  return $output;
}
 
/**
 * Lists all available presets
 */
function oembedcore_preset_list() {
  $return = array();
  $presets = oembedcore_preset_load_all();
  foreach ($presets as $preset) {
    $return[$preset->name] = $preset->name;
  }
  return $return;
}