comparison sites/all/modules/rdfx/rdfx.terms.inc @ 4:ce11bbd8f642

added modules
author danieleb <danielebarchiesi@me.com>
date Thu, 19 Sep 2013 10:38:44 +0100
parents
children
comparison
equal deleted inserted replaced
3:b28be78d8160 4:ce11bbd8f642
1 <?php
2
3 /**
4 * @file
5 * Functions for managing RDF Terms.
6 */
7
8 function rdfx_get_properties() {
9 $property_tids = _rdfx_get_terms('property');
10 return $property_tids;
11 }
12
13 function rdfx_get_classes() {
14 $class_tids = _rdfx_get_terms('class');
15 return $class_tids;
16 }
17
18 function _rdfx_get_terms($term_type) {
19 $term_types = rdfx_term_types();
20 switch ($term_type) {
21 case 'property':
22 $types = array_keys($term_types['properties']['term_types']);
23 break;
24 case 'class':
25 $types = array_keys($term_types['classes']['term_types']);
26 break;
27 default:
28 $types = array_merge(array_keys($term_types['properties']['term_types']), array_keys($term_types['classes']['term_types']));
29 }
30 $query = db_select('rdfx_term_types', 'rdftt')
31 ->fields('rdft', array('tid'))
32 ->condition('rdftt.type', $types, 'IN');
33 $query->join('rdfx_terms', 'rdft', 'rdftt.tid = rdft.tid');
34 $query->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid');
35 $query->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid');
36 $terms = $query->execute()->fetchCol();
37 return $terms;
38 }
39
40 /**
41 * Gets a list of all defined namespaces.
42 */
43 function rdfx_get_namespaces() {
44 $rdf_namespaces = &drupal_static(__FUNCTION__);
45 if (empty($rdf_namespaces)) {
46 $rdf_namespaces = rdf_get_namespaces();
47 }
48 return $rdf_namespaces;
49 }
50
51 /**
52 * Implements hook_rdf_namespaces.
53 */
54 function rdfx_rdf_namespaces() {
55 // Starts with some additionnal common namespaces which core does not include.
56 $ns_mappings = array(
57 'owl' => 'http://www.w3.org/2002/07/owl#',
58 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
59 'rss' => 'http://purl.org/rss/1.0/',
60 // url() does not support empty fragment.
61 'site' => url('ns', array('absolute' => TRUE)) . '#',
62 );
63
64 // Gets the custom namespaces stored in the database.
65 $query = db_select('rdfx_vocabulary_graphs', 'g');
66 $query->fields('n', array('prefix', 'uri'));
67 $query->join('rdfx_namespaces', 'n', 'g.main_ns = n.nsid');
68 $query->orderBy('n.prefix');
69 $namespaces = $query->execute()->fetchAllKeyed();
70 foreach ($namespaces as $prefix => $uri) {
71 $ns_mappings[$prefix] = $uri;
72 }
73
74 return $ns_mappings;
75 }
76
77 /**
78 * Saves the main namespace mapping for a vocabulary graph and the additional
79 * namespace mappings as defined in the document.
80 */
81 function _rdfx_save_vocabulary($main_ns, $main_ns_prefix, $vocabulary) {
82 $current_time = REQUEST_TIME;
83 // If the vocabulary URI matches the main_ns of a vocabulary source, then
84 // this is an update to that record. Otherwise, this is a newly imported
85 // source.
86 $gid = rdfx_get_gid($main_ns);
87
88 // If there is an existing vocabulary, make sure that the main_ns is in the
89 // namespaces array and that the user defined mapping is the last in the
90 // array so the prefix reflects the user definition. Also change the
91 // vocabulary graph updated date.
92 if ($gid) {
93 $vocabulary['namespaces'][$main_ns_prefix] = $main_ns;
94 db_update('rdfx_vocabulary_graphs')
95 ->fields(array('date_updated' => $current_time,))
96 ->execute();
97 }
98
99 // If this is a new vocabulary, create a graph with a main namespace and
100 // add the additional namespaces.
101 else {
102 // @todo If the vocab URI isn't used in any terms, don't add it to ns table.
103 // This may happen where multiple files are defining a vocabulary.
104
105 // @todo This should be handled as a transaction in case there is an error
106 // in the middle. If there is an error, then there will be an SQL error
107 // when the user retries the import.
108
109 // Insert this namespace to get the nsid. The vocabulary_source entry will
110 // point to this nsid for the main_ns. We temporarily insert 0 for the gid,
111 // then update when we have the real gid.
112 $nsid = db_insert('rdfx_namespaces')
113 ->fields(array('uri' => $main_ns, 'prefix' => $main_ns_prefix, 'gid' => '0'))
114 ->execute();
115 $gid = db_insert('rdfx_vocabulary_graphs')
116 ->fields(array(
117 'main_ns' => $nsid,
118 'date_created' => $current_time,
119 'date_updated' => $current_time,))
120 ->execute();
121 db_update('rdfx_namespaces')
122 ->condition('nsid', $nsid)
123 ->fields(array('gid' => $gid))
124 ->execute();
125 }
126 // Insert/update the vocabulary title.
127 if (count($vocabulary['title']) > 0) {
128 foreach ($vocabulary['title'] as $langcode => $title) {
129 $query = db_merge('rdfx_vocabulary_details')
130 ->key(array('gid' => $gid, 'language' => $langcode))
131 ->fields(array('language' => $langcode, 'label' => $title));
132 $status = $query->execute();
133 }
134 }
135
136 // Insert/update the vocabulary description.
137 if (count($vocabulary['description']) > 0) {
138 foreach ($vocabulary['description'] as $langcode => $description) {
139 $query = db_merge('rdfx_vocabulary_details')
140 ->key(array('gid' => $gid, 'language' => $langcode))
141 ->fields(array('language' => $langcode, 'description' => $description));
142 $status = $query->execute();
143 }
144 }
145
146 // Insert/update the other namespace mappings used in this vocabulary graph.
147 if (count($vocabulary['namespaces']) > 0) {
148 foreach ($vocabulary['namespaces'] as $prefix => $namespace) {
149 if ($namespace != $main_ns) {
150 $query = db_merge('rdfx_namespaces')
151 ->key(array('gid' => $gid, 'uri' => $namespace))
152 ->fields(array('uri' => $namespace, 'prefix' => $prefix, 'gid' => $gid))
153 ->updateFields(array('prefix' => $prefix));
154 $status = $query->execute();
155 }
156 }
157 }
158
159 $nsids = rdfx_get_nsids($main_ns);
160 return $nsids;
161 }
162
163 /**
164 * Saves vocabulary terms.
165 */
166 function rdfx_save_terms($vocabulary_uri, $prefix, $vocabulary) {
167 $nsids = _rdfx_save_vocabulary($vocabulary_uri, $prefix, $vocabulary);
168 foreach ($vocabulary['terms'] as $term_type => $terms) {
169 foreach ($terms as $term_uri => $term_description) {
170 list($term_ns, $term_local_name) = rdfx_split_uri($term_uri);
171 if (isset($nsids[$term_ns])) {
172 $nsid = $nsids[$term_ns];
173 }
174 else {
175 // If the namespace wasn't mapped to a prefix in the source graph, we
176 // didn't save it to the namespaces table, so we need to add an entry.
177
178 // @todo For the prefix value, we save the URI... should this be changed?
179 $gid = rdfx_get_gid($vocabulary_uri);
180 $nsid = db_insert('rdfx_namespaces')
181 ->fields(array('uri' => $term_ns, 'prefix' => $term_ns, 'gid' => $gid))
182 ->execute();
183 $nsids[$term_ns] = $nsid;
184 }
185
186 // Get the tid of this term, saving to {rdfx_terms} if not already there.
187 $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(':nsid' => $nsid, ':localname' => $term_local_name))->fetchField();
188 if ($tid == NULL) {
189 $tid = db_insert('rdfx_terms')
190 ->fields(array('nsid', 'local_name'))
191 ->values(array(
192 'nsid' => $nsid,
193 'local_name' => $term_local_name,
194 ))
195 ->execute();
196 }
197
198 // Add the current type to this term in {rdfx_term_types}.
199 db_merge('rdfx_term_types')
200 ->key(array('tid' => $tid, 'type' => $term_type))
201 ->fields(array(
202 'tid' => $tid,
203 'type' => $term_type,
204 ))
205 ->execute();
206
207 // Add label and comment to {rdfx_term_details}.
208 $term_details = array();
209 if (isset($term_description['label'])) {
210 foreach ($term_description['label'] as $lang => $text) {
211 $term_details[$lang]['label'] = $text;
212 }
213 }
214 if (isset($term_description['comment'])) {
215 foreach ($term_description['comment'] as $lang => $text) {
216 $term_details[$lang]['comment'] = $text;
217 }
218 }
219 if (!empty($term_details)) {
220 foreach ($term_details as $lang => $details) {
221 db_merge('rdfx_term_details')
222 ->key(array('tid' => $tid, 'language' => $lang))
223 ->fields(array(
224 'tid' => $tid,
225 'language' => $lang,
226 'label' => isset($details['label']) ? $details['label'] : NULL,
227 'comment' => isset($details['comment']) ? $details['comment'] : NULL,
228 ))
229 ->execute();
230 }
231 }
232
233 // Add relationships to their respective tables. This is handled as a
234 // complicated set of loops to reduce code duplication. To add a new
235 // relationship, just add the array key that is used in
236 // $types['properties']['description'] to define the relationship, and
237 // then add the name of the table that stores the relationship.
238 $relationships = array(
239 'domain' => 'rdfx_term_domains',
240 'range' => 'rdfx_term_ranges',
241 );
242 foreach ($relationships as $relationship => $table_name) {
243 if (isset($term_description[$relationship])) {
244 foreach ($term_description[$relationship] as $related_term) {
245 $related_term_tid = rdfx_get_tid($related_term, $vocabulary_uri);
246 if ($related_term_tid) {
247 db_merge($table_name)
248 ->key(array('tid' => $tid, $relationship . '_tid' => $related_term_tid))
249 ->fields(array(
250 'tid' => $tid,
251 $relationship . '_tid' => $related_term_tid,
252 ))
253 ->execute();
254 }
255 }
256 }
257 }
258 }
259 }
260 // @todo Add a hook that passes the $vocabulary and the $model.
261 }
262
263 /**
264 * Returns metadata about term types defined by rdf modules.
265 *
266 * If your module needs to determine what term types are being supplied by
267 * other modules, call this function. Querying rdfx database tables directly
268 * for this information is discouraged. Any additional term types should be
269 * added through the corresponding alter hook.
270 *
271 * Three major bins of data are stored: tags, value_types, and functions. Each
272 * entry in these bins is keyed by the value stored in the actual VotingAPI
273 * tables, and contains an array with (minimally) 'name' and 'description' keys.
274 * Modules can add extra keys to their entries if desired.
275 *
276 * This metadata can be modified or expanded using hook_rdfx_term_types_alter().
277 *
278 * @return
279 * An array of metadata defined by RDFx Terms and altered by rdf modules.
280 *
281 * @see hook_rdfx_term_types_alter()
282 *
283 * Modeled on VotingAPI votingapi_metadata.
284 */
285 function rdfx_term_types($reset = FALSE) {
286 static $types;
287 if ($reset || !isset($types)) {
288 $types['classes']['term_types'] = array();
289 $types['properties']['term_types'] = array();
290
291 $term_details = '';
292
293 // @todo Should the inference consider subProp and subClass relationships
294 // as well. ie. should all OWL classes also have the type RDFS Class
295
296 // @todo Switch to drupal cache
297 $types['classes']['term_types'] = array(
298 'rdfs_class' => array(
299 'uri' => 'http://www.w3.org/2000/01/rdf-schema#Class',
300 'inference' => array(
301 'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array(
302 'subject',
303 'object',
304 ),
305 'http://www.w3.org/2000/01/rdf-schema#domain' => array(
306 'object',
307 ),
308 'http://www.w3.org/2000/01/rdf-schema#range' => array(
309 'object',
310 ),
311 ),
312 ),
313 'owl_class' => array(
314 'uri' => 'http://www.w3.org/2002/07/owl#Class',
315 'inference' => array(
316 'http://www.w3.org/2002/07/owl#equivalentClass' => array(
317 'subject',
318 'object',
319 ),
320 'http://www.w3.org/2002/07/owl#disjointWith' => array(
321 'subject',
322 'object',
323 ),
324 ),
325 ),
326 );
327
328 $types['properties']['term_types'] = array (
329 'rdf_property' => array(
330 'uri' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property',
331 'inference' => array(
332 'http://www.w3.org/2000/01/rdf-schema#domain' => array(
333 'subject',
334 ),
335 'http://www.w3.org/2000/01/rdf-schema#range' => array(
336 'subject',
337 ),
338 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array(
339 'subject',
340 'object',
341 ),
342 'http://www.w3.org/2002/07/owl#equivalentProperty' => array(
343 'subject',
344 'object',
345 ),
346 'http://www.w3.org/2002/07/owl#inverseOf' => array(
347 'subject',
348 'object',
349 ),
350 ),
351 ),
352 'owl_property_datatype' => array(
353 'uri' => 'http://www.w3.org/2002/07/owl#DatatypeProperty',
354 'inference' => array(
355 ),
356 ),
357 'owl_property_object' => array(
358 'uri' => 'http://www.w3.org/2002/07/owl#ObjectProperty',
359 'inference' => array(
360 ),
361 ),
362 'owl_property_functional' => array(
363 'uri' => 'http://www.w3.org/2002/07/owl#FunctionalProperty',
364 'inference' => array(
365 ),
366 ),
367 'owl_property_inverse_functional' => array(
368 'uri' => 'http://www.w3.org/2002/07/owl#InverseFunctionalProperty',
369 'inference' => array(
370 ),
371 ),
372 'owl_property_symmetric' => array(
373 'uri' => 'http://www.w3.org/2002/07/owl#SymmetricProperty',
374 'inference' => array(
375 ),
376 ),
377 'owl_property_asymmetric' => array(
378 'uri' => 'http://www.w3.org/2002/07/owl#AsymmetricProperty',
379 'inference' => array(
380 ),
381 ),
382 'owl_property_annotation' => array(
383 'uri' => 'http://www.w3.org/2002/07/owl#AnnotationProperty',
384 'inference' => array(
385 ),
386 ),
387 'owl_property_reflexive' => array(
388 'uri' => 'http://www.w3.org/2002/07/owl#ReflexiveProperty',
389 'inference' => array(
390 ),
391 ),
392 'owl_property_irreflexive' => array(
393 'uri' => 'http://www.w3.org/2002/07/owl#IrreflexiveProperty',
394 'inference' => array(
395 ),
396 ),
397 'owl_property_transitive' => array(
398 'uri' => 'http://www.w3.org/2002/07/owl#TransitiveProperty',
399 'inference' => array(
400 ),
401 ),
402 );
403
404 $types['classes']['description'] = array(
405 'superclass' => array(
406 'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array(
407 'object',
408 ),
409 ),
410 'disjoint' => array(
411 'http://www.w3.org/2002/07/owl#disjointWith' => array(
412 'object',
413 ),
414 ),
415 );
416
417 $types['properties']['description'] = array(
418 'domain' => array(
419 'http://www.w3.org/2000/01/rdf-schema#domain' => array(
420 'object',
421 ),
422 ),
423 'range' => array(
424 'http://www.w3.org/2000/01/rdf-schema#range' => array(
425 'object',
426 ),
427 ),
428 'superproperty' => array(
429 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array(
430 'object',
431 ),
432 ),
433 'inverse' => array(
434 'http://www.w3.org/2002/07/owl#inverseOf' => array(
435 'object',
436 ),
437 ),
438 );
439 drupal_alter('rdfx_term_types', $types);
440 }
441
442 return $types;
443 }
444
445 /**
446 * Splits a URI into namespace and localpart.
447 */
448 function rdfx_split_uri ($uri) {
449 $parts = ARC2::splitURI($uri);
450 return $parts;
451 }
452
453 function rdfx_get_tid($term_uri, $graph_main_ns) {
454 $nsids = rdfx_get_nsids($graph_main_ns);
455 list($term_ns, $term_local_name) = rdfx_split_uri($term_uri);
456 if (isset($nsids[$term_ns])) {
457 $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(':nsid' => $nsids[$term_ns], ':localname' => $term_local_name))->fetchField();
458 return $tid;
459 }
460 else {
461 return NULL;
462 }
463 }
464
465 function rdfx_get_gid($main_ns) {
466 $gids = db_select('rdfx_namespaces', 'rdfns', array());
467 $gids->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid');
468 $gids
469 ->fields('rdfns', array('gid'))
470 ->condition('rdfns.uri', $main_ns);
471 // @todo There should only be one result if there is a matching vocab source.
472 // However, perhaps we should test to make sure and throw an error?
473 $gid = $gids->execute()->fetchField();
474 return $gid;
475 }
476
477 function rdfx_get_nsids($main_ns) {
478 $gid = rdfx_get_gid($main_ns);
479 $nsids = db_query("SELECT uri, nsid FROM {rdfx_namespaces} WHERE gid = :gid", array(':gid' => $gid))->fetchAllKeyed();
480 return $nsids;
481 }
482
483 function rdfx_curie($tid) {
484 $query = db_select('rdfx_terms', 'rdft')
485 ->fields('rdft', array('local_name'))
486 ->fields('rdfns', array('prefix'))
487 ->condition('rdft.tid', $tid, '=');
488 $query->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid');
489 $result = $query->execute()->fetch();
490 $curie = $result->prefix . ':' . $result->local_name;
491 return $curie;
492 }
493
494 function _rdfx_get_term_details($tid, $langcode = 'und') {
495 $query_language = db_query("SELECT language FROM {rdfx_term_details} WHERE tid = :tid", array(':tid' => $tid));
496 $languages = $query_language->fetchCol();
497 if (!in_array($langcode, $languages)) {
498 if (in_array('und', $languages)) {
499 $langcode = 'und';
500 }
501 elseif (in_array('en', $languages)) {
502 $langcode = 'en';
503 }
504 else {
505 return;
506 }
507 }
508 $query = db_select('rdfx_term_details', 'rdfd')
509 ->fields('rdfd', array('label', 'comment'))
510 ->condition('rdfd.tid', $tid, '=')
511 ->condition('language', $langcode, '=');
512 $details = $query->execute()->fetch();
513 return $details;
514 }
515
516 /**
517 * Queries a set of triples for classes and properties, and builds
518 * an associative array describing the vocabulary and any
519 * classes and properties found.
520 *
521 * @param array $model An ARC2-style array of triples an RDFS vocabulary or OWL ontology
522 * @param array $namespaces Associative array of namespaces parsed from the RDF file
523 * @param string $ns_prefix Namespace prefix for the vocabulary
524 * @param string $ns_uri Only terms in this namespace will be considered
525 * @return array Array describing the vocabulary, its classes and properties.
526 */
527 function _rdfx_extract_schema(&$model, $namespaces, $ns_prefix, $ns_uri) {
528 $vocabulary_details = _rdfx_get_vocabulary_details($model, $ns_uri);
529 $terms = _rdfx_fetch_terms($model);
530 $vocabulary = array(
531 'uri' => $ns_uri,
532 'title' => $vocabulary_details['title'],
533 'description' => $vocabulary_details['description'],
534 'terms' => $terms,
535 'namespaces' => $namespaces,
536 );
537 return $vocabulary;
538 }
539
540 function _rdfx_fetch_terms(&$model) {
541 $terms = array();
542 $term_uris = array();
543
544 // Retrieve the queries for term retrieval. This may have been modified by
545 // other modules.
546 $term_type_groups = rdfx_term_types();
547
548 foreach($term_type_groups as $term_type_group => $group) {
549 foreach ($group['term_types'] as $term_type => $term) {
550 $query = array(
551 array('?', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $term['uri']),
552 );
553 foreach ($term['inference'] as $inference_uri => $query_types) {
554 foreach ($query_types as $query_type) {
555 switch ($query_type) {
556 case 'subject':
557 $query[] = array('?', $inference_uri, null);
558 break;
559 case 'object':
560 $query[] = array(null, $inference_uri, '?');
561 break;
562 }
563 }
564 }
565 $term_uris[$term_type] = _rdfx_query_find_uris($model, $query);
566
567 // Add term details and various relationships for each term, as defined
568 // in rdfx_term_types() and altered by hook_rdfx_term_types_alter().
569 $query_x = array();
570 foreach ($term_uris[$term_type] as $term_uri) {
571
572 $terms[$term_type][$term_uri] = _evoc_query_for_term_description($model, $term_uri);
573 foreach ($group['description'] as $property => $queries) {
574 foreach ($queries as $predicate_uri => $query_types) {
575 foreach ($query_types as $query_type) {
576 switch ($query_type) {
577 case 'subject':
578 $query_x[$term_uri][$property][] = array('?', $predicate_uri, $term_uri);
579 break;
580 case 'object':
581 $query_x[$term_uri][$property][] = array($term_uri, $predicate_uri, '?');
582 break;
583 }
584 }
585 }
586 $terms[$term_type][$term_uri][$property] = _rdfx_query_find_uris($model, $query_x[$term_uri][$property]);
587 }
588 }
589 }
590 }
591 return $terms;
592 }
593
594 function _rdfx_get_vocabulary_details(&$model, $ns_uri) {
595 $query_predicates = array(
596 'title' => array(
597 'http://www.w3.org/2000/01/rdf-schema#label',
598 'http://purl.org/dc/elements/1.1/title',
599 'http://purl.org/dc/terms/title',
600 ),
601 'description' => array(
602 'http://www.w3.org/2000/01/rdf-schema#comment',
603 'http://purl.org/dc/elements/1.1/description',
604 'http://purl.org/dc/terms/description',
605 ),
606 );
607
608 if (substr($ns_uri, -1) == '#') {
609 $uri = substr($ns_uri, 0, -1);
610 }
611
612 foreach ($query_predicates as $query_element => $predicates) {
613 foreach ($predicates as $predicate) {
614 $queries[$query_element][] = array($ns_uri, $predicate, '?');
615 // if ($uri !== NULL) {
616 // $queries[$query_element][] = array($uri, $predicate, '?');
617 // }
618 }
619 $details[$query_element] = _rdfx_query_find_literal($model, $queries[$query_element]);
620 }
621 return $details;
622 }