/[drupal]/contributions/modules/rdf/rdf.module
ViewVC logotype

Contents of /contributions/modules/rdf/rdf.module

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


Revision 1.32 - (show annotations) (download) (as text)
Sat Mar 28 02:37:22 2009 UTC (8 months ago) by arto
Branch: MAIN
CVS Tags: HEAD
Changes since 1.31: +193 -7 lines
File MIME type: text/x-php
Changelog:
- Implemented hook_rdf_adapters(); added a new API function rdf_get_adapters().
- Merged rdf_db_rdf_namespaces() into rdf_rdf_namespaces().
- Merged rdf_db_rdf_contexts() into rdf_rdf_contexts().
- Merged rdf_db_rdf_repositories() into rdf_rdf_repositories().
- Implemented an RDF_DatabaseRepository class based on code previously in rdf.db.inc.
- Renamed rdf_db_create_repository() to rdf_create_repository().
- Renamed rdf_db_update_repository() to rdf_update_repository().
- Renamed rdf_db_rename_repository() to rdf_rename_repository().
- Renamed rdf_db_delete_repository() to rdf_delete_repository().
- Renamed rdf_db_get_schema() to rdf_get_schema().
- Renamed rdf_db_get_repository_tables() to rdf_get_tables().
- Renamed rdf_db_load_repository() to rdf_get_repository().
- Renamed rdf_db_load_namespace() to rdf_get_namespace().
- Renamed rdf_db_count_repository_triples() to RDF_DatabaseRepository::count().
- Renamed rdf_db_merge_duplicate_statements() to RDF_DatabaseRepository::merge_duplicates().
- Renamed _rdf_db_make_record() to RDF_DatabaseRepository::construct_statement().
- Renamed _rdf_db_uri_to_id to RDF_DatabaseRepository::uri_to_id().
- Renamed _rdf_db_uri_to_id_insert to RDF_DatabaseRepository::uri_to_id_insert().
- Renamed _rdf_db_uri_to_id_select to RDF_DatabaseRepository::uri_to_id_select().
- Renamed _rdf_db_delete_statements() to RDF_DatabaseRepository::delete_statements().
- Renamed _rdf_db_select_statements() to RDF_DatabaseRepository::select_statements().
- Renamed _rdf_db_query_statements() to RDF_DatabaseRepository::query_statements().
1 <?php
2 // $Id$
3
4 //////////////////////////////////////////////////////////////////////////////
5 // Module settings
6
7 define('RDF_SCHEMA_URI', rdf_get_schema_uri());
8 define('RDF_SCHEMA_MODULE_URI', 'http://drupal.org/project/rdf#schema');
9 define('RDF_RSS_URI', 'http://purl.org/rss/1.0/');
10 define('RDF_DB_TABLE_DEFAULT', 'rdf_data');
11 define('RDF_DB_TABLE_PREFIX', RDF_DB_TABLE_DEFAULT . '_');
12
13 //////////////////////////////////////////////////////////////////////////////
14 // Core API hooks
15
16 /**
17 * Implementation of hook_init().
18 */
19 function rdf_init() {
20 module_load_include('inc', 'rdf', 'rdf.api');
21 module_load_include('inc', 'rdf', 'rdf.db'); // TODO: remove in beta2
22 module_load_include('inc', 'rdf', 'rdf.schema');
23
24 rdf_define_vocabularies(); // TODO: replace with autoload-based solution
25
26 if (user_access('access RDF data')) {
27 rdf_add_autodiscovery_link(t('RDF'), url('rdf'));
28 }
29
30 // Attempt to load the ARC2 library, if available. This library must be
31 // manually installed by the administrator due to license incompatibility.
32 if (!class_exists('ARC2')) {
33 @include_once RDF_ARC2_PATH . '/ARC2.php';
34 }
35
36 // Integrate some RDF operations into the Services API, if available:
37 if (module_exists('services')) {
38 module_load_include('inc', 'rdf', 'rdf.services');
39 }
40
41 // Integrate with FeedAPI and the Feed Element Mapper, if installed:
42 if (module_exists('feedapi')) {
43 module_load_include('inc', 'rdf', 'rdf.feedapi');
44 }
45
46 // Replace the theme's DOCTYPE with the RDFa DOCTYPE, if enabled:
47 if (variable_get('rdf_rdfa_doctype', FALSE)) {
48 // Implement hook_exit() to mark us as incompatible with aggressive caching:
49 function rdf_exit($destination = NULL) {}
50 ob_start('_rdf_ob_handler');
51 }
52 }
53
54 /**
55 * Implementation of hook_help().
56 */
57 function rdf_help($path, $arg = NULL) {
58 switch ($path) {
59 case 'admin/content/rdf':
60 return '<p>' . t('Any knowledge about anything can be decomposed into statements of <em>triples</em> (3-tuples) consisting of <em>subject</em>, <em>predicate</em>, and <em>object</em>.') . '</p>';
61 case 'admin/settings/rdf':
62 return '<p>' . t('<a href="http://drupal.org/handbook/modules/rdf" title="Resource Description Framework">RDF</a> is a <a href="http://www.w3.org/RDF/">W3C standard</a> for modeling and sharing distributed knowledge based on a decentralized open-world assumption.') . '</p>';
63 case 'admin/settings/rdf#formats':
64 return '<p>' . t('RDF data can be serialized into a number of textual formats (also known as representations). The two built-in, always available formats are <a href="http://drupal.org/node/219870">RDF/PHP</a> and <a href="http://drupal.org/node/219874">RDF/JSON</a>. For interoperability with more RDF formats, you can <a href="@status">install the ARC2 library</a> which adds parsing/serialization support for several widespread formats.', array('@status' => url('admin/reports/status'))) . '</p>';
65 case 'admin/settings/rdf/mappings':
66 return '<p>' . t('This is a summary of the currently defined mappings between Drupal data and RDF classes/properties.') . '</p>';
67 case 'admin/settings/rdf/feeds':
68 return '<p>' . t('This is a list of the <a href="http://web.resource.org/rss/1.0/">RSS 1.0</a>-compatible RDF feeds available on this Drupal site.') . '</p>';
69 case 'admin/settings/rdf/feeds/edit/%':
70 return '<p>' . t('') . '</p>'; // TODO
71 case 'admin/settings/rdf/namespaces':
72 return '<p>' . t('<a href="http://drupal.org/node/219858#namespaces">Namespaces</a> define URI abbreviations for use in <a href="http://drupal.org/node/219856#curie" title="Compact URIs">CURIEs</a> and for purposes of human-friendly display of RDF data.') . '</p>';
73 case 'admin/settings/rdf/contexts':
74 return '<p>' . t('<a href="http://drupal.org/node/219858#contexts">Contexts</a>, also known as <a href="http://www.w3.org/2004/03/trix/">named graphs</a>, provide a mechanism for grouping RDF statements (e.g. by their provenance), facilitating mass operations on a set of statements.') . '</p>';
75 case 'admin/settings/rdf/repositories':
76 return '<p>' . t('<a href="http://drupal.org/node/219858#repositories">Repositories</a> are storage containers for RDF data, and can be implemented, for instance, in terms of an in-memory triple store, a serialized file on disk, an RDBMS database, or an RPC connection to a remote service.') . '</p>';
77 case 'admin/settings/rdf/repositories/rdf/add':
78 return '<p>' . t('To create a new local RDF repository, enter the human-readable name, the machine-readable name, and all other relevant fields that are on this page.') . '</p>';
79 }
80 }
81
82 /**
83 * Implementation of hook_perm().
84 */
85 function rdf_perm() {
86 return array(
87 'access RDF data',
88 'administer RDF data',
89 'administer RDF repositories',
90 'import RDF data',
91 'export RDF data',
92 'export site settings',
93 'import site settings',
94 'export enabled modules',
95 'import enabled modules',
96 );
97 }
98
99 /**
100 * Implementation of hook_menu().
101 */
102 function rdf_menu() {
103 module_load_include('inc', 'rdf', 'rdf.menu');
104 return rdf_menu_build();
105 }
106
107 /**
108 * Implementation of hook_menu_alter().
109 */
110 function rdf_menu_alter(&$items) {
111 // Override the RSS feeds output by Drupal's core modules:
112 foreach (rdf_rdf_feeds() as $feed_id => $feed) {
113 if (($feed = (object)$feed) && !empty($feed->enabled) && module_exists($feed->module)) {
114 $path = isset($feed->menu) ? $feed->menu : $feed->path;
115
116 if (isset($items[$path])) {
117 $arguments = empty($items[$path]['page arguments']) ? array($feed_id) : array_merge(array($feed_id), $items[$path]['page arguments']);
118 $items[$path]['page callback'] = 'rdf_feed_callback';
119 $items[$path]['page arguments'] = $arguments;
120 $items[$path]['module'] = 'rdf';
121 $items[$path]['file'] = 'rdf.pages.inc';
122 }
123 }
124 }
125 }
126
127 /**
128 * Implementation of hook_hook_info()
129 */
130 function rdf_hook_info() {
131 return array(
132 'rdf' => array(
133 'rdf' => array(
134 'insert' => array(
135 'runs when' => t('After inserting a new RDF statement'),
136 ),
137 'update' => array(
138 'runs when' => t('After deleting an existing RDF statement'),
139 ),
140 ),
141 ),
142 );
143 }
144
145 /**
146 * Implementation of hook_theme()
147 */
148 function rdf_theme() {
149 return array(
150 'rdf_property_table' => array(
151 'arguments' => array('data' => NULL),
152 'file' => 'rdf.theme.inc',
153 ),
154 'rdf_triple_table' => array(
155 'arguments' => array('data' => NULL),
156 'file' => 'rdf.theme.inc',
157 ),
158 'rdf_triple_row' => array(
159 'arguments' => array('subject' => NULL, 'predicate' => NULL, 'object' => NULL),
160 'file' => 'rdf.theme.inc',
161 ),
162 'rdf_triple_cell' => array(
163 'arguments' => array('value' => NULL),
164 'file' => 'rdf.theme.inc',
165 ),
166 'rdf_value' => array(
167 'arguments' => array('value' => NULL),
168 'file' => 'rdf.theme.inc',
169 ),
170 'rdf_admin_settings' => array(
171 'arguments' => array('form' => NULL),
172 'file' => 'rdf.admin.inc',
173 ),
174 'rdf_admin_data' => array(
175 'arguments' => array('form' => NULL),
176 'file' => 'rdf.admin.inc',
177 ),
178 );
179 }
180
181 /**
182 * Implementation of hook_cron().
183 */
184 function rdf_cron() {
185 // TODO: Run CHECK TABLE on {rdf_data_*}
186
187 // Merge duplicate statements in the {rdf_data_*} tables:
188 if (variable_get('rdf_db_merge_duplicates', FALSE)) {
189 module_load_include('inc', 'rdf', 'adapters/db');
190 foreach (rdf_get_tables() as $table) {
191 $repo = new RDF_DatabaseRepository(compact('table')); // FIXME
192 $repo->merge_duplicates();
193 }
194 }
195
196 // Purge unused URLs from the {rdf_resources} table:
197 if (variable_get('rdf_db_purge_resources', FALSE)) {
198 // TODO
199 }
200 }
201
202 /**
203 * Implementation of hook_user().
204 */
205 function rdf_user($op, &$edit, &$account, $category = NULL) {
206 switch ($op) {
207 case 'view':
208 if (user_access('access RDF data')) {
209 rdf_add_autodiscovery_link(t('RDF'), url('user/' . $account->uid . '/rdf'));
210 }
211 break;
212 }
213 }
214
215 /**
216 * Implementation of hook_nodeapi().
217 */
218 function rdf_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
219 switch ($op) {
220 case 'load':
221 case 'prepare':
222 $node->rdf = isset($node->rdf) ? $node->rdf : array(); // used e.g. by rdf_feedapi_mapper()
223 break;
224
225 case 'insert':
226 case 'update':
227 // This is a mechanism for overcoming the limitation that when working
228 // with a new $node object that hasn't yet been stored in the
229 // database, and hence doesn't have a $node->nid, it isn't possible to
230 // stores triples relating to the node; a lack of $node->nid implies a
231 // lack of an URI for the node. However, by populating $node->rdf with
232 // properties and values, we'll here take care of inserting triples,
233 // using the node's URI as subject, once the node has been created and
234 // $node->nid is available. For convenience and consistency, we
235 // provide the same facility for node updates, too.
236 //
237 // For example:
238 // $node->rdf['dc:origin'] = rdf_uri("http://...");
239 // ...will result in the insertion of a triple of:
240 // array('node/' . $node->nid, 'dc:origin', rdf_uri("http://..."));
241 //
242 if (isset($node->nid) && !empty($node->rdf)) {
243 $uri = url('node/' . $node->nid, array('absolute' => TRUE, 'alias' => TRUE));
244 rdf_insert_all(rdf_denormalize(array($uri => $node->rdf)), !empty($node->rdf_options) ? $node->rdf_options : array());
245 $node->rdf = array();
246 }
247 break;
248
249 case 'view':
250 // Add an RDF autodiscovery link when the node is displayed by itself as a page:
251 if (user_access('access RDF data') && !empty($a4)) {
252 rdf_add_autodiscovery_link(t('RDF'), url('node/' . $node->nid . '/rdf'));
253 }
254 break;
255 }
256 }
257
258 /**
259 * Implementation of hook_taxonomy().
260 */
261 function rdf_taxonomy($op, $type, $array = NULL) {
262 if ($type == 'vocabulary') {
263 switch ($op) {
264 case 'insert':
265 case 'update':
266 if (!empty($array['rdf_property'])) {
267 variable_set('rdf_schema_property_vocabulary_'. $array['vid'], rdf_qname_to_uri($array['rdf_property']));
268 }
269 else {
270 variable_del('rdf_schema_property_vocabulary_'. $array['vid']);
271 }
272 break;
273 case 'delete':
274 variable_del('rdf_schema_property_vocabulary_'. $array['vid']);
275 break;
276 }
277 }
278 }
279
280 /**
281 * Implementation of hook_form_alter().
282 */
283 function rdf_form_alter(&$form, $form_state, $form_id) {
284 switch ($form_id) {
285 // Administer >> User management >> Profiles >> Edit
286 case 'profile_field_form':
287 // The submitted value is stored using the callback function defined below.
288 $rdf_mapping = isset($form['fid']['#value']) ? variable_get('rdf_schema_property_profile_'. $form['fid']['#value'], '') : '';
289 $form['rdf_mapping'] = array('#type' => 'fieldset', '#title' => t('RDF mapping'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 1);
290 $form['rdf_mapping']['rdf_property'] = array(
291 '#type' => 'textfield',
292 '#title' => t('RDF property'),
293 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
294 '#maxlength' => 255,
295 '#description' => t(''), // TODO
296 '#autocomplete_path' => 'rdf/autocomplete/property',
297 );
298 $form['submit']['#weight'] = 40;
299 $form['#validate'][] = 'rdf_schema_property_form_validate';
300 $form['#submit'][] = 'rdf_schema_profile_field_form_submit';
301 break;
302
303 // Administer >> Content management >> Taxonomy >> Edit vocabulary
304 case 'taxonomy_form_vocabulary':
305 // The submitted value is stored in our implementation of hook_taxonomy()
306 $rdf_mapping = isset($form['vid']['#value']) ? variable_get('rdf_schema_property_vocabulary_'. $form['vid']['#value'], '') : '';
307 $form['rdf_mapping'] = array('#type' => 'fieldset', '#title' => t('RDF mapping'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 1);
308 $form['rdf_mapping']['rdf_property'] = array(
309 '#type' => 'textfield',
310 '#title' => t('RDF property'),
311 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
312 '#maxlength' => 255,
313 '#description' => t(''), // TODO
314 '#autocomplete_path' => 'rdf/autocomplete/property',
315 );
316 $form['submit']['#weight'] = 40;
317 $form['delete']['#weight'] = 45;
318 $form['#validate'][] = 'rdf_schema_property_form_validate';
319 break;
320
321 // Administer >> Content management >> Content types >> Edit
322 case 'node_type_form':
323 // The submitted value is auto-saved by node.module as a configuration variable with a name in the form 'rdf_schema_class_TYPE'
324 $rdf_mapping = variable_get('rdf_schema_class_'. $form['#node_type']->type, '');
325 $form['rdf_mapping'] = array('#type' => 'fieldset', '#title' => t('RDF mapping'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 1);
326 $form['rdf_mapping']['rdf_schema_class'] = array(
327 '#type' => 'textfield',
328 '#title' => t('RDF class'),
329 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
330 '#maxlength' => 255,
331 '#description' => t(''), // TODO
332 '#autocomplete_path' => 'rdf/autocomplete/class',
333 );
334 $form['#validate'][] = 'rdf_schema_class_form_validate';
335 break;
336
337 // Administer >> Content management >> Content types >> (content type) >> Manage fields >> Configure
338 case 'content_field_edit_form':
339 // The submitted value is stored using the callback function defined below.
340 $rdf_mapping = variable_get('rdf_schema_property_content_' . $form['field_name']['#value'], '');
341 $form['rdf_mapping'] = array('#type' => 'fieldset', '#title' => t('RDF mapping'), '#collapsible' => FALSE, '#collapsed' => FALSE, '#weight' => 1);
342
343 // Ordinary CCK fields
344 if (empty($form['#field']['todate'])) {
345 $form['rdf_mapping']['rdf_property'] = array(
346 '#type' => 'textfield',
347 '#title' => t('RDF property'),
348 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
349 '#maxlength' => 255,
350 '#description' => t(''), // TODO
351 '#autocomplete_path' => 'rdf/autocomplete/property',
352 );
353 }
354 // HACK: support CCK Date fields that have both a start and an end date:
355 else { // $form['#field']['todate'] == 'optional' | 'required'
356 // From date
357 $rdf_mapping = variable_get('rdf_schema_property_content_' . $form['field_name']['#value'] . '[from]', $rdf_mapping);
358 $form['rdf_mapping']['rdf_property_from'] = array(
359 '#type' => 'textfield',
360 '#title' => t('RDF property (from date)'),
361 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
362 '#maxlength' => 255,
363 '#description' => t(''), // TODO
364 '#autocomplete_path' => 'rdf/autocomplete/property',
365 );
366 // To date
367 $rdf_mapping = variable_get('rdf_schema_property_content_' . $form['field_name']['#value'] . '[to]', '');
368 $form['rdf_mapping']['rdf_property_to'] = array(
369 '#type' => 'textfield',
370 '#title' => t('RDF property (to date)'),
371 '#default_value' => rdf_uri_to_qname($rdf_mapping, FALSE, $rdf_mapping),
372 '#maxlength' => 255,
373 '#description' => t(''), // TODO
374 '#autocomplete_path' => 'rdf/autocomplete/property',
375 );
376 }
377
378 $form['submit']['#weight'] = 90;
379 $form['#validate'][] = 'rdf_schema_property_form_validate';
380 $form['#submit'][] = 'rdf_schema_content_field_form_submit';
381 break;
382 }
383 }
384
385 //////////////////////////////////////////////////////////////////////////////
386 // Forms API callbacks
387
388 /**
389 * @see rdf_form_alter()
390 */
391 function rdf_schema_class_form_validate($form, &$form_state) {
392 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
393 $rdf_schema_class = trim($rdf_schema_class);
394
395 if ($rdf_schema_class != '' && !rdf_is_valid_uri($rdf_schema_class) && !rdf_is_valid_curie($rdf_schema_class)) {
396 form_set_error('rdf_schema_class', t('RDF class is not a valid URI or CURIE.'));
397 }
398 else {
399 $rdf_schema_class = rdf_qname_to_uri($rdf_schema_class);
400 }
401 }
402
403 /**
404 * @see rdf_form_alter()
405 */
406 function rdf_schema_property_form_validate($form, &$form_state) {
407 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
408
409 if (isset($rdf_property)) {
410 if (($rdf_property = trim($rdf_property)) != '' && !rdf_is_valid_uri($rdf_property) &&
411 !(rdf_is_valid_curie($rdf_property) && rdf_is_valid_prefix($rdf_property))) {
412 form_set_error('rdf_property', t('RDF property is not a valid URI or CURIE.'));
413 }
414 }
415 // HACK: support CCK Date fields that have both a start and an end date:
416 else if (isset($rdf_property_from, $rdf_property_to)) {
417 foreach (array('rdf_property_from', 'rdf_property_to') as $rdf_property) {
418 if (($$rdf_property = trim($$rdf_property)) != '' && !rdf_is_valid_uri($$rdf_property) &&
419 !(rdf_is_valid_curie($$rdf_property) && rdf_is_valid_prefix($$rdf_property))) {
420 form_set_error($rdf_property, t('RDF property is not a valid URI or CURIE.'));
421 }
422 }
423 }
424 }
425
426 /**
427 * @see rdf_form_alter()
428 */
429 function rdf_schema_profile_field_form_submit($form, &$form_state) {
430 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
431
432 if (!empty($rdf_property)) {
433 variable_set('rdf_schema_property_profile_' . $fid, rdf_qname_to_uri($rdf_property));
434 }
435 else {
436 variable_del('rdf_schema_property_profile_' . $fid);
437 }
438 }
439
440 /**
441 * @see rdf_form_alter()
442 */
443 function rdf_schema_content_field_form_submit($form, &$form_state) {
444 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
445
446 // HACK: support CCK Date fields that have both a start and an end date:
447 $rdf_properties = isset($rdf_property) ?
448 array('rdf_property' => '') :
449 array('rdf_property_from' => '[from]', 'rdf_property_to' => '[to]');
450
451 foreach ($rdf_properties as $rdf_property_var => $field_suffix) {
452 if (!empty($$rdf_property_var)) {
453 variable_set('rdf_schema_property_content_' . $field_name . $field_suffix, rdf_qname_to_uri($$rdf_property_var));
454 }
455 else {
456 variable_del('rdf_schema_property_content_' . $field_name . $field_suffix);
457 }
458 }
459 }
460
461 //////////////////////////////////////////////////////////////////////////////
462 // Views API hooks
463
464 /**
465 * Implementation of hook_views_api().
466 */
467 function rdf_views_api() {
468 return array(
469 'api' => 2,
470 'path' => drupal_get_path('module', 'rdf'),
471 );
472 }
473
474 //////////////////////////////////////////////////////////////////////////////
475 // RDF API hooks
476
477 /**
478 * Implementation of hook_rdf_feeds().
479 */
480 function rdf_rdf_feeds() {
481 $enabled = variable_get('rdf_feed_override', array());
482
483 // Declare all RSS feeds provided by Drupal's core modules:
484 $feeds = array(
485 // node.module (front page feed)
486 'node_feed' => array(
487 'type' => RDF_RSS_URI,
488 'path' => 'rss.xml',
489 'title' => t('Front page feed'),
490 'module' => 'node',
491 'callback' => 'rdf_feed_node_frontpage',
492 'enabled' => !empty($enabled['node_feed']),
493 ),
494 // taxonomy.module
495 'taxonomy_term_feed' => array(
496 'type' => RDF_RSS_URI,
497 'path' => 'taxonomy/term/%/0/feed',
498 'menu' => 'taxonomy/term/%',
499 'title' => t('Taxonomy term feed'),
500 'module' => 'taxonomy',
501 'callback' => 'rdf_feed_taxonomy_term',
502 'enabled' => !empty($enabled['taxonomy_term_feed']),
503 ),
504 // blog.module
505 'blog_feed_user' => array(
506 'type' => RDF_RSS_URI,
507 'path' => 'blog/%user/feed',
508 'title' => 'Users\' recent blog entries feed',
509 'module' => 'blog',
510 'callback' => 'rdf_feed_blog_user',
511 'enabled' => !empty($enabled['blog_feed_user']),
512 ),
513 'blog_feed_last' => array(
514 'type' => RDF_RSS_URI,
515 'path' => 'blog/feed',
516 'title' => 'All recent blog entries feed',
517 'module' => 'blog',
518 'callback' => 'rdf_feed_blog_last',
519 'enabled' => !empty($enabled['blog_feed_last']),
520 ),
521 // aggregator.module
522 'aggregator_page_rss' => array(
523 'type' => RDF_RSS_URI,
524 'path' => 'aggregator/rss',
525 'title' => t('Aggregator feed'),
526 'module' => 'aggregator',
527 'callback' => 'rdf_feed_aggregator_rss',
528 'enabled' => !empty($enabled['aggregator_page_rss']),
529 ),
530 );
531
532 // Declare all Views module feeds, if the module is installed:
533 if (module_exists('views')) {
534 module_load_include('inc', 'rdf', 'rdf.views');
535 $feeds = array_merge($feeds, rdf_views_get_feeds());
536 }
537
538 return $feeds;
539 }
540
541 /**
542 * Implementation of hook_rdf_formats().
543 */
544 function rdf_rdf_formats() {
545 return array(
546 'rdf+php' => array(
547 'title' => t('RDF/PHP'),
548 'link' => 'http://drupal.org/node/219870',
549 'mime_type' => 'application/vnd.php.serialized',
550 'encoding' => 'ascii',
551 'file_ext' => 'txt',
552 'serialize' => 'rdf_serialize_php',
553 'unserialize' => 'rdf_unserialize_php',
554 ),
555 'rdf+json' => array(
556 'title' => t('RDF/JSON'),
557 'link' => 'http://drupal.org/node/219874',
558 'mime_type' => 'application/json',
559 'encoding' => 'utf-8',
560 'file_ext' => 'js',
561 'serialize' => 'rdf_serialize_json',
562 'unserialize' => 'rdf_unserialize_json',
563 ),
564 'rdf+xml' => array(
565 'title' => t('RDF/XML'),
566 'link' => 'http://www.w3.org/TR/rdf-syntax-grammar/',
567 'mime_type' => 'application/rdf+xml',
568 'encoding' => 'utf-8',
569 'file_ext' => 'rdf',
570 'serialize' => 'rdf_serialize_xml',
571 'unserialize' => class_exists('ARC2') ? 'rdf_unserialize_xml' : NULL,
572 'file' => 'formats/rdfxml.inc'
573 ),
574 'trix' => array(
575 'title' => t('TriX'),
576 'link' => 'http://www.w3.org/2004/03/trix/',
577 'mime_type' => 'application/trix',
578 'encoding' => 'utf-8',
579 'file_ext' => 'xml',
580 'serialize' => 'rdf_serialize_trix',
581 'unserialize' => NULL,
582 'file' => 'formats/trix.inc'
583 ),
584 'turtle' => array(
585 'title' => t('Turtle'),
586 'link' => 'http://www.dajobe.org/2004/01/turtle/',
587 'mime_type' => 'application/x-turtle',
588 'encoding' => 'utf-8',
589 'file_ext' => 'ttl',
590 'serialize' => 'rdf_serialize_turtle',
591 'unserialize' => class_exists('ARC2') ? 'rdf_unserialize_turtle' : NULL,
592 'file' => 'formats/turtle.inc'
593 ),
594 'ntriples' => array(
595 'title' => t('N-Triples'),
596 'link' => 'http://www.w3.org/TR/rdf-testcases/#ntriples',
597 'mime_type' => 'text/plain', // TODO: any semi-standardized alternative?
598 'encoding' => 'ascii',
599 'file_ext' => 'nt',
600 'serialize' => 'rdf_serialize_ntriples',
601 'unserialize' => class_exists('ARC2') ? 'rdf_unserialize_ntriples' : NULL,
602 'file' => 'formats/ntriples.inc'
603 ),
604 );
605 }
606
607 /**
608 * Implementation of hook_rdf_namespaces().
609 */
610 function rdf_rdf_namespaces() {
611 $namespaces = array(
612 '_' => 'http://bnode.net/',
613 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
614 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
615 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance#',
616 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
617 'owl' => 'http://www.w3.org/2002/07/owl#',
618 'dc' => 'http://purl.org/dc/elements/1.1/',
619 'dcterms' => 'http://purl.org/dc/terms/',
620 'dcmitype' => 'http://purl.org/dc/dcmitype/',
621 'foaf' => 'http://xmlns.com/foaf/0.1/',
622 'rss' => RDF_RSS_URI,
623 'drupal' => RDF_SCHEMA_URI,
624 );
625
626 foreach (rdf_schema_get_entities() as $entity) {
627 $namespaces[$entity] = RDF_SCHEMA_URI . $entity .'#';
628 }
629
630 $result = db_query('SELECT v.prefix, v.uri FROM {rdf_namespaces} v');
631 while ($row = db_fetch_object($result)) {
632 $namespaces[$row->prefix] = $row->uri;
633 }
634
635 return $namespaces;
636 }
637
638 /**
639 * Implementation of hook_rdf_contexts().
640 */
641 function rdf_rdf_contexts() {
642 $contexts = array();
643
644 foreach (rdf_get_tables() as $name => $table) {
645 $result = db_query('SELECT DISTINCT g.uri g FROM {' . db_escape_table($table) . '} d INNER JOIN {rdf_resources} g ON d.gid = g.rid');
646 while ($row = db_fetch_object($result)) {
647 $contexts[$row->g] = TRUE;
648 }
649 }
650
651 return array_merge(array(RDF_SITE_URI, RDF_SCHEMA_URI), array_keys($contexts));
652 }
653
654 /**
655 * Implementation of hook_rdf_adapters().
656 */
657 function rdf_rdf_adapters() {
658 return array(
659 'db' => array(
660 'title' => t('Database'),
661 'description' => t('Stores RDF data in a Drupal database table.'),
662 'enabled' => TRUE,
663 'mutable' => TRUE,
664 'file' => drupal_get_path('module', 'rdf') . '/adapters/db.inc',
665 'class' => 'RDF_DatabaseRepository',
666 ),
667 'file' => array(
668 'title' => t('File'),
669 'description' => t('Reads serialized RDF data from a file or URL.'),
670 'enabled' => TRUE,
671 'mutable' => FALSE,
672 'file' => drupal_get_path('module', 'rdf') . '/adapters/file.inc',
673 'class' => 'RDF_FileRepository',
674 ),
675 // TODO: ARC2 adapter
676 );
677 }
678
679 /**
680 * Implementation of hook_rdf_repositories().
681 */
682 function rdf_rdf_repositories() {
683 module_load_include('inc', 'rdf', 'adapters/db');
684
685 $repos = array(
686 'system' => array(
687 'title' => t('System'),
688 'type' => 'system',
689 'persistent' => TRUE,
690 'mutable' => FALSE,
691 'enabled' => TRUE,
692 'statements' => NULL,
693 'module' => 'rdf',
694 'callbacks' => array(
695 'query' => array('function' => 'rdf_rdf_query', 'arguments' => array()),
696 ),
697 'filters' => array(
698 'subject' => RDF_SITE_URI,
699 ),
700 ),
701 );
702
703 foreach (rdf_get_tables() as $name => $table) {
704 $repo = new RDF_DatabaseRepository(compact('name', 'table'));
705 $repos[$name] = array(
706 'title' => rdf_get_repository($name)->title,
707 'type' => 'local',
708 'persistent' => TRUE,
709 'mutable' => TRUE,
710 'enabled' => TRUE,
711 'statements' => $repo->count(),
712 'module' => 'rdf',
713 'callbacks' => array(
714 'insert' => array('function' => array($repo, 'insert'), 'arguments' => array()),
715 'delete' => array('function' => array($repo, 'delete'), 'arguments' => array()),
716 'query' => array('function' => array($repo, 'query'), 'arguments' => array()),
717 'count' => array('function' => array($repo, 'count'), 'arguments' => array()),
718 'flush' => array('function' => array($repo, 'flush'), 'arguments' => array()),
719 ),
720 );
721 }
722
723 return $repos;
724 }
725
726 /**
727 * Implementation of hook_rdf_classes().
728 */
729 function rdf_rdf_classes() {
730 return array(
731 'user' => array(
732 'title' => t('User'),
733 'module' => 'user',
734 'table' => 'users',
735 'query' => 'SELECT uid FROM {users} WHERE uid > 0',
736 'uri' => 'user/%uid',
737 'enabled' => TRUE,
738 ),
739 'vocabulary' => array(
740 'title' => t('Taxonomy vocabulary'),
741 'module' => 'taxonomy',
742 'table' => 'vocabulary',
743 'query' => 'SELECT vid FROM {vocabulary}',
744 // NOTE: Drupal vocabularies don't actually have dereferenceable URIs
745 'uri' => 'taxonomy/vocabulary/%vid',
746 'enabled' => FALSE, // TODO
747 ),
748 'term' => array(
749 'title' => t('Taxonomy term'),
750 'module' => 'taxonomy',
751 'table' => 'term_data',
752 'query' => 'SELECT tid FROM {term_data}',
753 'uri' => 'taxonomy/term/%tid',
754 'enabled' => FALSE, // TODO
755 ),
756 'node' => array(
757 'title' => t('Node'),
758 'module' => 'node',
759 'table' => 'node',
760 'query' => 'SELECT nid FROM {node}',
761 'uri' => 'node/%nid',
762 'load' => 'node_load',
763 'enabled' => TRUE,
764 ),
765 'comment' => array(
766 'title' => t('Comment'),
767 'module' => 'comment',
768 'table' => 'comments',
769 'query' => 'SELECT nid, cid FROM {comments}',
770 'uri' => 'node/%nid#comment-%cid',
771 'enabled' => FALSE, // TODO
772 ),
773 'path' => array(
774 'title' => t('URL alias'),
775 'module' => 'path',
776 'table' => 'url_alias',
777 'query' => 'SELECT dst FROM {url_alias}',
778 'uri' => '%dst',
779 'enabled' => FALSE, // TODO
780 ),
781 'variable' => array(
782 'title' => t('Setting'),
783 'module ' => 'system',
784 'table' => 'variable',
785 'query' => 'SELECT name FROM {variable}',
786 'uri' => 'rdf/variable/%name',
787 'enabled' => FALSE, // TODO
788 ),
789 );
790 }
791
792 /**
793 * Implementation of hook_rdf_properties().
794 */
795 function rdf_rdf_properties() {
796 return array(
797 'rdf' => array('first', 'object', 'predicate', 'rest', 'subject', 'type', 'value'),
798 'rdfs' => array('comment', 'domain', 'isDefinedBy', 'label', 'member', 'range', 'seeAlso', 'subClassOf', 'subPropertyOf'),
799 'xsd' => array('base64Binary', 'boolean', 'byte', 'date', 'dateTime', 'decimal', 'double', 'duration', 'float', 'hexBinary', 'int', 'integer', 'language', 'long', 'short', 'string', 'time', 'token'),
800 'owl' => array('allValuesFrom', 'backwardCompatibleWith', 'cardinality', 'complementOf', 'differentFrom', 'disjointWith', 'distinctMembers', 'equivalentClass', 'equivalentProperty', 'hasValue', 'imports', 'incompatibleWith', 'intersectionOf', 'inverseOf', 'maxCardinality', 'minCardinality', 'oneOf', 'onProperty', 'priorVersion', 'sameAs', 'someValuesFrom', 'unionOf', 'versionInfo'),
801 'dc' => array('contributor', 'coverage', 'creator', 'date', 'description', 'format', 'identifier', 'language', 'publisher', 'relation', 'rights', 'source', 'subject', 'title', 'type'),
802 'dcterms' => array('abstract_', 'accessRights', 'accrualMethod', 'accrualPeriodicity', 'accrualPolicy', 'alternative', 'audience', 'available', 'bibliographicCitation', 'conformsTo', 'contributor', 'coverage', 'created', 'creator', 'date', 'dateAccepted', 'dateCopyrighted', 'dateSubmitted', 'description', 'educationLevel', 'extent', 'format', 'hasFormat', 'hasPart', 'hasVersion', 'identifier', 'instructionalMethod', 'isFormatOf', 'isPartOf', 'isReferencedBy', 'isReplacedBy', 'isRequiredBy', 'issued', 'isVersionOf', 'language', 'license', 'mediator', 'medium', 'modified', 'provenance', 'publisher', 'references', 'relation', 'replaces', 'requires', 'rights', 'rightsHolder', 'source', 'spatial', 'subject', 'tableOfContents', 'temporal', 'title', 'type', 'valid'),
803 'dcmitype' => array(),
804 'foaf' => array(/*stable:*/'homepage', 'made', 'maker', 'mbox', 'member', /*testing:*/'depiction', 'depicts', 'family_name', 'firstName', 'gender', 'givenname', 'img', 'interest', 'isPrimaryTopicOf', 'knows', 'logo', 'mbox_sha1sum', 'name', 'nick', 'page', 'phone', 'primaryTopic', 'surname', 'thumbnail', 'title', 'topic', 'weblog'),
805 );
806 }
807
808 /**
809 * Implementation of hook_rdf_resources().
810 */
811 function rdf_rdf_resources($context) {
812 switch ($context) {
813 case NULL:
814 return array(); // FIXME
815 //return array_merge(rdf_schema_get_resources(), rdf_schema_get_classes());
816 case RDF_SITE_URI:
817 return array(RDF_SITE_URI => new RDF_QueryCallback('rdf_load_site'));
818 //return rdf_schema_get_resources(); // rdf_schema.module
819 case RDF_SCHEMA_URI:
820 return rdf_schema_get_classes();
821 }
822 }
823
824 /**
825 * Implementation of hook_rdf_query().
826 */
827 function rdf_rdf_query($subject, $predicate, $object, $options = array()) {
828 $context = isset($options['context']) ? $options['context'] : NULL;
829
830 $data = array();
831 foreach (module_implements('rdf_resources') as $module) {
832 $function = $module . '_rdf_resources';
833 if ($resources = $function($context)) {
834 foreach ($resources as $uri => $callback) {
835 if (!$subject || (string)$subject == $uri) {
836 $data = array_merge_recursive($data, _rdf_filter(array($uri => rdf_expand_qnames($callback->call())), $subject, $predicate, $object, $options));
837 }
838 }
839 }
840 }
841 return rdf_denormalize($data);
842 }
843
844 //////////////////////////////////////////////////////////////////////////////
845 // RDF storage adapter API
846
847 /**
848 * Returns information on the available storage adapters (repository backends).
849 */
850 function rdf_get_adapters($op = NULL, $enabled_only = FALSE) {
851 static $adapters = array();
852 if (empty($adapters)) {
853 foreach (module_implements('rdf_adapters') as $module) {
854 foreach (module_invoke($module, 'rdf_adapters') as $name => $info) {
855 $info['name'] = $name;
856 $info['module'] = $module;
857 $info['enabled'] = isset($info['enabled']) ? (bool)$info['enabled'] : TRUE;
858 $adapters[$name] = (object)$info;
859 }
860 }
861 }
862
863 $result = array();
864 foreach ($adapters as $name => $info) {
865 if (!$enabled_only || !empty($info->enabled)) {
866 switch ($op) {
867 case 'titles': $result[$name] = $info->title; break;
868 default: $result[$name] = $info; break;
869 }
870 }
871 }
872 return $result;
873 }
874
875 //////////////////////////////////////////////////////////////////////////////
876 // RDF repository API
877
878 function rdf_get_schema($table = RDF_DB_TABLE_DEFAULT, $rebuild = FALSE) {
879 if (!($schema = drupal_get_schema($table, $rebuild))) {
880 module_load_include('install', 'rdf');
881 $rdf_schema = rdf_schema();
882 $schema = isset($rdf_schema[$table]) ? $rdf_schema[$table] : array();
883 }
884 return $schema;
885 }
886
887 function rdf_get_tables($refresh = FALSE) {
888 static $tables;
889 if (!is_array($tables) || $refresh) {
890 $result = db_query("SELECT name FROM {rdf_repositories} WHERE module = 'rdf' AND type = 'local' AND name != 'local' ORDER BY weight ASC");
891 $tables = array('local' => RDF_DB_TABLE_DEFAULT);
892 while ($row = db_fetch_object($result)) {
893 if (db_table_exists($table = RDF_DB_TABLE_PREFIX . $row->name)) {
894 $tables[$row->name] = $table;
895 }
896 }
897 }
898 return $tables;
899 }
900
901 function rdf_get_repository($name) {
902 static $repos = array();
903 if (!isset($repos[$name]) && ($row = db_fetch_array(db_query("SELECT * FROM {rdf_repositories} WHERE name = '%s'", $name)))) {
904 $row = (object)array_merge($row, unserialize($row['options']), $row); // $row intentionally doubled, for key ordering & safety
905 unset($row->options);
906 $repos[$name] = $row;
907 }
908 return isset($repos[$name]) ? $repos[$name] : NULL;
909 }
910
911 function rdf_create_repository($name, array $options = array()) {
912 $schema = rdf_get_schema(RDF_DB_TABLE_DEFAULT);
913 db_create_table($results, db_escape_table(RDF_DB_TABLE_PREFIX . $name), $schema);
914
915 $options = !empty($options) ? $options : array('title' => $name, 'description' => '');
916 foreach (array('dc:title' => 'title', 'dc:description' => 'description') as $old_key => $new_key) {
917 if (isset($options[$old_key])) {
918 $options[$new_key] = $options[$old_key];
919 unset($options[$old_key]);
920 }
921 }
922 drupal_write_record('rdf_repositories', $record = (object)array(
923 'name' => $name,
924 'module' => 'rdf',
925 'type' => 'local',
926 'enabled' => TRUE,
927 'mutable' => TRUE,
928 'weight' => 0,
929 'options' => serialize($options),
930 ));
931
932 rdf_get_schema(db_escape_table(RDF_DB_TABLE_PREFIX . $name), TRUE); // clear the schema cache
933 return $results[0]['success'];
934 }
935
936 function rdf_update_repository($name, array $options = array()) {
937 db_query("UPDATE {rdf_repositories} SET options = '%s' WHERE name = '%s'", serialize($options), $name);
938 }
939
940 function rdf_rename_repository($old_name, $new_name) {
941 db_rename_table($results,
942 db_escape_table(RDF_DB_TABLE_PREFIX . $old_name),
943 db_escape_table(RDF_DB_TABLE_PREFIX . $new_name));
944 db_query("UPDATE {rdf_repositories} SET name = '%s' WHERE name = '%s'", $new_name, $old_name);
945 return $results[0]['success'];
946 }
947
948 function rdf_delete_repository($name) {
949 db_drop_table($results, db_escape_table(RDF_DB_TABLE_PREFIX . $name));
950 db_query("DELETE FROM {rdf_repositories} WHERE name = '%s'", $name);
951 return $results[0]['success'];
952 }
953
954 //////////////////////////////////////////////////////////////////////////////
955 // RDF namespace API
956
957 function rdf_get_namespace($prefix) {
958 return db_fetch_object(db_query("SELECT v.* FROM {rdf_namespaces} v WHERE v.prefix = '%s'", $prefix));
959 }
960
961 //////////////////////////////////////////////////////////////////////////////
962 // Miscellaneous helpers
963
964 function rdf_get_schema_uri() {
965 $uri = 'rdf/schema';
966 $uri = (function_exists('url') ? url($uri, array('absolute' => TRUE)) : $uri) . '/';
967 return variable_get('rdf_schema_uri', $uri);
968 }
969
970 function rdf_menu_access_user($account) {
971 return user_access('access RDF data') && user_view_access($account);
972 }
973
974 function rdf_menu_access_node($op, $node, $account = NULL) {
975 return user_access('access RDF data') && node_access($op, $node, $account);
976 }
977
978 function rdf_menu_access_http($perm_get, $perm_post) {
979 return user_access($_SERVER['REQUEST_METHOD'] == 'POST' ? $perm_post : $perm_get);
980 }
981
982 //////////////////////////////////////////////////////////////////////////////
983 // Output buffering callback
984
985 function _rdf_ob_handler($buffer) {
986 static $mimetypes = array('text/html');
987 static $doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML+RDFa 1.0//EN\"\n \"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd\">\n";
988
989 // Only attempt RDFa modifications for HTML output:
990 if (in_array($mimetype = _rdf_ob_handler_get_content_type(), $mimetypes)) {
991 $buffer = _rdf_ob_handler_remove_doctype($buffer, $doctype);
992 $buffer = _rdf_ob_handler_insert_namespaces($buffer);
993 $buffer = $doctype . $buffer;
994 }
995 return $buffer;
996 }
997
998 function _rdf_ob_handler_get_content_type($default = NULL) {
999 static $regex = '!^Content-Type:\s*([\w\d\/\-]+)!i';
1000 return _rdf_ob_handler_get_http_header($regex, $default);
1001 }
1002
1003 function _rdf_ob_handler_get_http_header($regex, $default = NULL) {
1004 // The last header is the one that counts:
1005 //$headers = preg_grep($regex, explode("\n", drupal_get_headers()));
1006 $headers = preg_grep($regex, headers_list());
1007 if (!empty($headers) && preg_match($regex, array_pop($headers), $matches)) {
1008 return $matches[1]; // found it
1009 }
1010 return $default; // no such luck
1011 }
1012
1013 function _rdf_ob_handler_remove_doctype($buffer, &$doctype) {
1014 $lines = $cutoff = 0;
1015 $line = strtok($buffer, "\n");
1016
1017 // Attempt to find the opening <html> tag in the first 15 lines:
1018 while ($line !== FALSE && $lines++ < 15) {
1019 if (($pos = strpos($line, '<html')) !== FALSE) {
1020 // If we managed to find <html>, substitute the DOCTYPE; otherwise, the
1021 // page output will be returned unmodified.
1022 return substr($buffer, $cutoff += $pos);
1023 }
1024
1025 $cutoff += strlen($line) + 1; // line length and "\n"
1026 $line = strtok("\n");
1027 }
1028
1029 $doctype = ''; // Don't prepend the new doctype
1030 return $buffer;
1031 }
1032
1033 function _rdf_ob_handler_insert_namespaces($buffer) {
1034 $namespaces = rdf_get_namespaces('rdfa');
1035
1036 // Attempt to find the last character of the opening <html> tag:
1037 if (!empty($namespaces) && ($html = strtok($buffer, '>'))) {
1038 $buffer = substr($buffer, strlen($html) + 1);
1039
1040 // Insert the xmlns:prefix="uri" definitions at the end of the opening <html> tag:
1041 $xmlns = array();
1042 foreach ($namespaces as $prefix => $uri) {
1043 $xmlns[] = ' xmlns:' . $prefix . '="' . $uri . '"';
1044 }
1045 $html .= "\n" . implode("\n", $xmlns) . '>';
1046 return $html . $buffer;
1047 }
1048 return $buffer;
1049 }

  ViewVC Help
Powered by ViewVC 1.1.2