Backport of new data model / form structure.
[mkdru-moved-to-drupal.org.git] / mkdru.module
1 <?php
2 // $Id$
3
4
5
6 // Module metainfo
7 /**
8 * Implements hook_node_info()
9 */
10 function mkdru_node_info() {
11   return array(
12     'mkdru' => array(
13       'name' => t("Pazpar2 metasearch interface"),
14       'module' => 'mkdru',
15       'description' => t("Metasearch interface for Z39.50/SRU and other targets via a Pazpar2/Service Proxy backend"),
16     )
17   );
18 }
19
20 function mkdru_ting_search_tab($keys) {
21   error_log("TING SEARCH TAB invoked");
22    $path = drupal_get_path('module', 'mkdru');
23   // Include client library.
24   drupal_add_js(variable_get('pz2_js_path', 'pazpar2/js') 
25     . '/pz2.js', 'module', 'footer');
26   drupal_add_js($path . '/jquery.ba-bbq.js', 'module', 'footer');
27   drupal_add_js($path . '/recipe.js', 'module', 'footer');
28   drupal_add_js($path . '/mkdru.theme.js', 'module', 'footer');
29   drupal_add_js($path . '/mkdru.client.js', 'module', 'footer');
30   $html = theme('mkdru_results');
31   drupal_add_js(array('mkdru' => 
32     array(
33       'settings' => variable_get('mkdru_ding'),
34       'pz2_path' => variable_get('pz2_path', '/pazpar2/search.pz2'),
35       'query' => $keys
36     )
37   ), 'setting');
38   return array("content" => $html, "title" => "Meta Search");
39 }
40
41 /**
42 * Implements hook_perm()
43 */
44 function mkdru_perm() {
45   return array('create metasearch interface', 'edit any metasearch interface', 'edit own metasearch interface');
46 }
47
48 /**
49 * Implements hook_access()
50 */
51 function mkdru_access($op, $node, $account) {
52
53   if ($op == 'create') {
54     // Only users with permission to do so may create this node type.
55     return user_access('create metasearch interface', $account);
56   }
57
58   // Users who create a node may edit or delete it later, assuming they have the
59   // necessary permissions.
60   if ($op == 'update' || $op == 'delete') {
61     if (user_access('edit own metasearch interface', $account) && ($account->uid == $node->uid)) {
62       return TRUE;
63     }
64     elseif (user_access('edit any metasearch interface', $account)) {
65       return TRUE;
66     }
67   }
68 }
69
70 /**
71 * Implements hook_menu()
72 */
73 function mkdru_menu() {
74   $items['admin/settings/mkdru'] = array(
75     'title' => 'Pazpar2 Metasearch Settings',
76     'description' => 'Settings for mkdru.',
77     'page callback' => 'drupal_get_form',
78     'page arguments' => array('mkdru_admin_settings'),
79     'access arguments' => array('administer site configuration'),
80     'type' => MENU_NORMAL_ITEM,
81     'file' => 'mkdru.admin.inc',
82   );
83   $items['admin/settings/mkdru-ding'] = array(
84     'title' => 'Pazpar2 Metasearch Ding Integration',
85     'description' => 'Search settings for mkdru instance integrated into Ding.',
86     'page callback' => 'drupal_get_form',
87     'page arguments' => array('mkdru_ding_settings'),
88     'access arguments' => array('administer site configuration'),
89     'type' => MENU_NORMAL_ITEM,
90   );
91   return $items;
92 }
93
94 /**
95 * Implements hook_init()
96 */
97 function mkdru_init() {
98   // Applies our module specific CSS to all pages. This works best because
99   // all CSS is aggregated and cached so we reduce the number of HTTP 
100   // requests and the size is negligible.
101   drupal_add_css(drupal_get_path('module', 'mkdru') .'/mkdru.css');
102 }
103
104
105 // Config form common to node and settings
106 // function mkdru_settings_form($form, &$form_state) {
107 function mkdru_settings_form(&$form_state) {
108 //   dpm($form_state);
109   if (isset($form_state['values']['settings'])) {
110     $settings = $form_state['build_info']['args']['settings'];
111   }
112   else if (isset($form_state['build_info']['args']['settings'])) {
113     $settings = $form_state['build_info']['args']['settings'];
114   }
115   else {
116     $settings = variable_get('mkdru_defaults', NULL);
117   }
118
119   $form['settings'] = array(
120     '#tree' => TRUE,
121   );
122
123   $form['settings']['pz2_path'] = array(
124     '#type' => 'textfield',
125     '#title' => t('Pazpar2/Service Proxy path'),
126     '#description' => t('Path that takes Pazpar2 commands via HTTP'),
127     '#required' => TRUE,
128     '#default_value' => $settings['pz2_path'],
129   );
130   $form['settings']['use_sessions'] = array(
131     '#type' => 'checkbox',
132     '#title' => t('Session handling'),
133     '#description' => t('Disable for use with Service Proxy'),
134     '#default_value' => $settings['use_sessions'],
135   );
136
137   $form['settings']['sp']  = array(
138     '#type' => 'fieldset',
139     '#title' => t('Service Proxy specific settings'),
140     '#collapsible' => TRUE,
141     '#collapsed' => FALSE
142   );
143   $form['settings']['sp']['user'] = array(
144     '#type' => 'textfield',
145     '#title' => t('Service Proxy username'),
146     '#description' => t('Service Proxy username'),
147     '#required' => FALSE,
148     '#default_value' => $settings['sp']['user'],
149   );
150   $form['settings']['sp']['pass'] = array(
151     '#type' => 'password',
152     '#title' => t('Service Proxy password'),
153     '#description' => t('Service Proxy password'),
154     '#required' => FALSE,
155     '#default_value' => $settings['sp']['pass'],
156   );
157
158   $form['settings']['facets']  = array(
159     '#type' => 'fieldset',
160     '#title' => t('Facet settings'),
161     // Set up the wrapper so that AJAX will be able to replace the fieldset.
162     '#prefix' => '<div id="mkdru-facets-form-wrapper">', 
163     '#suffix' => '</div>',
164     '#collapsible' => TRUE,
165     '#collapsed' => FALSE
166   );
167   if (empty($form_state['facet_names'])) {
168     $form_state['facet_names'] = array('source', 'author', 'subject');
169   }
170   foreach ($form_state['facet_names'] as $facet) {
171     $form['settings']['facets'][$facet]  = array(
172       '#type' => 'fieldset',
173       '#title' => $facet . ' ' . t('facet'),
174       '#collapsible' => TRUE,
175       '#collapsed' => FALSE
176     );
177     $form['settings']['facets'][$facet]['displayName'] = array(
178       '#type' => 'textfield',
179       '#title' => t('Facet name to display in UI'),
180       '#required' => TRUE,
181       '#default_value' => $settings['facets'][$facet]['displayName'],
182     );
183     $form['settings']['facets'][$facet]['pz2Name'] = array(
184       '#type' => 'textfield',
185       '#title' => t('Name of termlist in Pazpar2'),
186       '#required' => TRUE,
187       '#default_value' => $settings['facets'][$facet]['pz2Name'],
188     );
189     $form['settings']['facets'][$facet]['limiter'] = array(
190       '#type' => 'textfield',
191       '#title' => t('Query limiter string'),
192       '#default_value' => $settings['facets'][$facet]['limiter'],
193       '#size' => 5,
194     );
195     $form['settings']['facets'][$facet]['max'] = array(
196       '#type' => 'textfield',
197       '#title' => t('Number of terms to display'),
198       '#required' => TRUE,
199       '#default_value' => $settings['facets'][$facet]['max'],
200       '#size' => 3,
201       '#maxlength' => 3,
202     );
203     $form['settings']['facets'][$facet]['orderWeight'] = array(
204       '#type' => 'textfield',
205       '#title' => t('Facet weight'),
206       '#default_value' => $settings['facets'][$facet]['orderWeight'],
207       '#size' => 3,
208       '#maxlength' => 3,
209     );
210   }
211 //   $form['new_facet']  = array(
212 //     '#type' => 'fieldset',
213 //     '#title' => t('Add new facet...'),
214 //     '#tree' => TRUE,
215 //     '#collapsible' => TRUE,
216 //     '#collapsed' => FALSE
217 //   );
218 //   $form['new_facet']['canonical'] = array(
219 //     '#type' => 'textfield',
220 //     '#title' => t('Canonical name of new facet'),
221 //   );
222 //   $form['new_facet']['button'] = array(
223 //     '#type' => 'submit',
224 //     '#value' => t('Add facet'),
225 //     '#description' => t('Configure additional facets based on Pazpar2/SP termlists'),
226 //     '#weight' => 1,
227 //     '#submit' => array('mkdru_add_facet_form'),
228 //     '#ajax' => array(
229 //       'callback' => 'mkdru_add_facet_callback', 
230 //       'wrapper' => 'mkdru-facets-form-wrapper',
231 //     ),
232 //   );
233   return $form;
234 }
235
236
237
238 // Ding config
239 function mkdru_ding_settings(&$form_state) {
240   $form_state['build_info']['args']['settings'] = variable_get('mkdru_ding', NULL);
241   $form = drupal_retrieve_form('mkdru_settings_form', &$form_state);
242   $form['settings']['#title'] = t('Default search settings');
243   $form['submit'] = array(
244     '#type' => 'submit',
245     '#value' => 'Save configuration',
246   );
247   return $form;
248 }
249 function mkdru_ding_settings_submit($form, &$form_state) {
250   dpm($form_state);
251   variable_set('mkdru_ding', $form_state[values][settings]);
252   drupal_set_message(t('The configuration options have been saved.'));
253 }
254
255
256
257 // Node config
258 /**
259 * Implements hook_form()
260 */
261 function mkdru_form(&$node, &$form_state) {
262   if (isset($node->settings)) {
263     // Second decode parameter indicates associative array
264     $form_state['build_info']['args']['settings'] = json_decode($node->settings, TRUE);
265   }
266
267   $form = drupal_retrieve_form('mkdru_settings_form', &$form_state);
268
269   $type = node_get_types('type', $node);
270   $form['title'] = array(
271     '#type' => 'textfield',
272     '#title' => check_plain($type->title_label),
273     '#required' => FALSE,
274     '#default_value' => $node->title,
275     '#weight' => -5
276   );
277   return $form;
278 }
279
280
281 /**
282 * Implements hook_validate()
283 */
284 function mkdru_validate($node) {
285   // TODO: validation
286 }
287
288 /**
289 * Implements hook_insert().
290 */
291 function mkdru_insert($node) {
292   $record['nid'] =  $node->nid;
293   $record['vid'] =  $node->vid;
294   $record['settings'] =  json_encode($node->settings);
295   drupal_write_record('mkdru', &$record);
296 }
297
298 /**
299 * Implements hook_update().
300 */
301 function mkdru_update($node) {
302   if ($node->revision) {
303     // New revision; treat it as a new record.
304     mkdru_insert($node);
305   }
306   else {
307     $record['nid'] =  $node->nid;
308     $record['vid'] =  $node->vid;
309     $record['settings'] =  json_encode($node->settings);
310     drupal_write_record('mkdru', &$record, 'vid');
311   }
312 }
313
314 /**
315  * Implements hook_nodeapi().
316  *
317  * When a node revision is deleted, we need to remove the corresponding record
318  * from our table. The only way to handle revision deletion is by implementing
319  * hook_nodeapi().
320  */
321 function mkdru_nodeapi(&$node, $op, $teaser, $page) {
322   switch ($op) {
323     case 'delete revision':
324       db_query('DELETE FROM {mkdru} WHERE vid = %d', $node->vid);
325       break;
326   }
327 }
328
329 /**
330  * Implements hook_delete().
331  */
332 function mkdru_delete($node) {
333   // Deleting by nid covers all revisions.
334   db_query('DELETE FROM {mkdru} WHERE nid = %d', $node->nid);
335 }
336
337
338
339 // Node rendering
340 /**
341 * Implements hook_load()
342 */
343 function mkdru_load($node) {
344   return db_fetch_object(db_query('SELECT * FROM {mkdru} WHERE vid = %d', $node->vid));
345 }
346
347 /**
348 * Implements hook_theme().
349 */
350 function mkdru_theme() {
351   return array(
352     'mkdru_form' => array(
353       'template' => 'mkdru-form',
354       'arguments' => array(),
355     ),
356     'mkdru_results' => array(
357       'template' => 'mkdru-results',
358       'arguments' => array(),
359     ),
360     'mkdru_js' => array(
361       'arguments' => array('node' => NULL),
362     ),
363     'mkdru_block_search' => array(
364       'template' => 'mkdru-block-search',
365       'arguments' => array('nid' => NULL, 'path' => NULL),
366     ),
367     'mkdru_block_facet' => array(
368       'template' => 'mkdru-block-facet',
369       'arguments' => array('class' => NULL)
370     )
371   );
372 }
373
374 /**
375 * Theme function to include Javascript search client and deps
376 */
377 function theme_mkdru_js($node) {
378   $path = drupal_get_path('module', 'mkdru');
379   // Pazpar2 client library.
380   drupal_add_js(variable_get('pz2_js_path', 'pazpar2/js') . '/pz2.js', 'module', 'footer', TRUE, TRUE, FALSE);
381   // jQuery plugin for query string/history manipulation.
382   drupal_add_js($path . '/jquery.ba-bbq.js', 'module', 'footer', TRUE, TRUE, FALSE);
383   drupal_add_js($path . '/mkdru.theme.js', 'module', 'footer', TRUE, TRUE, FALSE);
384   drupal_add_js($path . '/mkdru.client.js', 'module', 'footer', TRUE, TRUE, FALSE);
385   drupal_add_js(array('mkdru' => $node->mkdru), 'setting');
386   drupal_add_js(array('mkdru' => 
387     array(
388       'settings' => $node->settings,
389     )
390   ), 'setting');
391 }
392
393 /** 
394 * Implements hook_view()
395 */
396 function mkdru_view($node, $teaser = FALSE, $page = FALSE) {
397   $node->content['mkdru_js'] = array(
398     '#value' => theme('mkdru_js', $node), 
399     '#weight' => 0,
400   );
401   $node->content['mkdru_form'] = array(
402     '#value' => theme('mkdru_form'), 
403     '#weight' => 1,
404   );
405   $node->content['mkdru_results'] = array(
406     '#value' => theme('mkdru_results'), 
407     '#weight' => 2,
408   );
409   return $node;
410 }
411
412 /** 
413 * Implements hook_block()
414 */
415 function mkdru_block($op='list', $delta='sources', $edit=array()) {
416   switch ($op) {
417     case 'list':
418       // facet blocks
419       // D6 has no setting for note type visibility, set
420       // the default to limit facet display to this type
421       $visPHP = '<?php
422   if (arg(0) == "node" && is_numeric(arg(1))) {
423     $node = node_load(array("nid" => arg(1)));
424     return $node->type == "mkdru";
425   }
426 ?>';
427
428       // NB: block caching is redundant for static content
429       $blocks['mkdru_sources']['info'] = t('mkdru - source facets');
430       $blocks['mkdru_sources']['cache'] = BLOCK_NO_CACHE;
431       $blocks['mkdru_sources']['visibility'] = 2;
432       $blocks['mkdru_sources']['pages'] = $visPHP;
433       $blocks['mkdru_subjects']['info'] = t('mkdru - subject facets');
434       $blocks['mkdru_subjects']['cache'] = BLOCK_NO_CACHE;
435       $blocks['mkdru_subjects']['visibility'] = 2;
436       $blocks['mkdru_subjects']['pages'] = $visPHP;
437       $blocks['mkdru_authors']['info'] = t('mkdru - author facets');
438       $blocks['mkdru_authors']['cache'] = BLOCK_NO_CACHE;
439       $blocks['mkdru_authors']['visibility'] = 2;
440       $blocks['mkdru_authors']['pages'] = $visPHP;
441       // search blocks
442       $result = db_query("SELECT title, nid FROM {node} WHERE type = 'mkdru';");
443       while ($node = db_fetch_object($result)) {
444         $blocks['mkdru_search_' . $node->nid]['info'] = 
445            t('mkdru - search box for "' . $node->title . '"');
446         $blocks['mkdru_sources']['cache'] = BLOCK_NO_CACHE;
447       };
448       return $blocks;
449
450     case 'view':
451       switch ($delta) {
452         case 'mkdru_sources':
453           $block['subject'] = t('Source');
454           $block['content'] = theme('mkdru_block_facet', 'mkdru-facet-source');
455           return $block;
456         case 'mkdru_subjects':
457           $block['subject'] = t('Subject');
458           $block['content'] = theme('mkdru_block_facet', 'mkdru-facet-subject');
459           return $block;
460         case 'mkdru_authors':
461           $block['subject'] = t('Author');
462           $block['content'] = theme('mkdru_block_facet', 'mkdru-facet-author');
463           return $block;
464     }
465     if (substr($delta, 0, 13) == 'mkdru_search_') {
466       $nid = substr($delta, 13);
467       $block['content'] = theme('mkdru_block_search', $nid, '/node/' . $nid);
468       return $block;
469     }
470   }
471 }