/[drupal]/contributions/modules/rdf/rdf.feed.inc
ViewVC logotype

Contents of /contributions/modules/rdf/rdf.feed.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.11 - (show annotations) (download) (as text)
Thu Sep 3 09:35:45 2009 UTC (2 months, 3 weeks ago) by arto
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +5 -2 lines
File MIME type: text/x-php
Do author name token substitution using both node and user tokens.
1 <?php
2 // $Id: rdf.feed.inc,v 1.7 2009/08/01 14:18:07 arto Exp $
3
4 //////////////////////////////////////////////////////////////////////////////
5 // RDF feeds
6
7 function rdf_build_rss_feed($feed, $url, $items = array(), array $options = array()) {
8 rdf_register_namespace('rss', RDF_RSS_URI);
9 rdf_register_namespace('sy', RDF_RSS_URI . 'modules/syndication/');
10
11 // Compose the feed title:
12 $title = variable_get('site_name', 'Drupal') . (($slogan = variable_get('site_slogan', '')) ? ' - ' . $slogan : '');
13 $title = !empty($options['title']) ? $options['title'] : $title; // FIXME
14
15 // Compose the feed description:
16 $description = !empty($options['description_from_mission']) ? variable_get('site_mission', '') : $options['description'];
17
18 // Construct the channel's metadata:
19 $index = rdf_seq();
20 $channel = rdf_denormalize(array(
21 $url => array(
22 'rdf:type' => rdf_qname_to_uriref('rss:channel'),
23 'rss:title' => rdf_literal($title, TRUE),
24 'rss:link' => !empty($options['link']) ? $options['link'] : $url,
25 'rss:description' => rdf_literal($description, TRUE),
26 'rss:items' => $index->uriref(),
27 ),
28 ));
29
30 // Provide support for RSS 1.0 Syndication hints:
31 // @see http://web.resource.org/rss/1.0/modules/syndication/
32 if (!empty($options['update_period'])) {
33 $channel[] = array($url, 'sy:updatePeriod', check_plain($options['update_period']));
34 }
35 if (!empty($options['update_frequency'])) {
36 $channel[] = array($url, 'sy:updateFrequency', check_plain($options['update_frequency']));
37 }
38 if (!empty($options['update_base'])) {
39 $channel[] = array($url, 'sy:updateBase', check_plain($options['update_base']));
40 }
41
42 // Invoke hook_rdf_feed_channel() to allow third-party modules to
43 // manipulate the RSS channel metadata:
44 foreach (module_implements('rdf_feed_channel') as $module) {
45 $function = $module . '_rdf_feed_channel';
46 $function($feed, $url, $channel);
47 }
48
49 // Compile the channel index and render the feed items into triples:
50 $data = array();
51 foreach ($items as $url => $item) {
52 // Invoke hook_rdf_feed_item() to allow third-party modules to
53 // manipulate the RSS item metadata:
54 $item = rdf_denormalize(array($url => $item));
55 foreach (module_implements('rdf_feed_item') as $module) {
56 $function = $module . '_rdf_feed_item';
57 $function($feed, $url, $item);
58 }
59
60 $index[] = $url;
61 $data = array_merge($data, $item);
62 }
63
64 // Invoke hook_rdf_feed() to allow third-party modules to manipulate the
65 // full contents of the RSS feed, including the channel metadata and all
66 // constituent feed items. Note that unlike the other two hooks,
67 // hook_rdf_feed_channel() and hook_rdf_feed_item(), the $data argument to
68 // this hook is given in normalized form. This makes manipulation easier
69 // and may be a good reason to implement this hook instead of the other
70 // two which, getting passed the triples in denormalized form, are more
71 // suited to easily adding more triples to the channel and the feed items
72 // than for manipulating existing data.
73 // Implementers of this hook have final say in the feed output.
74 $data = rdf_normalize(array_merge($channel, $index->to_triples(), $data));
75 foreach (module_implements('rdf_views_feed') as $module) {
76 $function = $module . '_rdf_views_feed';
77 $function($feed, $url, $data);
78 }
79
80 return $data;
81 }
82
83 //////////////////////////////////////////////////////////////////////////////
84
85 function rdf_build_rss_feed_item($path, array $options = array()) {
86 return array(); // TODO
87 }
88
89 function rdf_build_rss_feed_aggregator_item($item, $category = NULL) {
90 switch ($feed_length = variable_get('feed_item_length', 'teaser')) {
91 case 'teaser':
92 if ($item->description != ($item->description = node_teaser($item->description))) {
93 $item->description .= '<p><a href="' . check_url($item->link) . '">' . t('read more') . "</a></p>\n";
94 }
95 break;
96 case 'title':
97 $item->description = '';
98 break;
99 }
100
101 $data = array(
102 $item->link => array(
103 'rss:title' => t('@feed_title: @item_title', array('@feed_title' => $item->ftitle, '@item_title' => $item->title)),
104 'rss:link' => $item->link,
105 'rss:description' => $item->description,
106 'dc:date' => rdf_datetime($item->timestamp),
107 'dc:creator' => !empty($item->author) ? $item->author : $item->ftitle,
108 'dc:publisher' => rdf_uri($item->flink),
109 ),
110 );
111
112 if (!empty($category->title)) {
113 $data[$item->link]['dc:subject'] = $category->title;
114 }
115
116 return $data;
117 }
118
119 //////////////////////////////////////////////////////////////////////////////
120
121 function rdf_build_rss_feed_node($nid, array $options = array()) {
122 // Load the specified node:
123 $item = node_load(is_object($nid) ? $nid->nid : $nid);
124 $item->build_mode = NODE_BUILD_RSS;
125 $item->url = url('node/' . $item->nid, array('absolute' => TRUE, 'alias' => TRUE));
126 $item->link = url('node/' . $item->nid, array('absolute' => TRUE));
127
128 // Apply input filters and prepare the node teaser, also allowing other
129 // $modules to change node->teaser before viewing:
130 $item_length = variable_get('feed_item_length', 'teaser');
131 $item_length = (!empty($options['item_length']) && $options['item_length'] != 'default') ? $options['item_length'] : $item_length;
132 $teaser = ($item_length == 'teaser');
133 $item = node_hook($item, 'view') ? node_invoke($item, 'view', $teaser, FALSE) : node_prepare($item, $teaser);
134 node_invoke_nodeapi($item, 'view', $teaser, FALSE);
135
136 // Make sure the item has all the basic mandatory RSS 1.0 properties:
137 // @see http://web.resource.org/rss/1.0/spec#s5.5
138 $item->rdf = array(
139 'rdf:type' => rdf_qname_to_uriref('rss:item'),
140 'rss:title' => rdf_literal($item->title, $item),
141 'rss:link' => $item->link, // NOTE: it actually *is* defined as an xsd:string property, not a URI.
142 );
143
144 // Include the node body using a rss:description property unless "Title
145 // only" was selected in the feed configuration. This property is optional
146 // in the spec so leaving it out is fine.
147 if ($item_length != 'title') {
148 // Prepare the item description:
149 switch ($item_length) {
150 case 'fulltext':
151 $item_text = trim($item->body);
152 break;
153 case 'teaser':
154 $item_text = trim($item->teaser);
155 if (!empty($item->readmore)) {
156 $item_text .= '<p>' . l(t('read more'), 'node/' . $item->nid, array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . '</p>';
157 }
158 break;
159 }
160 if ($item_text != '') {
161 $item->rdf['rss:description'][] = rdf_literal($item_text, $item);
162 }
163 }
164
165 // Add dc:date for the created date and dc:creator for the author:
166 // @see http://web.resource.org/rss/1.0/modules/dc/
167 $item->rdf['dc:date'][] = rdf_datetime($item->created, empty($options['date_timezone']) ? 0 : variable_get('date_default_timezone', 0));
168
169 rdf_build_rss_feed_node_author($item, $options);
170 rdf_build_rss_feed_node_taxonomy($item, $options);
171 rdf_build_rss_feed_nodeapi($item, $options);
172
173 // HACK: this snippet provides direct support for RDF-mapped CCK fields.
174 // This should eventually be properly handled by the RDF Schema API.
175 if (isset($item->content) && is_array($item->content)) {
176 foreach ($item->content as $field_name => $field_info) {
177 rdf_build_rss_feed_node_field($item, $field_name, $field_info, $options);
178 }
179 if (module_exists('fieldgroup')) {
180 foreach (fieldgroup_groups($item->type) as $group_name => $group) {
181 foreach ($group['fields'] as $field_name => $field_info) {
182 rdf_build_rss_feed_node_field($item, $field_name, $field_info, $options);
183 }
184 }
185 }
186 }
187
188 // Query the RDF API for all available information on this node. The RDF
189 // Schema API takes care of mapping core fields and CCK fields to RDF
190 // properties for us to use. Note that we remove any returned rdf:type
191 // properties as we want these nodes to be explicitly typed to rss:item.
192 $skip_predicates = array_map('rdf_qname_to_uri', array('rdf:type'));
193 foreach (rdf_query($item->url) as $statement) {
194 list($s, $p, $o) = $statement;
195 if (!in_array((string)$p, $skip_predicates)) { // skip rdf:type
196 $item->rdf[$p][] = $o;
197 }
198 }
199
200 return array($item->url => $item->rdf);
201 }
202
203 function rdf_build_rss_feed_node_author(&$item, array $options) {
204 $item->rdf['dc:creator'][] = $item->name;
205
206 // We always add dc:creator, above, but we also allow another custom
207 // property (e.g. foaf:maker) to be configured. Furthermore, if the Token
208 // module is available, the administrator can configure a token
209 // substitution string in the feed settings and this will be used to build
210 // up the author name to output.
211 if (!empty($options['author_property'])) {
212 if (module_exists('token') && !empty($options['author_value'])) {
213 $author = $options['author_value'];
214 $author = token_replace($author, 'node', $item);
215 $author = token_replace($author, 'user', user_load($item->uid));
216 }
217 else {
218 $author = $item->name;
219 }
220 $item->rdf[$options['author_property']][] = $author;
221 }
222 }
223
224 function rdf_build_rss_feed_node_taxonomy(&$item, array $options) {
225 // Add dc:subject properties for the node's taxonomy terms.
226 // @see http://web.resource.org/rss/1.0/modules/dc/
227 if (!empty($item->taxonomy)) {
228 foreach ($item->taxonomy as $tid => $term) {
229 // TODO: should these be output as literals or taxonomy URIs?
230 $item->rdf['dc:subject'][] = $term->name;
231 }
232 }
233 }
234
235 function rdf_build_rss_feed_nodeapi(&$item, array $options) {
236 // Invoke hook_nodeapi('rss item') to allow modules to add additional item
237 // fields and/or modify $item; this is the same hook as used by
238 // node_feed() in node.module.
239 foreach (node_invoke_nodeapi($item, 'rss item') as $element) {
240 // To prevent general pandemonium due to the modules responding to this
241 // hook having been designed around RSS 2.0, we only support
242 // explicitly-namespaced properties (such as those output by the
243 // Location module).
244 if (!empty($element['namespace'])) {
245 foreach ($element['namespace'] as $ns => $ns_uri) {
246 $ns_prefix = preg_replace('/^xmlns:(.*)$/', '\1', $ns);
247 rdf_register_namespace($ns_prefix, $ns_uri);
248 }
249
250 if (!is_array($element['value'])) {
251 $item->rdf[$element['key']][] = $element['value'];
252 }
253 else {
254 foreach ($element['value'] as $key => $value) {
255 if (!is_array($value)) {
256 $item->rdf[$key][] = $value;
257 }
258 }
259 // TODO: need to construct an equivalent BNode structure
260 //$item->rdf[$element['key']][] =
261 }
262 }
263 }
264 }
265
266 function rdf_build_rss_feed_node_field(&$item, $field_name, $field_info, array $options) {
267 if (isset($field_info['#access']) && !$field_info['#access']) {
268 return; // this provides integration with CCK's Content Permissions module
269 }
270
271 foreach (array('' => 'value', '[from]' => 'value', '[to]' => 'value2') as $field_suffix => $field_value_index) {
272 if (($field_uri = variable_get('rdf_schema_property_content_' . $field_name . $field_suffix, '')) != '' && rdf_is_valid_uri($field_uri)) {
273 if (isset($item->$field_name)) {
274 $field_values = is_array($item->$field_name) ? $item->$field_name : array($item->$field_name);
275 foreach ($field_values as $field_value) {
276 if (($value = $field_value[$field_value_index]) != '') { // skip empty values
277
278 // Cast CCK Date field values into the correct RDF datatype,
279 // with optional timezone information:
280 if (isset($field_value['date_type'])) {
281 $value = rdf_get_rss_feed_date_field($field_value, $field_value_index, $options);
282 }
283 $item->rdf[$field_uri][] = $value;
284 }
285 }
286 }
287 }
288 }
289 }
290
291 function rdf_get_rss_feed_date_field($field_value, $field_value_index, array $options) {
292 $timezone_db = timezone_open($field_value['timezone_db']);
293 $timezone_cck = timezone_open($field_value['timezone']);
294 $timezone = timezone_offset_get($timezone_cck, date_create('now', $timezone_cck));
295
296 switch ($field_value['date_type']) {
297 case 'datestamp': // value, value2, timezone, offset, offset2, timezone_db
298 $offset = $field_value[str_replace('value', 'offset', $field_value_index)];
299 $timestamp = (int)$field_value[$field_value_index];
300 $timestamp = $timestamp + (int)$offset - $timezone;
301 break;
302 case 'date':
303 case 'datetime': // value, value2, timezone, timezone_db
304 $datetime = date_create($field_value[$field_value_index], $timezone_db);
305 $timestamp = (int)date_format($datetime, 'U');
306 break;
307 }
308
309 // Daylight Saving Time (DST) adjustments:
310 if (@$options['date_timezone'] == 'local' && !empty($options['date_timezone_dst'])) {
311 if (($dst_diff = (int)date('I', $timestamp) - (int)date('I', time()))) {
312 $timezone += ($dst_diff * 3600);
313 $timestamp -= ($dst_diff * 3600);
314 }
315 }
316
317 return rdf_datetime($timestamp,
318 empty($options['date_timezone']) ? 0 : $timezone, // TZ offset
319 empty($options['date_timezone_name']) ? NULL : @$field_value['timezone'], // TZ name
320 variable_get('rdf_datetime_datatype', 'xsd:dateTime')); // RDF datatype
321 }

  ViewVC Help
Powered by ViewVC 1.1.2