media_internet.module

Tracking 7.x-2.x branch
  1. drupal
    1. 7 contributions/media/modules/media_internet/media_internet.module

Classes

NameDescription
MediaInternetBaseHandler
MediaInternetFileHandler
MediaInternetNoHandlerException
MediaInternetValidationException

Functions & methods

NameDescription
media_internet_accessAccess callback for the media_internet media browser plugin.
media_internet_addProvides a form for adding media items from 3rd party sources.
media_internet_add_submitUpload a file from a URL.
media_internet_add_validateAllow stream wrappers to have their chance at validation.
media_internet_get_providerFinds the appropriate provider for a given URL or embed_string
media_internet_get_providersGets the list of providers.
media_internet_hook_infoImplements hook_hook_info().
media_internet_menuImplements hook_menu().
media_internet_permissionImplement hook_permission().

File

View source
  1. <?php
  2. /**
  3. * Implements hook_hook_info().
  4. */
  5. function media_internet_hook_info() {
  6. $hooks = array(
  7. 'media_internet_providers',
  8. );
  9. return array_fill_keys($hooks, array('group' => 'media'));
  10. }
  11. /**
  12. * Implements hook_menu().
  13. */
  14. function media_internet_menu() {
  15. $items['file/add/web'] = array(
  16. 'title' => 'Web',
  17. 'description' => 'Add internet files to your media library.',
  18. 'page callback' => 'drupal_get_form',
  19. 'page arguments' => array('media_internet_add'),
  20. 'access callback' => 'media_internet_access',
  21. 'type' => MENU_LOCAL_TASK,
  22. );
  23. return $items;
  24. }
  25. /**
  26. * Access callback for the media_internet media browser plugin.
  27. */
  28. function media_internet_access($account = NULL) {
  29. return user_access('administer files', $account) || user_access('add media from remote sources', $account);
  30. }
  31. /**
  32. * Implement hook_permission().
  33. */
  34. function media_internet_permission() {
  35. return array(
  36. 'add media from remote sources' => array(
  37. 'title' => t('Add media from remote services'),
  38. 'description' => t('Add media from remote sources such as other websites, YouTube, etc'),
  39. ),
  40. );
  41. }
  42. /**
  43. * Provides a form for adding media items from 3rd party sources.
  44. *
  45. * @todo Convert the form arguments to just one array of options/parameters.
  46. */
  47. function media_internet_add($form, &$form_state = array(), $types = NULL) {
  48. $providers = array();
  49. foreach (media_internet_get_providers() as $key => $provider) {
  50. if (empty($provider['hidden']) || $provider['hidden'] != TRUE) {
  51. // @todo Convert this to show provider images in a nice format.
  52. $class = drupal_clean_css_identifier(drupal_strtolower($provider['title']));
  53. $providers[] = array(
  54. 'data' => check_plain($provider['title']),
  55. 'class' => array($class),
  56. );
  57. }
  58. }
  59. $form['embed_code'] = array(
  60. '#type' => 'textfield',
  61. '#title' => t('Enter a URL to an image'),
  62. '#description' => t('Enter a URL to an image on the web.'),
  63. '#attributes' => array('class' => array('media-add-from-url')),
  64. // There is no standard specifying a maximum length for a URL. Internet
  65. // Explorer supports upto 2083 (http://support.microsoft.com/kb/208427), so
  66. // we assume publicly available media URLs are within this limit.
  67. '#maxlength' => 2083,
  68. '#required' => TRUE,
  69. );
  70. // If any providers are enabled it is assumed that some kind of embed is supported
  71. if ($providers) {
  72. $form['embed_code']['#title'] = t('Enter an image URL or an embed code');
  73. $form['embed_code']['#description'] = t('Enter a URL or the embed code from a media provider.');
  74. $form['providers'] = array(
  75. '#theme' => 'item_list',
  76. '#title' => t('Supported providers'),
  77. '#items' => $providers,
  78. '#attributes' => array(
  79. 'class' => array('media-internet-providers'),
  80. ),
  81. );
  82. }
  83. $form['#validators'] = array();
  84. if ($types) {
  85. $form['#validators']['media_file_validate_types'] = array($types);
  86. }
  87. $form['actions'] = array('#type' => 'actions');
  88. $form['actions']['submit'] = array(
  89. '#type' => 'submit',
  90. '#value' => t('Submit'),
  91. );
  92. return $form;
  93. }
  94. /**
  95. * Allow stream wrappers to have their chance at validation.
  96. *
  97. * Any module that implements hook_media_parse will have an
  98. * opportunity to validate this.
  99. *
  100. * @see media_parse_to_uri()
  101. */
  102. function media_internet_add_validate($form, &$form_state) {
  103. // Supporting providers can now claim this input. It might be a URL, but it
  104. // might be an embed code as well.
  105. $embed_code = $form_state['values']['embed_code'];
  106. try {
  107. $provider = media_internet_get_provider($embed_code);
  108. $provider->validate();
  109. } catch (MediaInternetNoHandlerException $e) {
  110. form_set_error('embed_code', $e->getMessage());
  111. return;
  112. } catch (MediaInternetValidationException $e) {
  113. form_set_error('embed_code', $e->getMessage());
  114. return;
  115. }
  116. $validators = $form['#validators'];
  117. $file = $provider->getFileObject();
  118. if ($validators) {
  119. try {
  120. $file = $provider->getFileObject();
  121. } catch (Exception $e) {
  122. form_set_error('embed_code', $e->getMessage());
  123. return;
  124. }
  125. // Check for errors. @see media_add_upload_validate calls file_save_upload().
  126. // this code is ripped from file_save_upload because we just want the validation part.
  127. // Call the validation functions specified by this function's caller.
  128. $errors = file_validate($file, $validators);
  129. if (!empty($errors)) {
  130. $message = t('%url could not be added.', array('%url' => $embed_code));
  131. if (count($errors) > 1) {
  132. $message .= theme('item_list', array('items' => $errors));
  133. }
  134. else {
  135. $message .= ' ' . array_pop($errors);
  136. }
  137. form_set_error('embed_code', $message);
  138. return FALSE;
  139. }
  140. }
  141. // @TODO: Validate that if we have no $uri that this is a valid file to
  142. // save. For instance, we may only be interested in images, and it would
  143. // be helpful to let the user know they passed the HTML page containing
  144. // the image accidentally. That would also save us from saving the file
  145. // in the submit step.
  146. // This is kinda a hack of the same.
  147. // This should use the file_validate routines that the upload form users.
  148. // We need to fix the media_parse_to_file routine to allow for a validation.
  149. }
  150. /**
  151. * Upload a file from a URL.
  152. *
  153. * This will copy a file from a remote location and store it locally.
  154. *
  155. * @see media_parse_to_uri()
  156. * @see media_parse_to_file()
  157. */
  158. function media_internet_add_submit($form, &$form_state) {
  159. $embed_code = $form_state['values']['embed_code'];
  160. try {
  161. // Save the remote file
  162. $provider = media_internet_get_provider($embed_code);
  163. // Providers decide if they need to save locally or somewhere else.
  164. // This method returns a file object
  165. $file = $provider->save();
  166. }
  167. catch (Exception $e) {
  168. form_set_error('embed_code', $e->getMessage());
  169. return;
  170. }
  171. if (!$file->fid) {
  172. form_set_error('embed_code', t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $embed_code)));
  173. return;
  174. }
  175. else {
  176. $form_state['file'] = $file;
  177. }
  178. // Redirect to the file edit page after submission.
  179. if (file_entity_access('update', $file)) {
  180. $destination = array('destination' => 'admin/content/file');
  181. if (isset($_GET['destination'])) {
  182. $destination = drupal_get_destination();
  183. unset($_GET['destination']);
  184. }
  185. $form_state['redirect'] = array('file/' . $file->fid . '/edit', array('query' => $destination));
  186. }
  187. else {
  188. $form_state['redirect'] = 'admin/content/file';
  189. }
  190. }
  191. /**
  192. * Gets the list of providers.
  193. *
  194. * A "Provider" is a bit of meta-data like a title and a logo and a class which
  195. * can handle saving remote files. Each provider is able to parse an embed code or URL
  196. * and store it as a file object in file_managed.
  197. */
  198. function media_internet_get_providers() {
  199. $providers = &drupal_static(__FUNCTION__);
  200. if (!isset($providers)) {
  201. $cid = 'media:internet:providers';
  202. if ($cache = cache_get($cid)) {
  203. $providers = $cache->data;
  204. }
  205. else {
  206. $providers = array();
  207. foreach (module_implements('media_internet_providers') as $module) {
  208. foreach (module_invoke($module, 'media_internet_providers') as $key => $provider) {
  209. // Store the module here too for convinience.
  210. $providers[$key] = $provider;
  211. $providers[$key]['module'] = $module;
  212. if (!isset($providers[$key]['weight'])) {
  213. $providers[$key]['weight'] = 0;
  214. }
  215. }
  216. }
  217. uasort($providers, 'drupal_sort_weight');
  218. cache_set($cid, $providers);
  219. }
  220. }
  221. return $providers;
  222. }
  223. /**
  224. * Finds the appropriate provider for a given URL or embed_string
  225. *
  226. * Each provider has a claim() method which it uses to tell media_internet
  227. * that it should handle this input. We cycle through all providers to find
  228. * the right one.
  229. *
  230. * @todo: Make this into a normal hook or something because we have to instantiate
  231. * each class to test and that's not right.
  232. */
  233. function media_internet_get_provider($embed_string) {
  234. foreach (media_internet_get_providers() as $class_name => $nothing) {
  235. $p = new $class_name($embed_string);
  236. if ($p->claim($embed_string)) {
  237. return $p;
  238. }
  239. }
  240. throw new MediaInternetNoHandlerException(t('Unable to handle the provided embed string or URL.'));
  241. }
  242. class MediaInternetFileHandler extends MediaInternetBaseHandler {
  243. public $fileObject;
  244. public function preSave(&$file_obj) {
  245. // Coppies the remote file locally.
  246. $remote_uri = $file_obj->uri;
  247. //@TODO: we should follow redirection here an save the final filename, not just the basename.
  248. $local_filename = basename($remote_uri);
  249. $local_filename = file_munge_filename($local_filename, media_variable_get('file_extensions'), FALSE);
  250. $local_uri = file_stream_wrapper_uri_normalize('temporary://' . $local_filename);
  251. if (!@copy($remote_uri, $local_uri)) {
  252. throw new Exception('Unable to add file ' . $remote_uri);
  253. return;
  254. }
  255. // Make the current fileObject point to the local_uri, not the remote one.
  256. $file_obj = file_uri_to_object($local_uri);
  257. }
  258. public function postSave(&$file_obj) {
  259. $scheme = variable_get('file_default_scheme', 'public') . '://';
  260. $uri = file_stream_wrapper_uri_normalize($scheme . $file_obj->filename);
  261. // Now to its new home.
  262. $file_obj = file_move($file_obj, $uri, FILE_EXISTS_RENAME);
  263. }
  264. public function getFileObject() {
  265. if (!$this->fileObject) {
  266. $this->fileObject = file_uri_to_object($this->embedCode);
  267. }
  268. return $this->fileObject;
  269. }
  270. public function claim($embedCode) {
  271. // Claim only valid URLs using a supported scheme.
  272. if (!valid_url($embedCode, TRUE) || !in_array(file_uri_scheme($embedCode), media_variable_get('fromurl_supported_schemes'))) {
  273. return FALSE;
  274. }
  275. // This handler is intended for regular files, so don't claim URLs
  276. // containing query strings or fragments.
  277. if (preg_match('/[\?\#]/', $embedCode)) {
  278. return FALSE;
  279. }
  280. // Since this handler copies the remote file to the local web server, do not
  281. // claim a URL with an extension disallowed for media uploads.
  282. $regex = '/\.(' . preg_replace('/ +/', '|', preg_quote(media_variable_get('file_extensions'))) . ')$/i';
  283. if (!preg_match($regex, basename($embedCode))) {
  284. return FALSE;
  285. }
  286. return TRUE;
  287. }
  288. }
  289. abstract class MediaInternetBaseHandler {
  290. public function __construct($embedCode) {
  291. $this->embedCode = $embedCode;
  292. }
  293. /**
  294. * If required, implementors can validate the embedCode.
  295. */
  296. public function validate() {}
  297. /**
  298. * Returns a file object which can be used for validation
  299. *
  300. * @return StdClass
  301. */
  302. abstract public function getFileObject();
  303. /**
  304. * Saves a file to the file_managed table (with file_save)
  305. *
  306. * @return StdClass
  307. */
  308. public function save() {
  309. $file_obj = $this->getFileObject();
  310. $this->preSave($file_obj);
  311. file_save($file_obj);
  312. $this->postSave($file_obj);
  313. return $file_obj;
  314. }
  315. /**
  316. * After the file has been saved, implementors may do additional operations.
  317. *
  318. * @param $file_obj;
  319. */
  320. public function postSave(&$file_obj) {
  321. }
  322. /**
  323. * Before the file has been saved, implementors may do additional operations.
  324. */
  325. public function preSave(&$file_obj) {
  326. }
  327. /**
  328. * Recognize if this handler should take the the item with the embed
  329. * coded passed as argument.
  330. *
  331. * @param string $embed_code
  332. *
  333. * @return boolean
  334. * Whether or not this handler is resposible for the give embed code.
  335. */
  336. abstract public function claim($embed_code);
  337. }
  338. class MediaInternetValidationException extends Exception {
  339. }
  340. class MediaInternetNoHandlerException extends Exception {
  341. }