views_rss_media.field.inc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <?php
  2. /**
  3. * Implements hook_field_formatter_info().
  4. */
  5. function views_rss_media_field_formatter_info() {
  6. $formatters = array(
  7. 'media_content' => array(
  8. 'label' => t('RSS <media:content> element'),
  9. 'field types' => array('image', 'file', 'video', 'video_embed_field'),
  10. 'settings' => array(
  11. 'image_style' => '',
  12. 'group_multiple_values' => 0,
  13. 'medium' => '',
  14. 'expression' => '',
  15. 'generate_hash' => 0,
  16. 'hash_algo' => 'md5',
  17. ),
  18. ),
  19. 'media_thumbnail' => array(
  20. 'label' => t('RSS <media:thumbnail> element'),
  21. 'field types' => array('image', 'file', 'video'),
  22. 'settings' => array(),
  23. ),
  24. 'media_category' => array(
  25. 'label' => t('RSS <media:category> element'),
  26. 'field types' => array('taxonomy_term_reference'),
  27. 'settings' => array(),
  28. ),
  29. );
  30. return $formatters;
  31. }
  32. /**
  33. * Implements hook_field_formatter_settings_form().
  34. */
  35. function views_rss_media_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  36. $display = $instance['display'][$view_mode];
  37. $settings = $display['settings'];
  38. $element = array();
  39. if ($field['type'] == 'image' || $field['type'] == 'file') {
  40. $image_styles = image_style_options(FALSE);
  41. $element['image_style'] = array(
  42. '#title' => t('Image style'),
  43. '#type' => 'select',
  44. '#default_value' => $settings['image_style'],
  45. '#empty_option' => t('None (original image)'),
  46. '#options' => $image_styles,
  47. );
  48. }
  49. if ($display['type'] == 'media_content') {
  50. $element['group_multiple_values'] = array(
  51. '#title' => t('Group multiple field values in one &lt;media:group&gt; element'),
  52. '#type' => 'checkbox',
  53. '#default_value' => $settings['group_multiple_values'],
  54. '#description' => t('&lt;media:group&gt; element allows grouping of &lt;media:content&gt; elements that are effectively the same content, yet different representations. For instance: the same song recorded in both the WAV and MP3 format. It is an optional element that must only be used for this purpose. !more_link', array(
  55. '!more_link' => l('[?]', 'http://www.rssboard.org/media-rss#media-group', array('attributes' => array('title' => t('Need more information?')))),
  56. )),
  57. );
  58. $element['medium'] = array(
  59. '#title' => t('Medium'),
  60. '#type' => 'select',
  61. '#default_value' => $settings['medium'],
  62. '#empty_option' => t('None (do not display)'),
  63. '#options' => drupal_map_assoc(array('image', 'audio', 'video', 'document', 'executable')),
  64. '#description' => t('<em>medium</em> attribute of &lt;media:content&gt; element is the type of object. While this attribute can at times seem redundant if type is supplied, it is included because it simplifies decision making on the reader side, as well as flushes out any ambiguities between MIME type and object type. It is an optional attribute. !more_link', array(
  65. '!more_link' => l('[?]', 'http://www.rssboard.org/media-rss#media-content', array('attributes' => array('title' => t('Need more information?')))),
  66. )),
  67. );
  68. $element['expression'] = array(
  69. '#title' => t('Expression'),
  70. '#type' => 'select',
  71. '#default_value' => $settings['expression'],
  72. '#empty_option' => t('None (do not display)'),
  73. '#options' => drupal_map_assoc(array('sample', 'full', 'nonstop')),
  74. '#description' => t('<em>expression</em> attribute of &lt;media:content&gt; element determines if the object is a sample or the full version of the object, or even if it is a continuous stream. Default value is "full". It is an optional attribute. !more_link', array(
  75. '!more_link' => l('[?]', 'http://www.rssboard.org/media-rss#media-content', array('attributes' => array('title' => t('Need more information?')))),
  76. )),
  77. );
  78. // hash_file() function used to generate file hashes
  79. // is available only in PHP versions >= 5.1.2.
  80. // @see http://www.php.net/manual/en/function.hash-file.php
  81. // Also, no hash support for Video Embed fields, as these are not
  82. // stored locally, not possible then to calculate hashes for them.
  83. if ($field['type'] != 'video_embed_field' && version_compare(phpversion(), '5.1.2') >= 0) {
  84. $element['generate_hash'] = array(
  85. '#title' => t('Generate hash values for files'),
  86. '#type' => 'checkbox',
  87. '#default_value' => $settings['generate_hash'],
  88. '#description' => t('Enabling this will generate &lt;media:hash&gt; subelement for each &lt;media:content&gt; element. !more_link', array(
  89. '!more_link' => l('[?]', 'http://www.rssboard.org/media-rss#media-hash', array('attributes' => array('title' => t('Need more information?')))),
  90. )),
  91. );
  92. $element['hash_algo'] = array(
  93. '#title' => t('Hashing algorithm'),
  94. '#type' => 'select',
  95. '#default_value' => $settings['hash_algo'],
  96. '#options' => drupal_map_assoc(array('md5', 'sha1')),
  97. '#description' => t('Indicates the algorithm used to create the hash. Will be added as <em>algo</em> attribute of &lt;media:hash&gt; element. !more_link', array(
  98. '!more_link' => l('[?]', 'http://www.rssboard.org/media-rss#media-hash', array('attributes' => array('title' => t('Need more information?')))),
  99. )),
  100. );
  101. // Add #states value for showing/hiding "Hashing algorithm"
  102. // field based on state of "generate_hash" field value.
  103. $name = ($view_mode == '_dummy') ? 'options[settings][generate_hash]' : 'fields[' . $field['field_name'] . '][settings_edit_form][settings][generate_hash]';
  104. $element['hash_algo']['#states']['invisible'][':input[name="' . $name . '"]'] = array('checked' => FALSE);
  105. }
  106. }
  107. return $element;
  108. }
  109. /**
  110. * Implements hook_field_formatter_settings_summary().
  111. */
  112. function views_rss_media_field_formatter_settings_summary($field, $instance, $view_mode) {
  113. $display = $instance['display'][$view_mode];
  114. $settings = $display['settings'];
  115. $summary = array();
  116. if ($field['type'] == 'image' || $field['type'] == 'file') {
  117. $image_styles = image_style_options(FALSE);
  118. // Unset possible 'No defined styles' option.
  119. unset($image_styles['']);
  120. // Styles could be lost because of enabled/disabled modules that defines
  121. // their styles in code.
  122. if (isset($settings['image_style']) && isset($image_styles[$settings['image_style']])) {
  123. $summary[] = t('Image style: @style', array('@style' => $image_styles[$settings['image_style']]));
  124. }
  125. else {
  126. $summary[] = t('Original image');
  127. }
  128. }
  129. if ($display['type'] == 'media_content') {
  130. if (!empty($display['settings']['group_multiple_values'])) {
  131. $summary[] = t('Group multiple values');
  132. }
  133. $value = (!empty($display['settings']['medium'])) ? $display['settings']['medium'] : t('none');
  134. $summary[] = t('Medium: !medium', array('!medium' => $value));
  135. $value = (!empty($display['settings']['expression'])) ? $display['settings']['expression'] : t('none');
  136. $summary[] = t('Expression: !expression', array('!expression' => $value));
  137. if (!empty($display['settings']['generate_hash'])) {
  138. $summary[] = t('Generate hash: !algo', array('!algo' => $display['settings']['hash_algo']));
  139. }
  140. }
  141. return implode('<br />', $summary);
  142. }
  143. /**
  144. * Implements hook_field_formatter_view().
  145. */
  146. function views_rss_media_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  147. $element = array();
  148. foreach ($items as $delta => $item) {
  149. if ($field['type'] == 'video_embed_field') {
  150. $url = $item['video_url'];
  151. $item['video_data'] = unserialize($item['video_data']);
  152. }
  153. // When generating a thumbnail for a video file,
  154. // let's process only thumbnail image details.
  155. if ($display['type'] == 'media_thumbnail' && $item['type'] == 'video') {
  156. $item = (array) $item['thumbnailfile'];
  157. }
  158. if (($display['type'] == 'media_content' || $display['type'] == 'media_thumbnail') && $field['type'] != 'video_embed_field') {
  159. // Inside a view item may contain NULL data. In that case, just return.
  160. if (empty($item['fid'])) {
  161. unset($items[$delta]);
  162. continue;
  163. }
  164. // Default file URL.
  165. $item_uri = $item['uri'];
  166. $url = file_create_url($item_uri);
  167. // For images provide styled image URL if required.
  168. if (!empty($display['settings']['image_style']) && $image_style = image_style_load($display['settings']['image_style'])) {
  169. // Get full image URI based on provided image style.
  170. $url = image_style_url($display['settings']['image_style'], $item_uri);
  171. // Make sure that image style file has already been created.
  172. $path = image_style_path($display['settings']['image_style'], $item_uri);
  173. if (!file_exists(drupal_realpath($path))) {
  174. image_style_create_derivative($image_style, $item_uri, $path);
  175. }
  176. $item_uri = $path;
  177. }
  178. }
  179. if ($display['type'] == 'media_content') {
  180. $rss_element = array(
  181. 'key' => 'media:content',
  182. 'attributes' => array(
  183. 'url' => $url,
  184. ),
  185. );
  186. if (!empty($item['filesize'])) {
  187. $rss_element['attributes']['fileSize'] = $item['filesize'];
  188. }
  189. if (!empty($item['filemime'])) {
  190. $rss_element['attributes']['type'] = $item['filemime'];
  191. }
  192. if (!empty($display['settings']['medium'])) {
  193. $rss_element['attributes']['medium'] = $display['settings']['medium'];
  194. }
  195. if (!empty($display['settings']['expression'])) {
  196. $rss_element['attributes']['expression'] = $display['settings']['expression'];
  197. }
  198. if ($langcode != LANGUAGE_NONE) {
  199. $rss_element['attributes']['lang'] = $langcode;
  200. }
  201. // Additional properties for images.
  202. if ($item['type'] == 'image') {
  203. $media_info = image_load($item_uri);
  204. if (!empty($media_info)) {
  205. $item['info'] = $media_info;
  206. $rss_element['attributes']['width'] = $media_info->info['width'];
  207. $rss_element['attributes']['height'] = $media_info->info['height'];
  208. }
  209. }
  210. // Additional properties for audio files.
  211. if ($item['type'] == 'audio' && module_exists('getid3')) {
  212. $media_info = getid3_analyze(drupal_realpath($item_uri));
  213. if (!empty($media_info)) {
  214. $item['info'] = $media_info;
  215. $rss_element['attributes']['bitrate'] = round($media_info['audio']['bitrate'] / 1000);
  216. $rss_element['attributes']['samplingrate'] = $media_info['audio']['sample_rate'];
  217. $rss_element['attributes']['channels'] = $media_info['audio']['channels'];
  218. $rss_element['attributes']['duration'] = round($media_info['playtime_seconds']);
  219. }
  220. }
  221. // Additional properties for video files.
  222. if ($item['type'] == 'video' && module_exists('getid3')) {
  223. $media_info = getid3_analyze(drupal_realpath($item_uri));
  224. if (!empty($media_info)) {
  225. $item['info'] = $media_info;
  226. $rss_element['attributes']['bitrate'] = round($media_info['video']['bitrate'] / 1000);
  227. $rss_element['attributes']['framerate'] = $media_info['video']['frame_rate'];
  228. $rss_element['attributes']['duration'] = round($media_info['playtime_seconds']);
  229. $rss_element['attributes']['width'] = $media_info['video']['resolution_x'];
  230. $rss_element['attributes']['height'] = $media_info['video']['resolution_y'];
  231. }
  232. }
  233. // Additional properties for Video Embed fields.
  234. if ($field['type'] == 'video_embed_field') {
  235. if (!empty($item['video_data']['media$group']['media$content'][0])) {
  236. $rss_element['attributes'] += $item['video_data']['media$group']['media$content'][0];
  237. }
  238. if (!empty($item['video_data']['media$group']['media$title'])) {
  239. $rss_element['value'][] = array(
  240. 'key' => 'media:title',
  241. 'value' => $item['video_data']['media$group']['media$title'],
  242. );
  243. }
  244. if (!empty($item['video_data']['media$group']['media$description'])) {
  245. $rss_element['value'][] = array(
  246. 'key' => 'media:description',
  247. 'value' => $item['video_data']['media$group']['media$description'],
  248. );
  249. }
  250. if (!empty($item['video_data']['media$group']['media$thumbnail'][0])) {
  251. $rss_element['value'][] = array(
  252. 'key' => 'media:thumbnail',
  253. 'attributes' => $item['video_data']['media$group']['media$thumbnail'][0],
  254. );
  255. }
  256. if (!empty($item['video_data']['media$group']['media$category'])) {
  257. foreach ($item['video_data']['media$group']['media$category'] as $category) {
  258. $rss_element['value'][] = array(
  259. 'key' => 'media:category',
  260. 'value' => $category,
  261. );
  262. }
  263. }
  264. if (!empty($item['video_data']['media$group']['media$keywords'])) {
  265. $rss_element['value'][] = array(
  266. 'key' => 'media:keywords',
  267. 'value' => implode(', ', $item['video_data']['media$group']['media$keywords']),
  268. );
  269. }
  270. }
  271. // Generate file hash for <media:hash> element.
  272. // hash_file() function used to generate file hashes
  273. // is available only in PHP versions >= 5.1.2.
  274. // @see http://www.php.net/manual/en/function.hash-file.php
  275. if (version_compare(phpversion(), '5.1.2') >= 0 && !empty($display['settings']['generate_hash'])) {
  276. $hash = hash_file($display['settings']['hash_algo'], $item_uri);
  277. $rss_element['value'][] = array(
  278. 'key' => 'media:hash',
  279. 'value' => $hash,
  280. 'attributes' => array(
  281. 'algo' => strtr($display['settings']['hash_algo'], array('sha1' => 'sha-1')),
  282. ),
  283. );
  284. }
  285. // Other additional sub-elements.
  286. $extra_fields = array('title', 'description');
  287. foreach ($extra_fields as $extra_field) {
  288. $subelement = array();
  289. $field_extra_field = 'field_' . $extra_field;
  290. // Images could have 'title' and 'description' properties.
  291. if (isset($item[$extra_field]) && $item[$extra_field]) {
  292. $subelement = array(
  293. 'key' => 'media:' . $extra_field,
  294. 'value' => $item[$extra_field],
  295. );
  296. }
  297. // Other fieldable entities (think media) could have, well, fields.
  298. if (isset($item[$field_extra_field][LANGUAGE_NONE][0]['value']) && $item[$field_extra_field][LANGUAGE_NONE][0]['value']) {
  299. $subelement = array(
  300. 'key' => 'media:' . $extra_field,
  301. 'value' => $item[$field_extra_field][LANGUAGE_NONE][0]['value'],
  302. );
  303. }
  304. // Check whether original value format is text or HTML.
  305. if ($subelement) {
  306. $type = 'plain';
  307. $value_decoded = htmlspecialchars_decode($subelement['value'], ENT_QUOTES);
  308. if ($value_decoded != strip_tags($value_decoded)) {
  309. $type = 'html';
  310. }
  311. $subelement['attributes']['type'] = $type;
  312. $rss_element['value'][] = $subelement;
  313. }
  314. }
  315. }
  316. if ($display['type'] == 'media_thumbnail') {
  317. // Main RSS element.
  318. $rss_element = array(
  319. 'key' => 'media:thumbnail',
  320. 'attributes' => array(
  321. 'url' => $url,
  322. ),
  323. );
  324. // Attributes for images: width and height.
  325. if ($item['type'] == 'image') {
  326. $media_info = image_load($item_uri);
  327. if (!empty($media_info)) {
  328. $item['info'] = $media_info;
  329. $rss_element['attributes']['width'] = $media_info->info['width'];
  330. $rss_element['attributes']['height'] = $media_info->info['height'];
  331. }
  332. }
  333. // Attribute: time.
  334. if (isset($item['field_time'][LANGUAGE_NONE][0]['value']) && $item['field_time'][LANGUAGE_NONE][0]['value']) {
  335. $rss_element['attributes']['time'] = $item['field_time'][LANGUAGE_NONE][0]['value'];
  336. }
  337. }
  338. if ($display['type'] == 'media_category') {
  339. // Main RSS element.
  340. $term = taxonomy_term_load($item['tid']);
  341. $rss_element = array(
  342. 'key' => 'media:category',
  343. 'value' => $term->name,
  344. );
  345. // Attributes: scheme and label.
  346. $attributes = array('scheme', 'label');
  347. foreach ($attributes as $attribute) {
  348. $field_name = 'field_' . $attribute;
  349. $field_items = field_get_items('taxonomy_term', $term, $field_name);
  350. if (!empty($field_items[0]['value'])) {
  351. $rss_element['attributes'][$attribute] = $field_items[0]['value'];
  352. }
  353. }
  354. }
  355. $element[$delta] = array(
  356. '#item' => $item,
  357. '#markup' => format_xml_elements(array($rss_element)),
  358. '#rss_element' => $rss_element,
  359. '#settings' => $display['settings'],
  360. );
  361. }
  362. return $element;
  363. }