views_rss_plugin_style_fields.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <?php
  2. /**
  3. * @file
  4. * Extends the view_plugin_style class to provide new RSS view style.
  5. */
  6. class views_rss_plugin_style_fields extends views_plugin_style {
  7. /**
  8. * Attach this view to another display as a feed.
  9. */
  10. function attach_to($display_id, $path, $title) {
  11. $display = $this->view->display[$display_id]->handler;
  12. $url_options = array('absolute' => TRUE);
  13. $input = $this->view->get_exposed_input();
  14. if ($input) {
  15. $url_options['query'] = $input;
  16. }
  17. // Don't add arguments to RSS path if the feed does not support arguments.
  18. $feed_path = !empty($this->display->display_options['arguments']) ? $this->view->get_url(NULL, $path) : $path;
  19. $url = url($feed_path, $url_options);
  20. if ($display->has_path() && !$this->options['feed_settings']['feed_in_links']) {
  21. if (empty($this->preview)) {
  22. drupal_add_feed($url, $title);
  23. }
  24. }
  25. else {
  26. if (empty($this->view->feed_icon)) {
  27. $this->view->feed_icon = '';
  28. }
  29. $this->view->feed_icon .= theme('feed_icon', array('url' => $url, 'title' => $title));
  30. drupal_add_html_head_link(array(
  31. 'rel' => 'alternate',
  32. 'type' => 'application/rss+xml',
  33. 'title' => $title,
  34. 'href' => $url,
  35. ));
  36. }
  37. }
  38. function option_definition() {
  39. $options = parent::option_definition();
  40. // Namespace defaults.
  41. $namespaces = views_rss_get('namespaces');
  42. if (count($namespaces)) {
  43. foreach ($namespaces as $module => $module_namespaces) {
  44. foreach (array_keys($module_namespaces) as $namespace) {
  45. $options['namespaces'][$module][$namespace] = array('default' => NULL);
  46. }
  47. }
  48. }
  49. // Channel element defaults.
  50. $channel_elements = views_rss_get('channel_elements');
  51. if (count($channel_elements)) {
  52. foreach ($channel_elements as $module => $module_channel_elements) {
  53. foreach (array_keys($module_channel_elements) as $element) {
  54. list($namespace, $element_name) = views_rss_extract_element_names($element, 'core');
  55. $options['channel'][$namespace][$module][$element_name] = array('default' => NULL);
  56. }
  57. }
  58. }
  59. // Item element defaults.
  60. $item_elements = views_rss_get('item_elements');
  61. if (count($item_elements)) {
  62. foreach ($item_elements as $module => $module_item_elements) {
  63. foreach (array_keys($module_item_elements) as $element) {
  64. list($namespace, $element_name) = views_rss_extract_element_names($element, 'core');
  65. $options['item'][$namespace][$module][$element_name] = array('default' => NULL);
  66. }
  67. }
  68. }
  69. // Other feed settings defaults.
  70. $options['feed_settings']['feed_in_links'] = array('default' => 0);
  71. return $options;
  72. }
  73. /**
  74. * Provide a form for setting options.
  75. *
  76. * @param array $form
  77. * @param array $form_state
  78. */
  79. function options_form(&$form, &$form_state) {
  80. parent::options_form($form, $form_state);
  81. $handlers = $this->display->handler->get_handlers('field');
  82. if (empty($handlers)) {
  83. drupal_set_message(t('You need at least one field before you can configure your field settings.'), 'error');
  84. }
  85. else {
  86. // Field chooser.
  87. $field_names = array('' => '--');
  88. foreach ($handlers as $field => $handler) {
  89. if ($label = $handler->label()) {
  90. $field_names[$field] = $label;
  91. }
  92. else {
  93. $field_names[$field] = $handler->ui_name();
  94. }
  95. }
  96. // Element groups could be used both in channel and item settings.
  97. $element_groups = views_rss_get('element_groups');
  98. // Channel elements settings.
  99. $channel_elements = views_rss_get('channel_elements');
  100. if (count($channel_elements)) {
  101. foreach ($channel_elements as $module => $module_channel_elements) {
  102. foreach ($module_channel_elements as $element => $definition) {
  103. if (!isset($definition['configurable']) || $definition['configurable']) {
  104. list($namespace, $element_name) = views_rss_extract_element_names($element, 'core');
  105. // Add fieldset for namespace if not yet added.
  106. if (!isset($form['channel'][$namespace])) {
  107. $form['channel'][$namespace] = array(
  108. '#type' => 'fieldset',
  109. '#title' => t('Channel elements : @namespace', array('@namespace' => $namespace)),
  110. '#description' => t('Provide values for &lt;channel&gt; elements in "@namespace" namespace. See <a href="@guide_url">Views RSS documentation</a> for more information.', array(
  111. '@namespace' => $namespace,
  112. '@guide_url' => url('http://drupal.org/node/1344136'),
  113. )),
  114. '#collapsible' => TRUE,
  115. '#collapsed' => TRUE,
  116. );
  117. }
  118. // Prepare form element.
  119. $default_value = NULL;
  120. if (!empty($this->options['channel'][$namespace][$module][$element_name])) {
  121. $default_value = $this->options['channel'][$namespace][$module][$element_name];
  122. }
  123. $form_item = array(
  124. '#type' => 'textfield',
  125. '#title' => filter_xss(isset($definition['title']) ? $definition['title'] : $element_name),
  126. '#description' => filter_xss(isset($definition['description']) ? $definition['description'] : NULL),
  127. '#default_value' => $default_value,
  128. );
  129. // Allow to overwrite default form element.
  130. if (!empty($definition['settings form'])) {
  131. $form_item = array_merge($form_item, $definition['settings form']);
  132. // Make sure that #options is an associative array.
  133. if (!empty($definition['settings form']['#options'])) {
  134. $form_item['#options'] = views_rss_map_assoc($definition['settings form']['#options']);
  135. }
  136. }
  137. if (!empty($definition['settings form options callback'])) {
  138. $function = $definition['settings form options callback'];
  139. $form_item['#options'] = views_rss_map_assoc($function());
  140. }
  141. // Add help link if provided.
  142. if (!empty($definition['help'])) {
  143. $form_item['#description'] .= ' ' . l('[?]', $definition['help'], array('attributes' => array('title' => t('Need more information?'))));
  144. }
  145. // Check if element should be displayed in a subgroup.
  146. if (!empty($definition['group'])) {
  147. // Add a subgroup to the form if it not yet added.
  148. if (!isset($form['channel'][$namespace][$module][$definition['group']])) {
  149. // Does module provide the group definition?
  150. $group_title = !empty($element_groups[$module][$definition['group']]['title']) ? $element_groups[$module][$definition['group']]['title'] : $definition['group'];
  151. $group_description = !empty($element_groups[$module][$definition['group']]['description']) ? $element_groups[$module][$definition['group']]['description'] : NULL;
  152. $form['channel'][$namespace][$module][$definition['group']] = array(
  153. '#type' => 'fieldset',
  154. '#title' => filter_xss($group_title),
  155. '#description' => filter_xss($group_description),
  156. '#collapsible' => TRUE,
  157. '#collapsed' => TRUE,
  158. );
  159. }
  160. $form['channel'][$namespace][$module][$definition['group']][$element_name] = $form_item;
  161. }
  162. // Display element normally (not within a subgroup).
  163. else {
  164. $form['channel'][$namespace][$module][$element_name] = $form_item;
  165. }
  166. }
  167. }
  168. }
  169. }
  170. // Item elements settings.
  171. $item_elements = views_rss_get('item_elements');
  172. if (count($item_elements)) {
  173. foreach ($item_elements as $module => $module_item_elements) {
  174. foreach ($module_item_elements as $element => $definition) {
  175. if (!isset($definition['configurable']) || $definition['configurable']) {
  176. list($namespace, $element_name) = views_rss_extract_element_names($element, 'core');
  177. // Add fieldset for namespace if not yet added.
  178. if (!isset($form['item'][$namespace])) {
  179. $form['item'][$namespace] = array(
  180. '#type' => 'fieldset',
  181. '#title' => t('Item elements : @namespace', array('@namespace' => $namespace)),
  182. '#description' => t('Select fields containing relevant values for &lt;item&gt; elements in "@namespace" namespace. See <a href="@guide_url">Views RSS documentation</a> for more information.', array(
  183. '@namespace' => $namespace,
  184. '@guide_url' => url('http://drupal.org/node/1344136'),
  185. )),
  186. '#collapsible' => TRUE,
  187. '#collapsed' => TRUE,
  188. );
  189. }
  190. // Prepare form element.
  191. $default_value = NULL;
  192. if (!empty($this->options['item'][$namespace][$module][$element_name])) {
  193. $default_value = $this->options['item'][$namespace][$module][$element_name];
  194. }
  195. $form_item = array(
  196. '#type' => 'select',
  197. '#title' => filter_xss(isset($definition['title']) ? $definition['title'] : $element_name),
  198. '#description' => filter_xss(isset($definition['description']) ? $definition['description'] : NULL),
  199. '#options' => $field_names,
  200. '#default_value' => $default_value,
  201. );
  202. // Allow to overwrite default form element.
  203. if (!empty($definition['settings form'])) {
  204. $form_item = array_merge($form_item, $definition['settings form']);
  205. // Make sure that #options is an associative array.
  206. if (!empty($definition['settings form']['#options'])) {
  207. $form_item['#options'] = views_rss_map_assoc($definition['settings form']['#options']);
  208. }
  209. }
  210. // Add help link if provided.
  211. if (isset($definition['help']) && $definition['help']) {
  212. $form_item['#description'] .= ' ' . l('[?]', $definition['help'], array('attributes' => array('title' => t('Need more information?'))));
  213. }
  214. // Check if element should be displayed in a subgroup.
  215. if (isset($definition['group']) && $definition['group']) {
  216. // Add a subgroup to the form if it not yet added.
  217. if (!isset($form['item'][$namespace][$module][$definition['group']])) {
  218. // Does module provide the group definition?
  219. $group_title = !empty($element_groups[$module][$definition['group']]['title']) ? $element_groups[$module][$definition['group']]['title'] : $definition['group'];
  220. $group_description = !empty($element_groups[$module][$definition['group']]['description']) ? $element_groups[$module][$definition['group']]['description'] : NULL;
  221. $form['item'][$namespace][$module][$definition['group']] = array(
  222. '#type' => 'fieldset',
  223. '#title' => filter_xss($group_title),
  224. '#description' => filter_xss($group_description),
  225. '#collapsible' => TRUE,
  226. '#collapsed' => TRUE,
  227. );
  228. }
  229. $form['item'][$namespace][$module][$definition['group']][$element_name] = $form_item;
  230. }
  231. // Display element normally (not within a subgroup).
  232. else {
  233. $form['item'][$namespace][$module][$element_name] = $form_item;
  234. }
  235. }
  236. }
  237. }
  238. }
  239. // Undefined namespaces derived from <channel> and/or <item>
  240. // elements defined by extension modules.
  241. $namespaces = views_rss_get('namespaces');
  242. if (count($namespaces)) {
  243. foreach ($namespaces as $module => $module_namespaces) {
  244. foreach ($module_namespaces as $namespace => $definition) {
  245. if (empty($definition['uri'])) {
  246. // Add fieldset for namespace if not yet added.
  247. if (!isset($form['namespaces'])) {
  248. $form['namespaces'] = array(
  249. '#type' => 'fieldset',
  250. '#title' => t('Namespaces'),
  251. '#description' => t('Enter missing URLs for namespaces derived from &lt;channel&gt; and/or &lt;item&gt; elements defined by extension modules.'),
  252. '#collapsible' => TRUE,
  253. '#collapsed' => TRUE,
  254. );
  255. }
  256. if (!empty($this->options['namespaces'][$module][$namespace])) {
  257. $default_value = $this->options['namespaces'][$module][$namespace];
  258. }
  259. else {
  260. $default_value = NULL;
  261. }
  262. $form['namespaces'][$module][$namespace] = array(
  263. '#type' => 'textfield',
  264. '#title' => check_plain($namespace),
  265. '#default_value' => $default_value,
  266. );
  267. }
  268. }
  269. }
  270. }
  271. // Other feed settings.
  272. $form['feed_settings'] = array(
  273. '#type' => 'fieldset',
  274. '#title' => t('Other feed settings'),
  275. '#collapsible' => TRUE,
  276. '#collapsed' => TRUE,
  277. );
  278. $form['feed_settings']['absolute_paths'] = array(
  279. '#type' => 'checkbox',
  280. '#title' => t("Replace relative paths with absolute URLs"),
  281. '#description' => t('Enabling this option will replace all relative paths (like <em>/node/1</em>) with absolute URLs (<em>!absolute_url</em>) in all feed elements configured to use this feature (for example &lt;description&gt; element).', array(
  282. '!absolute_url' => trim($GLOBALS['base_url'], '/') . '/node/1',
  283. )),
  284. '#default_value' => !empty($this->options['feed_settings']['absolute_paths']) || !isset($this->options['feed_settings']['absolute_paths']),
  285. '#weight' => 1,
  286. );
  287. $form['feed_settings']['feed_in_links'] = array(
  288. '#type' => 'checkbox',
  289. '#title' => t('Display feed icon in the links attached to the view'),
  290. '#default_value' => !empty($this->options['feed_settings']['feed_in_links']),
  291. '#weight' => 3,
  292. );
  293. }
  294. }
  295. /**
  296. * Allow other modules to validate options form values prior to submit.
  297. */
  298. function options_validate(&$form, &$form_state) {
  299. foreach (module_implements('views_rss_options_form_validate') as $module) {
  300. $function = $module . '_views_rss_options_form_validate';
  301. $function($form, $form_state);
  302. }
  303. }
  304. /**
  305. * Allow other modules to perform any necessary changes
  306. * to options form values prior to storage.
  307. */
  308. function options_submit(&$form, &$form_state) {
  309. foreach (module_implements('views_rss_options_form_submit') as $module) {
  310. $function = $module . '_views_rss_options_form_submit';
  311. $function($form, $form_state);
  312. }
  313. }
  314. /**
  315. * Make sure the display and all associated handlers are valid.
  316. */
  317. function validate() {
  318. parent::validate();
  319. $errors = array();
  320. $channel_elements = views_rss_get('channel_elements');
  321. $item_elements = views_rss_get('item_elements');
  322. if (empty($channel_elements) && empty($item_elements)) {
  323. $errors[] = t('You have not enabled any modules providing feed elements. Please enable at least <a href="@modules_url">Views RSS: Core Elements</a> module.', array(
  324. '@modules_url' => url('admin/modules', array('fragment' => 'edit-modules-views')),
  325. ));
  326. }
  327. return $errors;
  328. }
  329. /**
  330. * Map views row result to an RSS item.
  331. */
  332. function map_rows($rows) {
  333. // Fields must be pre-rendered starting from version 2.3 of Views module.
  334. $rendered = $raw = array();
  335. $keys = array_keys($this->view->field);
  336. foreach ($rows as $count => $row) {
  337. $this->view->row_index = $count;
  338. foreach ($keys as $id) {
  339. $rendered[$count][$id] = $this->view->field[$id]->theme($row);
  340. // Also let's keep raw value for further processing.
  341. $field_name = 'field_' . $id;
  342. if (!empty($row->$field_name)) {
  343. $raw[$count][$id] = $row->$field_name;
  344. }
  345. }
  346. }
  347. // Rewrite view rows to XML item rows.
  348. $items = $raw_items = array();
  349. $item_elements = views_rss_get('item_elements');
  350. foreach ($rendered as $id => $row) {
  351. $item = $raw_item = array();
  352. foreach ($item_elements as $module => $module_item_elements) {
  353. foreach (array_keys($module_item_elements) as $element) {
  354. list($namespace, $element_name) = views_rss_extract_element_names($element, 'core');
  355. // Assign values for all elements, not only those defined in view settings.
  356. // If element value is not defined in view settings, let's just assign NULL.
  357. // It will not be passed to final theme function anyway during processing
  358. // taking place in template_preprocess_views_view_views_rss().
  359. if (
  360. isset($this->options['item'][$namespace][$module][$element_name])
  361. && isset($row[$this->options['item'][$namespace][$module][$element_name]])
  362. ) {
  363. $item[$module][$element] = $row[$this->options['item'][$namespace][$module][$element_name]];
  364. }
  365. else {
  366. $item[$module][$element] = NULL;
  367. }
  368. // Keep raw values too.
  369. if (
  370. !empty($this->options['item'][$namespace][$module][$element_name])
  371. && !empty($raw[$id][$this->options['item'][$namespace][$module][$element_name]])
  372. ) {
  373. $raw_item[$module][$element] = $raw[$id][$this->options['item'][$namespace][$module][$element_name]];
  374. }
  375. }
  376. }
  377. $items[$id] = $item;
  378. $raw_items[$id] = $raw_item;
  379. }
  380. $this->view->views_rss['raw_items'] = $raw_items;
  381. return $items;
  382. }
  383. }