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

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

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


Revision 1.41 - (show annotations) (download) (as text)
Tue Sep 1 06:03:17 2009 UTC (2 months, 3 weeks ago) by arto
Branch: MAIN
CVS Tags: HEAD
Changes since 1.40: +29 -1 lines
File MIME type: text/x-php
Implemented an admin-configurable RDF mapping and token substitution value for author names in RDF feeds.
1 <?php
2 // $Id: rdf.admin.inc,v 1.39 2009/07/10 14:40:08 arto Exp $
3
4 //////////////////////////////////////////////////////////////////////////////
5 // RDF settings form
6
7 function rdf_admin_settings() {
8 $form = array();
9
10 // Formats
11 $form['formats'] = array(
12 '#type' => 'fieldset',
13 '#title' => t('Formats'),
14 '#collapsible' => TRUE,
15 '#collapsed' => FALSE,
16 '#description' => rdf_help('admin/settings/rdf#formats'),
17 );
18
19 $formats = array();
20 foreach (rdf_get_formats('names') as $key => $value) {
21 $formats[$key] = '';
22 }
23 $form['formats']['rdf_format'] = array('#type' => 'radios', '#options' => $formats, '#default_value' => RDF_FORMAT);
24
25 // RDFa
26 $form['rdfa'] = array('#type' => 'fieldset', '#title' => t('RDFa'), '#collapsible' => TRUE, '#collapsed' => TRUE);
27 $form['rdfa']['rdf_rdfa_doctype'] = array(
28 '#type' => 'radios',
29 '#title' => t('Output an XHTML+RDFa DOCTYPE'),
30 '#default_value' => (int)variable_get('rdf_rdfa_doctype', FALSE),
31 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
32 '#description' => t('Whether to override the current theme\'s output and replace any existing <a href="http://en.wikipedia.org/wiki/Document_Type_Declaration">Document Type Declaration</a> with an <tt>XHTML+RDFa 1.0</tt> DOCTYPE that enables the use of <a href="http://en.wikipedia.org/wiki/RDFa">RDFa</a> metadata in Drupal content. Please note that this feature may be incompatible with <a href="@admin-performance">aggressive caching</a>. <em>This is an experimental feature, please use caution and <a href="http://drupal.org/node/add/project-issue/rdf">report any problems</a></em>.</a>.', array('@admin-performance' => url('admin/settings/performance'))),
33 );
34 $form['rdfa']['rdf_rdfa_prefixes'] = array(
35 '#type' => 'checkboxes',
36 '#title' => t('Enabled RDFa namespace prefixes'),
37 '#default_value' => array_keys(rdf_get_namespaces('rdfa')),
38 '#options' => array_diff_key(array_combine($ns = array_keys(rdf_get_namespaces()), $ns), array('_' => '')),
39 '#description' => t('Select which <a href="@admin-rdf-namespaces">namespace prefixes</a> will be available on RDFa-enabled pages.', array('@admin-rdf-namespaces' => url('admin/settings/rdf/namespaces'))),
40 );
41 if (!variable_get('rdf_rdfa_doctype', FALSE)) {
42 $form['rdfa']['rdf_rdfa_prefixes']['#attributes']['disabled'] = 'disabled';
43 $form['rdfa']['rdf_rdfa_disabled'] = array('#type' => 'hidden', '#value' => '1');
44 }
45
46 // RDF Schema
47 $form['rdfs'] = array('#type' => 'fieldset', '#title' => t('RDF Schema'), '#collapsible' => TRUE, '#collapsed' => TRUE);
48 $form['rdfs']['rdf_schema_uri'] = array(
49 '#type' => 'textfield',
50 '#title' => t('RDFS base URI'),
51 '#default_value' => RDF_SCHEMA_URI,
52 '#maxlength' => 255,
53 '#required' => TRUE,
54 '#description' => t(''), // TODO
55 );
56
57 // Maintenance
58 $form['maintenance'] = array(
59 '#type' => 'fieldset',
60 '#title' => t('Maintenance'),
61 '#collapsible' => TRUE,
62 '#collapsed' => TRUE,
63 '#description' => rdf_help('admin/settings/rdf#maintenance'),
64 '#weight' => 70,
65 );
66 $form['maintenance']['rdf_db_prevent_duplicates'] = array(
67 '#type' => 'radios',
68 '#title' => t('Prevent duplicate statement insertion'),
69 '#default_value' => (int)variable_get('rdf_db_prevent_duplicates', FALSE),
70 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
71 '#description' => t('Whether to always perform a check to see if an RDF statement that is about to be inserted into a local RDF repository already exists in said repository, in which case the insertion request can be silently ignored. Note that this involves a slight performance hit, and it may be a good idea to disable this option e.g. when importing large RDF data sets.'),
72 );
73 $form['maintenance']['rdf_db_merge_duplicates'] = array(
74 '#type' => 'radios',
75 '#title' => t('Merge duplicate statements on cron runs'),
76 '#default_value' => (int)variable_get('rdf_db_merge_duplicates', FALSE),
77 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
78 '#description' => t('Whether to check for and remove superfluous identical RDF statements in each local RDF repository during cron runs, ensuring each repository contains only unique statements. It may be useful to enable this after importing large RDF data sets if the real-time duplicate prevention option was disabled during the import.'),
79 );
80 $form['maintenance']['rdf_db_purge_resources'] = array(
81 '#type' => 'radios',
82 '#title' => t('Purge unused resource URIs on cron runs'),
83 '#default_value' => (int)variable_get('rdf_db_purge_resources', FALSE),
84 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
85 '#description' => t('Whether to perform periodic purges of unused resource URIs on cron runs, decreasing the size of the <code>rdf_resources</code> table. It may be useful to enable this when dealing with transient RDF data sets that contain large numbers of URIs that are not likely to be seen again once the data sets expire or are removed.'),
86 );
87
88 return array_merge_recursive(system_settings_form($form), array('#theme' => 'rdf_admin_settings', 'buttons' => array('#weight' => 99)));
89 }
90
91 function rdf_admin_settings_validate($form, &$form_state) {
92 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
93
94 if (!empty($rdf_rdfa_disabled)) {
95 // Ensure we don't muck up the default value when disabled:
96 unset($form_state['values']['rdf_rdfa_prefixes']);
97 unset($form_state['values']['rdf_rdfa_disabled']);
98 }
99 else {
100 $rdf_rdfa_prefixes = array_filter($rdf_rdfa_prefixes, 'is_string');
101 }
102 }
103
104 function theme_rdf_admin_settings($form) {
105 $head = array(t('Default'), t('Name'), t('MIME type'), t('Readable'), t('Writable'));
106 $rows = array();
107 foreach (rdf_get_formats() as $format) {
108 $rows[] = array(
109 drupal_render($form['formats']['rdf_format'][$format->name]),
110 !empty($format->link) ? l($format->title, $format->link) : $format->title,
111 $format->mime_type,
112 !empty($format->unserialize) ? t('Yes') : t('No'),
113 !empty($format->serialize) ? t('Yes') : t('No')
114 );
115 }
116
117 $form['formats']['#value'] = theme('table', $head, $rows, array('class' => 'formats'));
118 unset($form['formats']['rdf_format']);
119
120 return drupal_render($form);
121 }
122
123 //////////////////////////////////////////////////////////////////////////////
124 // RDF data management
125
126 function rdf_admin_data() {
127 $subject = isset($_GET['s']) ? urldecode($_GET['s']) : url(NULL, array('absolute' => TRUE));
128 $predicate = isset($_GET['p']) ? urldecode($_GET['p']) : '';
129 $object = isset($_GET['o']) ? urldecode($_GET['o']) : '';
130 $context = isset($_GET['g']) ? urldecode($_GET['g']) : '';
131
132 $form = array();
133 $form['query'] = array('#type' => 'fieldset', '#title' => t('Query'), '#prefix' => '<div class="container-inline">', '#suffix' => '</div>');
134 $form['query']['subject'] = array(
135 '#type' => 'textfield',
136 '#title' => '',
137 '#default_value' => $subject,
138 '#maxlength' => 255,
139 '#size' => 20,
140 '#autocomplete_path' => 'rdf/autocomplete/resource',
141 );
142 $form['query']['predicate'] = array(
143 '#type' => 'textfield',
144 '#title' => '',
145 '#default_value' => $predicate,
146 '#maxlength' => 255,
147 '#size' => 20,
148 '#autocomplete_path' => 'rdf/autocomplete/property',
149 );
150 $form['query']['object'] = array(
151 '#type' => 'textfield',
152 '#title' => '',
153 '#default_value' => $object,
154 '#maxlength' => 255,
155 '#size' => 20,
156 );
157 $form['query']['submit'] = array('#type' => 'submit', '#value' => t('Find'), '#submit' => array('rdf_admin_data_submit'));
158
159 $form['advanced'] = array('#type' => 'fieldset', '#title' => t('Advanced options'), '#collapsible' => TRUE, '#collapsed' => empty($context));
160 $form['advanced']['context'] = array(
161 '#type' => 'select',
162 '#title' => 'Context',
163 '#default_value' => $context,
164 '#options' => array_merge(array('' => t('(all)')), array_combine($contexts = rdf_get_contexts(), $contexts)),
165 );
166
167 return $form;
168 }
169
170 function rdf_admin_data_submit($form, &$form_state) {
171 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
172
173 $form_state['redirect'] = array('admin/content/rdf', drupal_query_string_encode(array('g' => $context, 's' => $subject, 'p' => $predicate, 'o' => $object)));
174 }
175
176 function theme_rdf_admin_data($form) {
177 foreach (array('query' => array('subject', 'predicate', 'object'), 'advanced' => array('context')) as $fieldset => $fields) {
178 foreach ($fields as $field) {
179 $$field = !empty($form[$fieldset][$field]['#value']) ? trim($form[$fieldset][$field]['#value']) : NULL;
180 }
181 }
182 $data = rdf_normalize(rdf_query($subject, $predicate, $object, $context ? array('context' => $context) : array()));
183
184 $output = drupal_render($form['query']);
185 $output .= drupal_render($form['advanced']);
186 $output .= theme('rdf_triple_table', $data, array('link' => 'theme_rdf_admin_link'));
187 $output .= drupal_render($form);
188 return $output;
189 }
190
191 function theme_rdf_admin_link($title, $uri, array $attributes = array()) {
192 return l($title, 'admin/content/rdf', array('query' => drupal_query_string_encode(array('s' => $uri))));
193 }
194
195 function rdf_db_admin_data_form(&$form_state, $edit = array('subject' => '', 'predicate' => '', 'object' => '', 'repository' => 'local')) {
196 $edit = (object)$edit;
197 $form = array();
198
199 $form['repository'] = array(
200 '#type' => 'select',
201 '#title' => t('Repository'),
202 '#default_value' => $edit->repository,
203 '#options' => rdf_get_repositories('names', array('persistent' => TRUE, 'mutable' => TRUE)),
204 '#description' => t('Select which RDF repository to store new statement in.'),
205 );
206 $form['subject'] = array(
207 '#type' => 'textfield',
208 '#title' => t('Subject'),
209 '#default_value' => $edit->subject,
210 '#maxlength' => 255,
211 '#required' => TRUE,
212 '#description' => t('The subject of the statement: the entity or resource that the statement is about, i.e. the who or what. This should be given as either a <a href="http://en.wikipedia.org/wiki/URI" title="Uniform Resource Identifier">URI</a> or a <a href="http://en.wikipedia.org/wiki/CURIE" title="Compact URI">CURIE</a>.'),
213 '#autocomplete_path' => 'rdf/autocomplete/resource',
214 );
215 $form['predicate'] = array(
216 '#type' => 'textfield',
217 '#title' => t('Predicate'),
218 '#default_value' => $edit->predicate,
219 '#maxlength' => 255,
220 '#required' => TRUE,
221 '#description' => t('The predicate of the statement: the relation between subject and object, i.e. the property name or verb. This should be given as either a <a href="http://en.wikipedia.org/wiki/URI" title="Uniform Resource Identifier">URI</a> or a <a href="http://en.wikipedia.org/wiki/CURIE" title="Compact URI">CURIE</a>.'),
222 '#autocomplete_path' => 'rdf/autocomplete/property',
223 );
224 $form['object'] = array(
225 '#type' => 'textfield',
226 '#title' => t('Object'),
227 '#default_value' => $edit->object,
228 '#maxlength' => 255,
229 '#required' => TRUE,
230 '#description' => t('The object of the statement: the entity or resource that relates to the subject as described by the predicate; i.e. the property value. This should be given as either a <a href="http://en.wikipedia.org/wiki/URI" title="Uniform Resource Identifier">URI</a>, a <a href="http://en.wikipedia.org/wiki/CURIE" title="Compact URI">CURIE</a>, or a literal.'),
231 );
232
233 $form['submit'] = array('#type' => 'submit', '#value' => t('Add statement'));
234 return $form;
235 }
236
237 function rdf_db_admin_data_form_validate($form, &$form_state) {
238 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
239
240 if (!rdf_is_valid_uri($subject) && !rdf_is_valid_curie($subject)) {
241 form_set_error('subject', t('Subject value is not a valid URI or CURIE.'));
242 }
243
244 if (!rdf_is_valid_uri($predicate) && !rdf_is_valid_curie($predicate)) {
245 form_set_error('predicate', t('Predicate value is not a valid URI or CURIE.'));
246 }
247
248 // TODO: relax this once we have a specific input box for literals.
249 if (!rdf_is_valid_uri($object) && !rdf_is_valid_curie($object)) {
250 //form_set_error('object', t('Object value is not a valid URI or CURIE.'));
251 }
252 }
253
254 function rdf_db_admin_data_form_submit($form, &$form_state) {
255 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
256
257 // TODO: properly sanitize/expand/convert subject, predicate and object.
258 $subject = rdf_is_valid_curie($subject) ? rdf_qname_to_uriref($subject) : rdf_uri($subject);
259 $predicate = rdf_is_valid_curie($predicate) ? rdf_qname_to_uriref($predicate) : rdf_uri($predicate);
260 $object = rdf_is_valid_uri($object) ? rdf_uri($object) : $object;
261
262 if (rdf_insert($subject, $predicate, $object, array('repository' => $repository))) {
263 drupal_set_message(t('The statement has been added.'));
264 }
265 else {
266 drupal_set_message(t('The statement was not added due to an error reported by the repository.'), 'error');
267 }
268
269 $form_state['redirect'] = array('admin/content/rdf', drupal_query_string_encode(array('s' => (string)$subject)));
270 }
271
272 //////////////////////////////////////////////////////////////////////////////
273 // RDF mappings management
274
275 function theme_rdf_mapping($mapping) {
276 return empty($mapping) ? '-' : l(rdf_uri_to_qname($mapping, FALSE, $mapping), $mapping, array('attributes' => array('title' => $mapping)));
277 }
278
279 function rdf_admin_mappings() {
280 // TODO: this should get refactored for extensibility, at some point.
281
282 $header = array(t('Title'), t('Class/property'), t('RDF mapping'), array('data' => t('Operations'), 'colspan' => '2'));
283 $rows = array();
284
285 // Content types
286 if (module_exists('node')) {
287 $rows[] = array(array('data' => t('Content types'), 'colspan' => 5, 'class' => 'category'));
288 $mapping = NULL;
289 foreach (node_get_types('names') as $node_type => $node_type_title) {
290 $mapping = theme_rdf_mapping(variable_get('rdf_schema_class_' . $node_type, ''));
291 $link = l(t('edit'), 'admin/content/node-type/' . $node_type, array('query' => 'destination=' . drupal_urlencode($_GET['q'])));
292 $rows[] = array($node_type_title, $node_type, $mapping, $link, '');
293 }
294 if (is_null($mapping)) {
295 $rows[] = array(array('data' => t('No content types defined.'), 'colspan' => 5));
296 }
297 }
298
299 // Content fields
300 $rows[] = array(array('data' => t('Content fields'), 'colspan' => 5, 'class' => 'category'));
301 if (module_exists('node')) {
302 // Show the core field mappings that are currently hardcoded in the RSS output:
303 $rows[] = array(t('Title'), 'title', theme_rdf_mapping('dc:title'), '-', '');
304 $rows[] = array(t('Body'), 'body', theme_rdf_mapping('dc:description'), '-', '');
305 $rows[] = array(t('Language'), 'language', theme_rdf_mapping('dc:language'), '-', '');
306 $rows[] = array(t('Authored by'), 'name', theme_rdf_mapping('dc:creator'), '-', '');
307 $rows[] = array(t('Authored on'), 'created', theme_rdf_mapping('dc:date'), '-', '');
308 }
309 if (module_exists('content')) {
310 $mapping = NULL;
311 foreach (content_fields() as $field_name => $field_info) {
312 $link = l(t('edit'), 'admin/content/types/fields', array('fragment' => $field_name)); // FIXME: link
313 if (($mapping = variable_get('rdf_schema_property_content_' . $field_name, ''))) {
314 $mapping = theme_rdf_mapping($mapping);
315 $rows[] = array($field_info['widget']['label'], $field_name, $mapping, $link, '');
316 }
317 // HACK: support CCK Date fields that have both a start and an end date:
318 else if (variable_get('rdf_schema_property_content_' . $field_name . '[from]', '')) {
319 $mappings = array();
320 if (($mapping = variable_get('rdf_schema_property_content_' . $field_name . '[from]', ''))) {
321 $mappings[] = theme_rdf_mapping($mapping);
322 }
323 if (($mapping = variable_get('rdf_schema_property_content_' . $field_name . '[to]', ''))) {
324 $mappings[] = theme_rdf_mapping($mapping);
325 }
326 $rows[] = array($field_info['widget']['label'], $field_name, implode(', ', $mappings), $link, '');
327 }
328 else {
329 $rows[] = array($field_info['widget']['label'], $field_name, '-', $link, '');
330 }
331 }
332 if (is_null($mapping)) {
333 $rows[] = array(array('data' => t('No content fields defined.'), 'colspan' => 5));
334 }
335 }
336
337 // Taxonomy vocabularies
338 if (module_exists('taxonomy')) {
339 $rows[] = array(array('data' => t('Taxonomy vocabularies'), 'colspan' => 5, 'class' => 'category'));
340 $mapping = NULL;
341 foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
342 $mapping = theme_rdf_mapping(variable_get('rdf_schema_property_vocabulary_' . $vid, ''));
343 $link = l(t('edit'), 'admin/content/taxonomy/edit/vocabulary/' . $vid, array('query' => 'destination=' . drupal_urlencode($_GET['q'])));
344 $rows[] = array($vocabulary->name, '-', $mapping, $link, '');
345 }
346 if (is_null($mapping)) {
347 $rows[] = array(array('data' => t('No vocabularies defined.'), 'colspan' => 5));
348 }
349 }
350
351 // Profile fields
352 if (module_exists('profile')) {
353 $rows[] = array(array('data' => t('Profile fields'), 'colspan' => 5, 'class' => 'category'));
354 $result = db_query('SELECT fid, title, name FROM {profile_fields} ORDER BY category, weight');
355 $mapping = NULL;
356 while ($field = db_fetch_object($result)) {
357 $mapping = theme_rdf_mapping(variable_get('rdf_schema_property_profile_' . $field->fid, ''));
358 $link = l(t('edit'), 'admin/user/profile/edit/' . $field->fid, array('query' => 'destination=' . drupal_urlencode($_GET['q'])));
359 $rows[] = array($field->title, $field->name, $mapping, $link, '');
360 }
361 if (is_null($mapping)) {
362 $rows[] = array(array('data' => t('No profile fields defined.'), 'colspan' => 5));
363 }
364 }
365
366 return theme('table', $header, $rows);
367 }
368
369 function rdf_admin_autocomplete_resource($string = '', $limit = 15) {
370 $matches = array();
371 if (strlen($string = trim($string))) {
372 $result = db_query("SELECT r.uri FROM {rdf_resources} r WHERE r.uri LIKE '%s%%' ORDER BY r.uri ASC LIMIT %d", $string, $limit);
373 while ($resource = db_fetch_object($result)) {
374 $matches[$resource->uri] = $resource->uri;
375 }
376 }
377 drupal_json($matches);
378 }
379
380 function rdf_admin_autocomplete_class($string = '', $limit = 15) {
381 $matches = array();
382 if (!empty($string)) {
383 // TODO
384 }
385 drupal_json($matches);
386 }
387
388 function rdf_admin_autocomplete_property($string = '', $limit = 15) {
389 $matches = array();
390 if (!empty($string)) {
391 foreach (module_invoke_all('rdf_properties') as $namespace => $properties) {
392 foreach ($properties as $property) {
393 $match = implode(':', array($namespace, $property));
394 if (strpos($match, $string) === 0) {
395 $matches[$match] = $match;
396 }
397 if (count($matches) == $limit) {
398 break 2;
399 }
400 }
401 }
402 }
403 drupal_json($matches);
404 }
405
406 //////////////////////////////////////////////////////////////////////////////
407 // RDF feeds management
408
409 function rdf_admin_feeds() {
410 $header = array('', t('Path'), t('Module'), t('Title'), t('Operations'));
411
412 $rows = array();
413 foreach (rdf_get_feed_info(NULL, TRUE) as $feed_id => $feed) {
414 $module = unserialize(db_result(db_query("SELECT info FROM {system} WHERE type = 'module' AND name = '%s'", $feed->module)));
415
416 $is_wildcard = (strpos($feed->path, '%') !== FALSE);
417 $is_core = in_array($feed->module, array('node', 'taxonomy', 'blog', 'aggregator'));
418 $is_rdf = !$is_core || array_search($feed->id, array_keys(variable_get('rdf_feed_override', array()))) !== FALSE;
419
420 $path = drupal_get_path_alias($feed->path);
421 $rows[] = array(
422 theme('feed_icon', !$is_wildcard ? url($feed->path) : '#', $path),
423 !$is_wildcard ? l($path, $feed->path) : $feed->path,
424 $module ? $module['name'] : $feed->module,
425 $feed->title,
426 l(t($is_rdf ? 'configure' : 'enable'), 'admin/settings/rdf/feeds/edit/' . $feed_id),
427 );
428 }
429
430 return theme('table', $header, $rows);
431 }
432
433 function rdf_admin_feed(&$form_state, $feed_id) {
434 $form = array();
435 $feed = rdf_get_feed_info($feed_id);
436 $edit = rdf_get_feed_settings($feed_id);
437
438 $is_core = in_array($feed->module, array('node', 'taxonomy', 'blog', 'aggregator'));
439 $is_rdf = !$is_core || array_search($feed->id, array_keys(variable_get('rdf_feed_override', array()))) !== FALSE;
440
441 // Feed compatibility
442 $form['compatibility'] = array(
443 '#type' => 'fieldset',
444 '#title' => t('Feed compatibility'),
445 '#collapsible' => FALSE,
446 '#collapsed' => FALSE,
447 '#description' => rdf_help('admin/settings/rdf/feeds/edit#compatibility', $is_core),
448 );
449 if ($is_core) { // this is a feed from a core module, and needs an RDF override
450 $form['compatibility']['override'] = array(
451 '#type' => 'radios',
452 '#title' => '',
453 '#default_value' => $is_rdf ? 1 : 0,
454 '#options' => array(0 => t('RSS 2.0 (default, but <strong>not</strong> compatible with RDF)'), 1 => t('RSS 1.0 (<strong>recommended</strong>, compatible with RDF)')),
455 '#description' => t('The feeds provided by Drupal\'s core modules are <strong>not</strong> RDF-compatible by default. Use this setting to let the RDF module upgrade those feeds to reusable, extensible and future-proof RSS 1.0-compatible RDF output.'),
456 );
457 }
458 else {
459 $form['compatibility']['text'] = array('#value' => t('<a href="http://web.resource.org/rss/1.0/">RSS 1.0</a> (compatible with RDF)'));
460 }
461
462 // Channel settings
463 $form['channel'] = array(
464 '#type' => 'fieldset',
465 '#title' => t('Channel settings'),
466 '#collapsible' => TRUE,
467 '#collapsed' => FALSE,
468 '#description' => rdf_help('admin/settings/rdf/feeds/edit#channel'),
469 );
470 rdf_admin_feed_channel_form($form['channel'], $form_state, $edit);
471
472 // Item settings
473 $form['item'] = array(
474 '#type' => 'fieldset',
475 '#title' => t('Item settings'),
476 '#collapsible' => TRUE,
477 '#collapsed' => FALSE,
478 '#description' => rdf_help('admin/settings/rdf/feeds/edit#item'),
479 );
480 rdf_admin_feed_item_form($form['item'], $form_state, $edit);
481
482 // TODO: namespace selection
483
484 if ($is_core) { // this is a feed from a core module, and hence lacks some options
485 $form['channel']['description_from_mission'] = array('#type' => 'hidden', '#value' => '');
486 $form['channel']['description'] = array('#type' => 'hidden', '#value' => '');
487 }
488
489 if (!$is_rdf) { // disable extended configuration if the feed isn't RSS 1.0
490 foreach (array('channel', 'item') as $fieldset) {
491 $form[$fieldset]['#collapsed'] = TRUE;
492 $form[$fieldset]['#attributes']['disabled'] = 'disabled';
493 foreach ($form[$fieldset] as $field => &$field_info) {
494 if ($field[0] != '#') {
495 $field_info['#attributes']['disabled'] = 'disabled';
496 }
497 }
498 }
499 }
500
501 $form['feed_id'] = array('#type' => 'hidden', '#value' => $feed_id);
502 $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
503
504 return $form;
505 }
506
507 function rdf_admin_feed_channel_form(&$form, &$form_state, $defaults = array()) {
508 $form['description_from_mission'] = array(
509 '#type' => 'checkbox',
510 '#default_value' => !empty($defaults['description_from_mission']),
511 '#title' => t('Use the site mission as the channel description'),
512 );
513 $form['description'] = array(
514 '#type' => 'textfield',
515 '#title' => t('RSS 1.0 channel description'),
516 '#default_value' => @$defaults['description'],
517 '#description' => t('This will appear as the channel description in the RDF feed.'),
518 '#process' => array('views_process_dependency'),
519 '#dependency' => array('edit-style-options-override' => array(FALSE)),
520 );
521 $form['update_period'] = array(
522 '#type' => 'select',
523 '#title' => t('Update period'),
524 '#default_value' => @$defaults['update_period'],
525 '#options' => array('' => t('<not specified>'), 'hourly' => t('hourly'), 'daily' => t('daily'), 'weekly' => t('weekly'), 'monthly' => t('monthly'), 'yearly' => t('yearly')),
526 '#description' => t('Provides <a href="http://web.resource.org/rss/1.0/modules/syndication/">RSS 1.0 syndication hints</a> to aggregators and others picking up this RSS feed regarding how often it is updated.'),
527 );
528 $form['update_frequency'] = array(
529 '#type' => 'select',
530 '#title' => t('Update frequency'),
531 '#default_value' => @$defaults['update_frequency'],
532 '#options' => array_merge(array('' => t('<not specified>')), drupal_map_assoc(range(1, 24))),
533 '#description' => t('Used to describe the frequency of updates in relation to the update period. This is a positive integer that indicates how many times in that period the channel is updated. For example, an update period of <em>daily</em> with an update frequency of <em>2</em> indicates the channel is updated twice daily. If omitted, a value of <em>1</em> is assumed.'),
534 );
535 $form['update_base'] = array(
536 '#type' => 'textfield',
537 '#title' => t('Update base date'),
538 '#default_value' => @$defaults['update_base'],
539 '#description' => t('Defines a base date to be used in concert with the update period and update frequency to calculate the publishing schedule. The date format takes the form: <code>yyyy-mm-ddThh:mm</code>.'),
540 '#size' => 16,
541 '#maxlength' => 16,
542 );
543 $form['format'] = array(
544 '#type' => 'select',
545 '#title' => t('Default format'),
546 '#default_value' => !empty($defaults['format']) ? $defaults['format'] : RDF_FORMAT_RSS,
547 '#options' => rdf_get_formats('names'),
548 '#description' => t('This determines the default RDF serialization format to output. Browsers and RSS feed consumers can override this through <a href="http://en.wikipedia.org/wiki/Content_negotiation">HTTP content negotiation</a>, that is, by passing an <code>Accept</code> header specifying another supported format.'),
549 );
550 }
551
552 function rdf_admin_feed_item_form(&$form, &$form_state, $defaults = array(), $module = NULL) {
553 // Content display settings
554 $form['item_length'] = array(
555 '#type' => 'select',
556 '#title' => t('Content display type'),
557 '#default_value' => !empty($defaults['item_length']) ? $defaults['item_length'] : '',
558 '#options' => array(
559 '' => t('Use default RSS settings'),
560 'fulltext' => t('Full text'),
561 'teaser' => t('Title plus teaser'),
562 'title' => t('Title only'),
563 ),
564 '#description' => t('How to display content items in the feed.'),
565 );
566
567 // Author settings
568 $form['author_property'] = array(
569 '#type' => 'textfield',
570 '#title' => 'Author property',
571 '#default_value' => !empty($defaults['author_property']) ? $defaults['author_property'] : 'dc:creator',
572 '#maxlength' => 255,
573 '#size' => 20,
574 '#description' => t('Configure the RDF predicate to use when outputting the author name in this RDF feed. For compatibility with the RSS 1.0 specification, the default property of <code>dc:creator</code> is always output; however, by changing this property, you can output an additional custom property (e.g. <code>foaf:maker</code>).'),
575 '#autocomplete_path' => 'rdf/autocomplete/property',
576 );
577 if (module_exists('token')) {
578 // If the Token module is installed, allow customization of the author property's value:
579 $form['author_value'] = array(
580 '#type' => 'textfield',
581 '#title' => 'Author text',
582 '#default_value' => !empty($defaults['author_value']) ? $defaults['author_value'] : '[author-name-raw]',
583 '#maxlength' => 255,
584 '#size' => 60,
585 '#description' => t('If you have installed the <a href="http://drupal.org/project/token" target="_blank">Token</a> module, you may here configure text substitutions to be used when outputting the author name into RDF feeds. The default setting is to use the <code>[author-name-raw]</code> token, which displays the author\'s user name in the usual Drupal manner.'),
586 );
587 }
588 else {
589 $form['author_value'] = array('#type' => 'hidden', '#value' => '[author-name-raw]');
590 }
591
592 // Time zone settings
593 $form['date_timezone'] = array(
594 '#type' => 'select',
595 '#title' => t('Time zone output'),
596 '#default_value' => !empty($defaults['date_timezone']) ? $defaults['date_timezone'] : '',
597 '#options' => array('' => t('UTC'), 'local' => t('Local')),
598 '#description' => t('It is <strong>strongly</strong> recommended you output dates in UTC to prevent a multitude of problems due to buggy client software.'),
599 );
600 $form['date_timezone_dst'] = array(
601 '#type' => 'checkbox',
602 '#title' => t('Adjust time zone offsets for Daylight Saving Time (DST)'),
603 '#default_value' => !empty($defaults['date_timezone_dst']) ? $defaults['date_timezone_dst'] : '',
604 '#description' => t(''),
605 );
606 $form['date_timezone_name'] = array(
607 '#type' => 'checkbox',
608 '#title' => t('Append the time zone name to datetime literals'),
609 '#default_value' => !empty($defaults['date_timezone_name']) ? $defaults['date_timezone_name'] : '',
610 '#description' => t('This is a non-standard extension to XML Schema\'s datetime literal datatype and <strong>not</strong> recommended for ordinary usage.'),
611 );
612 }
613
614 function rdf_admin_feed_validate($form, &$form_state) {
615 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
616
617 // TODO
618 }
619
620 function rdf_admin_feed_submit($form, &$form_state) {
621 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
622 $feed = rdf_get_feed_info($feed_id);
623
624 $form_state['redirect'] = 'admin/settings/rdf/feeds';
625
626 // For core module feeds
627 if (isset($override)) {
628 $overrides = variable_get('rdf_feed_override', array());
629 $was_overridden = !empty($overrides[$feed->id]);
630
631 if (!empty($override) && !$was_overridden) {
632 $overrides[$feed->id] = TRUE;
633
634 variable_set('rdf_feed_override', $overrides);
635 drupal_set_message(t('The feed %path has been upgraded for RDF compatibility.', array('%path' => $feed->path)));
636 menu_rebuild(); // we need to rebuild the menu cache for this to take effect
637
638 $form_state['redirect'] = 'admin/settings/rdf/feeds/edit/' . $feed->id;
639 return; // continue workflow on the same screen to allow for further feed configuration
640 }
641 else if (empty($override) && $was_overridden) {
642 unset($overrides[$feed->id]);
643
644 variable_set('rdf_feed_override', $overrides);
645 drupal_set_message(t('The feed %path has been downgraded to the default format.', array('%path' => $feed->path)));
646 menu_rebuild(); // we need to rebuild the menu cache for this to take effect
647 }
648 else if (empty($override)) {
649 return; // don't save feed settings for non-RDF feeds
650 }
651 }
652
653 // Save feed settings
654 $function = ($feed->module == 'views') ? 'rdf_views_set_feed_settings' : 'rdf_set_feed_settings';
655 $function($feed, compact('description_from_mission', 'description', 'update_period', 'update_frequency', 'update_base', 'format', 'item_length', 'author_property', 'author_value', 'date_timezone', 'date_timezone_dst', 'date_timezone_name'));
656 drupal_set_message(t('The feed configuration for %path has been updated.', array('%path' => $feed->path)));
657 }
658
659 //////////////////////////////////////////////////////////////////////////////
660 // RDF namespace management
661
662 function rdf_admin_namespaces() {
663 $header = array(t('Prefix'), t('Base URI'), array('data' => t('Operations'), 'colspan' => '2'));
664
665 $rows = array();
666 foreach (rdf_get_namespaces() as $prefix => $uri) {
667 $mutable = (bool)rdf_get_namespace($prefix);
668
669 $rows[] = array(
670 $prefix, //l($prefix, 'admin/settings/rdf/predicates/' . $prefix),
671 check_plain($uri),
672 !$mutable ? '' : l(t('edit'), 'admin/settings/rdf/namespaces/edit/' . $prefix),
673 !$mutable ? '' : l(t('delete'), 'admin/settings/rdf/namespaces/delete/' . $prefix),
674 );
675 }
676
677 return theme('table', $header, $rows);
678 }
679
680 function rdf_db_admin_ns_edit($prefix = '') {
681 if (empty($prefix)) {
682 return drupal_get_form('rdf_db_admin_ns_form');
683 }
684 else {
685 return drupal_get_form('rdf_db_admin_ns_form', rdf_get_namespace($prefix));
686 }
687 }
688
689 function rdf_db_admin_ns_form(&$form_state, $edit = array('prefix' => '', 'uri' => '')) {
690 $edit = (object)$edit;
691 $form = array();
692
693 $form['prefix'] = array('#type' => 'textfield', '#title' => t('Prefix'), '#default_value' => $edit->prefix, '#maxlength' => 64, '#required' => TRUE, '#description' => t('The short abbreviation to use in the place of the base URI. Keep it short, and use only lowercase, alphanumeric letters.'));
694 $form['uri'] = array('#type' => 'textfield', '#title' => t('Base URI'), '#default_value' => $edit->uri, '#maxlength' => 255, '#required' => TRUE, '#description' => t('The absolute base URI of the RDF vocabulary. Make sure that the URI terminates with a hash or slash character.'));
695
696 $form['key'] = array('#type' => 'hidden', '#value' => $edit->prefix);
697 $form['submit'] = array('#type' => 'submit', '#value' => empty($edit->prefix) ? t('Create new namespace') : t('Update namespace'));
698
699 return $form;
700 }
701
702 function rdf_db_admin_ns_form_validate($form, &$form_state) {
703 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
704
705 if (!preg_match('/^[a-z_]+[a-z\d\._]*$/', $prefix)) {
706 form_set_error('prefix', t('Prefix %prefix contains illegal characters.', array('%prefix' => $prefix)));
707 }
708
709 if (!rdf_is_valid_uri($uri)) {
710 form_set_error('uri', t('Base URI %uri contains illegal characters.', array('%uri' => $uri)));
711 }
712
713 if (!preg_match('@[#/:]$@', $uri)) {
714 form_set_error('uri', t('Base URI %uri must terminate with "#", "/" or ":".', array('%uri' => $uri)));
715 }
716 }
717
718 function rdf_db_admin_ns_form_submit($form, &$form_state) {
719 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
720
721 if (empty($key)) {
722 db_query("INSERT INTO {rdf_namespaces} (prefix, uri) VALUES ('%s', '%s')", $prefix, $uri);
723 drupal_set_message(t('The namespace has been created.'));
724 }
725 else {
726 db_query("UPDATE {rdf_namespaces} SET prefix = '%s', uri = '%s' WHERE prefix = '%s'", $prefix, $uri, $key);
727 drupal_set_message(t('The namespace has been updated.'));
728 }
729
730 $form_state['redirect'] = 'admin/settings/rdf/namespaces';
731 }
732
733 function rdf_db_admin_ns_delete($form_state, $prefix) {
734 if (user_access('administer RDF namespaces') && rdf_get_namespace($prefix)) {
735 $form['prefix'] = array('#type' => 'value', '#value' => $prefix);
736 $output = confirm_form($form,
737 t('Are you sure you want to delete the RDF namespace %title?', array('%title' => $prefix)),
738 isset($_GET['destination']) ? $_GET['destination'] : 'admin/settings/rdf/namespaces');
739 }
740 return $output;
741 }
742
743 function rdf_db_admin_ns_delete_submit($form, &$form_state) {
744 if ($form_state['values']['confirm']) {
745 db_query("DELETE FROM {rdf_namespaces} WHERE prefix = '%s'", $form_state['values']['prefix']);
746 drupal_set_message(t('The namespace has been deleted.'));
747
748 $form_state['redirect'] = 'admin/settings/rdf/namespaces';
749 }
750 }
751
752 //////////////////////////////////////////////////////////////////////////////
753 // RDF context management
754
755 function rdf_admin_contexts() {
756 $header = array(t('URI'), t('Statements'));
757
758 $uncountables = array(RDF_SITE_URI, RDF_SCHEMA_URI); // FIXME
759
760 $rows = array();
761 foreach (rdf_get_contexts() as $uri) {
762 $countable = !in_array($uri, $uncountables);
763
764 $rows[] = array(
765 l($uri, 'admin/content/rdf', array('query' => array('g' => $uri, 's' => '', 'p' => '', 'o' => ''))),
766 !$countable ? t('n/a') : rdf_count(NULL, NULL, NULL, array('context' => $uri)), // FIXME
767 );
768 }
769
770 return theme('table', $header, $rows);
771 }
772
773 //////////////////////////////////////////////////////////////////////////////
774 // RDF repository management
775
776 function rdf_admin_repositories() {
777 $header = array(t('Name'), t('Module'), t('Statements'), t('Mutable'), array('data' => t('Operations'), 'colspan' => '2'));
778
779 $rows = array();
780 foreach (rdf_get_repositories() as $name => $info) {
781 $module = unserialize(db_result(db_query("SELECT info FROM {system} WHERE type = 'module' AND name = '%s'", $info['module'])));
782
783 $mutable = !empty($info['mutable']) && !in_array($name, array('system', 'local', 'default')); // FIXME
784 $rows[] = array(
785 !$mutable ? $info['title'] : l($info['title'], 'admin/settings/rdf/repositories/' . $info['module'] . '/edit/' . $name,
786 array('attributes' => array('title' => @$info['dc:description']))),
787 $module ? $module['name'] : $info['module'],
788 $name == 'system' ? t('n/a') : (empty($info['statements']) ? '-' : number_format($info['statements'])),
789 !empty($info['mutable']) ? t('Yes') : t('No'),
790 !$mutable ? '' : l(t('edit'), 'admin/settings/rdf/repositories/' . $info['module'] . '/edit/' . $name),
791 !$mutable ? '' : l(t('delete'), 'admin/settings/rdf/repositories/' . $info['module'] . '/delete/' . $name),
792 );
793 }
794
795 return theme('table', $header, $rows);
796 }
797
798 function rdf_db_admin_db_edit($name = '') {
799 if (empty($name)) {
800 return drupal_get_form('rdf_db_admin_db_form');
801 }
802 else {
803 return drupal_get_form('rdf_db_admin_db_form', rdf_get_repository($name));
804 }
805 }
806
807 function rdf_db_admin_db_form(&$form_state, $edit = array('name' => '', 'title' => '', 'description' => '')) {
808 $edit = (object)$edit;
809 $form = array();
810
811 $form['identity'] = array('#type' => 'fieldset', '#title' => t('Identification'));
812
813 $form['identity']['title'] = array('#type' => 'textfield', '#title' => t('Name'), '#default_value' => $edit->title, '#maxlength' => 64, '#required' => TRUE, '#description' => t('The human-readable name of this repository. It is recommended that this name begins with a capital letter and consists only of letters, numbers, and spaces.'));
814 $form['identity']['name'] = array('#type' => 'textfield', '#title' => t('ID'), '#default_value' => $edit->name, '#maxlength' => 32, '#required' => TRUE, '#description' => t('The machine-readable name of this repository. This text will be used for constructing the unique URI identifying this repository. This name may consist of only of lowercase letters, numbers, and underscores. Hyphens are not allowed. Underscores will be converted into hyphens when constructing the URI for the repository. This name must be unique to this repository.'));
815 $form['identity']['description'] = array('#title' => t('Description'), '#type' => 'textarea', '#default_value' => $edit->description, '#rows' => 2, '#description' => t('A brief description of this repository.'));
816
817 $form['key'] = array('#type' => 'hidden', '#value' => $edit->name);
818 $form['submit'] = array('#type' => 'submit', '#value' => empty($edit->name) ? t('Create new repository') : t('Update repository'));
819
820 return $form;
821 }
822
823 function rdf_db_admin_db_form_validate($form, &$form_state) {
824 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
825
826 if (!preg_match('/^[a-z]+[a-z\d_]*$/', $name)) {
827 form_set_error('name', t('The machine-readable name can only consist of lowercase letters, underscores, and numbers.', array('%name' => $name)));
828 }
829
830 if (array_search($name, rdf_get_tables()) !== FALSE) { // FIXME
831 form_set_error('name', t('The machine-readable name %name is already used by another repository.', array('%name' => $name)));
832 }
833 }
834
835 function rdf_db_admin_db_form_submit($form, &$form_state) {
836 extract($form_state['values'], EXTR_SKIP | EXTR_REFS);
837
838 if (empty($key)) {
839 rdf_db_create_repository($name, array('title' => $title, 'description' => $description));
840 drupal_set_message(t('The repository has been created.'));
841 }
842 else {
843 if ($key != $name) {
844 rdf_db_rename_repository($key, $name);
845 }
846 rdf_db_update_repository($name, array('title' => $title, 'description' => $description));
847 drupal_set_message(t('The repository has been updated.'));
848 }
849
850 $form_state['redirect'] = 'admin/settings/rdf/repositories';
851 }
852
853 function rdf_db_admin_db_delete($form_state, $name) {
854 if (user_access('administer RDF repositories')) {
855 $form['name'] = array('#type' => 'value', '#value' => $name);
856 $output = confirm_form($form,
857 t('Are you sure you want to delete the RDF repository %title?', array('%title' => $name)),
858 isset($_GET['destination']) ? $_GET['destination'] : 'admin/settings/rdf/repositories',
859 t('This action will destroy all data contained in the repository and cannot be undone.'));
860 }
861 return $output;
862 }
863
864 function rdf_db_admin_db_delete_submit($form, &$form_state) {
865 if ($form_state['values']['confirm']) {
866 rdf_db_delete_repository($form_state['values']['name']);
867 drupal_set_message(t('The repository has been deleted.'));
868
869 $form_state['redirect'] = 'admin/settings/rdf/repositories';
870 }
871 }
872
873 //////////////////////////////////////////////////////////////////////////////
874 // RDF data import
875
876 function rdf_import_admin_screen(&$form_state, $edit = array('repository' => 'local', 'url' => 'http://', 'format' => RDF_FORMAT)) {
877 $edit = (object)$edit;
878 $form = array();
879
880 foreach (array('repository', 'url', 'format') as $key) {
881 if (isset($_GET[$key])) {
882 // This is idiotic, but due to drupal_urlencode() the URL has a
883 // double-escaped slash character that we need to get rid of...
884 // @see http://api.drupal.org/api/function/drupal_urlencode/6
885 $edit->$key = ($key == 'url') ? str_replace('/%2F', '//', $_GET[$key]) : $_GET[$key];
886 }
887 }
888
889 $form['import'] = array('#type' => 'fieldset', '#title' => t('Import RDF data from a URL'));
890 $form['import']['repository'] = array(
891 '#type' => 'select',
892 '#title' => t('Repository'),
893 '#default_value' => $edit->repository,
894 '#options' => rdf_get_repositories('names', array('persistent' => TRUE,