Sync from HEAD
[project/views.git] / views_rss.module
1 <?php
2 // $Id$
3
4 /**
5 * Provide views plugins for the feed types we support.
6 */
7 function views_rss_views_style_plugins() {
8 return array(
9 'views_rss' => array(
10 'name' => t('Views RSS: RSS feed'),
11 'theme' => 'views_rss_feed',
12 'needs_table_header' => TRUE,
13 'needs_fields' => TRUE,
14 'even_empty' => TRUE,
15 ),
16 );
17 }
18
19 /**
20 * While we support the global selector, some might want to allow
21 * ONLY RSS feeds so we support a stingy selector too
22 */
23 function views_rss_views_arguments() {
24 $arguments = array(
25 'rss_feed' => array(
26 'name' => t('RSS: RSS Feed Selector'),
27 'handler' => 'views_handler_arg_rss_feed',
28 'help' => t('This argument specifies a specific RSS feed selector; it will only select RSS feeds, unlike the built-in selector which can select pluggable feeds.'),
29 ),
30 );
31 return $arguments;
32 }
33
34 /**
35 * handler for our own RSS argument; mimics the feed selector
36 */
37 function views_handler_arg_rss_feed($op, &$query, $argtype, $arg = '') {
38 switch($op) {
39 case 'summary':
40 case 'sort':
41 case 'link':
42 case 'title':
43 break;
44 case 'filter':
45 // This is a clone of the default selector, but it just invokes ours
46 // rather than calling all of them.
47 views_rss_views_feed_argument('argument', $GLOBALS['current_view'], $arg);
48 }
49 }
50
51 /**
52 * post view for our own op -- mimics the feed selector
53 */
54 function views_rss_views_post_view($view, $items, $output) {
55 foreach ($view->argument as $id => $argument) {
56 if ($argument['type'] == 'rss_feed') {
57 $feed = $id;
58 break;
59 }
60 }
61
62 if ($feed !== NULL) {
63 return views_rss_views_feed_argument('post_view', $view, 'rss_feed');
64 }
65 }
66
67 /**
68 * feed argument hook that will convert us to RSS or display an icon.
69 * the 4th argument isn't part of the hook, but we use it to differentiate
70 * when called as a hook or when called manually from views_rss_views_post_view
71 */
72 function views_rss_views_feed_argument($op, &$view, $arg) {
73 if ($op == 'argument' && $arg == 'feed') {
74 $view->page_type = 'views_rss';
75 }
76 else if ($op == 'post_view') {
77 $args = views_post_view_make_args($view, $arg, 'feed');
78 $url = views_get_url($view, $args);
79 $title = views_get_title($view, 'page', $args);
80
81 if ($view->used_filters) {
82 $filters = drupal_query_string_encode($view->used_filters);
83 }
84
85 drupal_add_feed(url($url, $filters), $title);
86 }
87 }
88
89 /**
90 * plugin that actually displays an RSS feed
91 */
92 function theme_views_rss_feed($view, $nodes, $type) {
93 if ($type == 'block') {
94 return;
95 }
96 global $base_url;
97
98 $channel = array(
99 // a check_plain isn't required on these because format_rss_channel
100 // already does this.
101 'title' => views_get_title($view, 'page'),
102 'link' => url($view->real_url, NULL, NULL, true),
103 'description' => $view->description,
104 );
105
106 $item_length = variable_get('feed_item_length', 'teaser');
107 $namespaces = array('xmlns:dc="http://purl.org/dc/elements/1.1/"');
108
109 // Except for the original being a while and this being a foreach, this is
110 // completely cut & pasted from node.module.
111 foreach ($nodes as $node) {
112 // Load the specified node:
113 $item = node_load($node->nid);
114 $link = url("node/$node->nid", NULL, NULL, 1);
115
116 if ($item_length != 'title') {
117 $teaser = ($item_length == 'teaser') ? TRUE : FALSE;
118
119 // Filter and prepare node teaser
120 if (node_hook($item, 'view')) {
121 node_invoke($item, 'view', $teaser, FALSE);
122 }
123 else {
124 $item = node_prepare($item, $teaser);
125 }
126
127 // Allow modules to change $node->teaser before viewing.
128 node_invoke_nodeapi($item, 'view', $teaser, FALSE);
129 }
130
131 // Allow modules to add additional item fields
132 $extra = node_invoke_nodeapi($item, 'rss item');
133 $extra = array_merge($extra, array(array('key' => 'pubDate', 'value' => date('r', $item->created)), array('key' => 'dc:creator', 'value' => $item->name), array('key' => 'guid', 'value' => $item->nid . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false'))));
134 foreach ($extra as $element) {
135 if ($element['namespace']) {
136 $namespaces = array_merge($namespaces, $element['namespace']);
137 }
138 }
139
140 // Prepare the item description
141 switch ($item_length) {
142 case 'fulltext':
143 $item_text = $item->body;
144 break;
145 case 'teaser':
146 $item_text = $item->teaser;
147 if ($item->readmore) {
148 $item_text .= '<p>'. l(t('read more'), 'node/'. $item->nid, NULL, NULL, NULL, TRUE) .'</p>';
149 }
150 break;
151 case 'title':
152 $item_text = '';
153 break;
154 }
155
156 $items .= format_rss_item($item->title, $link, $item_text, $extra);
157 }
158
159 $channel_defaults = array(
160 'version' => '2.0',
161 'title' => variable_get('site_name', 'drupal') .' - '. variable_get('site_slogan', ''),
162 'link' => $base_url,
163 'description' => variable_get('site_mission', ''),
164 'language' => $GLOBALS['locale'],
165 );
166 $channel = array_merge($channel_defaults, $channel);
167
168 $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
169 $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\" ". implode(' ', $namespaces) .">\n";
170 $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
171 $output .= "</rss>\n";
172
173 drupal_set_header('Content-Type: text/xml; charset=utf-8');
174 print $output;
175 module_invoke_all('exit');
176 exit;
177 }