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

Contents of /contributions/modules/object_driver/object_driver.module

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


Revision 1.10 - (show annotations) (download) (as text)
Thu Sep 13 01:59:21 2007 UTC (2 years, 2 months ago) by pwolanin
Branch: MAIN
CVS Tags: DRUPAL-5--1-0, HEAD
Changes since 1.9: +13 -13 lines
File MIME type: text/x-php
#159096 fix table prefixing
1 <?php
2 // $Id: object_driver.module,v 1.9 2007/04/08 16:45:03 pwolanin Exp $
3
4 /**
5 * hooks this module looks for (hook -> module name, classhook -> class name):
6 *
7 * hook_data_classes(): returns an array of class names
8 *
9 * classhook_class_define(): return a definition for the object's data table.
10 * classhook_class_access($op): $op = 'create', 'view', 'update', 'delete'
11 *
12 * The primary key of the data table must have the name classhook_id
13 *
14 *
15 * optional (overrides default functionality)- functionally similar to node module functions
16 *
17 * classhook_class_new(): return an object with all keys set to default values
18 * classhook_class_load($class, $id) : returns a full object according to $id
19 * classhook_class_validate($obj): sets errors on the edit form
20 * classhook_class_submit(&$obj): fix up the upject before saving, after validation
21 * classhook_class_save($obj): save the new or updated data to the module's table
22 * classhook_class_delete($obj): delete an object
23 * classhook_class_edit_form($obj): an editing form
24 * classhook_class_view_list(): a list or table of data
25 * classhook_class_view($obj): a view of a single data item
26 *
27 *
28 * optionally implemented by a 3rd module to alter the behavior/data of a class
29 *
30 * hook_objectapi_X($obj): X = load, validate, submit, insert, update, delete, view
31 */
32
33 function object_driver_perm() {
34 return array('access object driver data');
35 }
36
37 function object_driver_menu($may_cache) {
38 $items = array();
39
40 if ($may_cache) {
41 $class_list = object_driver_class_list(TRUE);
42 if (count($class_list)) {
43 $items[] = array(
44 'path' => 'add-data',
45 'title' => t('Data add'),
46 'callback' => 'object_driver_add_data',
47 'access' => user_access('access object driver data'),
48 );
49 $items[] = array(
50 'path' => 'view-data',
51 'title' => t('Data view'),
52 'callback' => 'object_driver_view_data',
53 'access' => user_access('access object driver data'),
54 );
55 foreach ($class_list as $class) {
56 $obj_def = call_user_func($class .'_class_define');
57 if (!empty($obj_def['auto_menu'])) {
58 $items[] = array(
59 'path' => 'add-data/'. $class,
60 'title' => t('Add @class', array('@class' => ucfirst($class))),
61 'access' => object_driver_access('create', $class),
62 );
63 $items[] = array(
64 'path' => 'view-data/'. $class,
65 'title' => t('@class overview', array('@class' => ucfirst($class))),
66 'access' => object_driver_access('view', $class),
67 );
68 }
69 }
70 }
71 }
72 else {
73 $class_list = object_driver_class_list();
74 foreach ($class_list as $class) {
75 if (arg(0) == $class && is_numeric(arg(1))) {
76 $obj_def = call_user_func($class .'_class_define');
77 if (!empty($obj_def['auto_menu'])) {
78 $id_name = $class .'_id';
79 $obj = object_driver_load($class, arg(1));
80 if ($id = $obj->$id_name) {
81 $items[] = array('path' => $class. '/'. $id, 'title' => t('View @class', array('@class' => $class)),
82 'callback' => 'object_driver_view',
83 'callback arguments' => array($obj),
84 'access' => object_driver_access('view', $class),
85 'type' => MENU_CALLBACK);
86 $items[] = array('path' => $class. '/'. $id .'/view', 'title' => t('View'),
87 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
88 $items[] = array('path' => $class. '/'. $id .'/edit', 'title' => t('Edit'),
89 'callback' => 'object_driver_edit_page',
90 'callback arguments' => array($obj),
91 'access' => object_driver_access('update', $class),
92 'weight' => 1,
93 'type' => MENU_LOCAL_TASK);
94 $items[] = array('path' => $class. '/'. $id .'/delete', 'title' => t('Delete'),
95 'callback' => 'drupal_get_form',
96 'callback arguments' => array('object_driver_delete_confirm', $obj),
97 'access' => object_driver_access('delete', $class),
98 'weight' => 1,
99 'type' => MENU_CALLBACK);
100 }
101 }
102 }
103 }
104 }
105
106 return $items;
107 }
108
109 function object_driver_access($op, $class) {
110
111 if (function_exists($class .'_class_access')) {
112 $function = $class .'_class_access';
113 return $function($op);
114 }
115 drupal_set_message(t('No access function is defined for object_driver class %class', array('%class' => $class)), 'error');
116 return TRUE;
117 }
118
119 function object_driver_add_data($class = '') {
120
121 $class_list = object_driver_class_list();
122 if ($class && in_array($class, $class_list)) {
123 if (function_exists($class .'_class_new')) {
124 $obj = call_user_func($class .'_class_new');
125 }
126 else { // default functionality
127 $obj = new stdClass();
128 $obj->class = $class;
129 $obj_def = call_user_func($class .'_class_define');
130 $fields = $obj_def['columns'];
131 foreach ($fields as $key => $data) {
132 $obj->$key = isset($data['default']) ? $data['default'] : '';
133 }
134 }
135 return drupal_get_form($class .'_class_edit_form', $obj);
136 }
137 elseif (count($class_list)) {
138 $links = array();
139 foreach ($class_list as $class) {
140 $obj_def = call_user_func($class .'_class_define');
141 if (!empty($obj_def['auto_menu'])) {
142 if (object_driver_access('create', $class)) {
143 $links[$class]['href'] = l(ucfirst($class), 'add-data/'. $class);
144 $links[$class]['description'] = isset($obj_def['description']) ? $obj_def['description'] : '';
145 }
146 }
147 }
148 return theme('object_driver_add_links', $links);
149 }
150 }
151
152 function theme_object_driver_add_links($links) {
153 $output = '';
154
155 if (count($links)) {
156 uksort($links, 'strnatcasecmp');
157 $output .= t('Choose the appropriate item from the list:') .'<dl>';
158 foreach ($links as $class => $link) {
159 $output .= '<dt>'. $link['href'] .'</dt>';
160 $output .= '<dd>'. $link['description'] .'</dd>';
161 }
162 $output .= '</dl>';
163 }
164 else {
165 $output = t('No data types are available.');
166 }
167 return $output;
168 }
169
170 function object_driver_view_data($class = '') {
171
172 $class_list = object_driver_class_list();
173 if ($class && in_array($class, $class_list)) {
174 if (function_exists($class .'_class_view_list')) {
175 return call_user_func($class .'_class_view_list');
176 }
177 else { // default functionality
178 $obj_def = call_user_func($class .'_class_define');
179 $fields = $obj_def['columns'];
180 $cols = array_keys($fields);
181 $id_name = $class .'_id';
182 $query_cols = array($id_name);
183 $next = array_shift($cols);
184 while ($next && count($query_cols) < 7) { // TODO make this number changeable?
185 $query_cols[] = $next;
186 $next = array_shift($cols);
187 }
188 // drupal_set_message(print_r($query_cols,1));
189 $table = isset($obj_def['table']) ? $obj_def['table'] : $class;
190 $count_sql = "SELECT COUNT(*) FROM {". $table ."}";
191 $sql = "SELECT ". implode(', ', $query_cols) ." FROM {". $table ."} ORDER BY ". $id_name;
192 $result = pager_query($sql, 25, 0, $count_sql); // TODO make this number changeable?
193 return theme('object_driver_show_list', $result, $class, $query_cols, $fields);
194 }
195
196 }
197 elseif (count($class_list)) {
198 $links = array();
199 foreach ($class_list as $class) {
200 $obj_def = call_user_func($class .'_class_define');
201 if (!empty($obj_def['auto_menu'])) {
202 if (object_driver_access('view', $class)) {
203 $links[$class]['href'] = l(ucfirst($class), 'view-data/'. $class);
204 $links[$class]['description'] = isset($obj_def['description']) ? $obj_def['description'] : '';
205 }
206 }
207 }
208 return theme('object_driver_view_links', $links);
209 }
210 }
211
212 function theme_object_driver_view_links($links) {
213 return theme_object_driver_add_links($links);
214 }
215
216 function theme_object_driver_show_list($result, $class, $query_cols, $fields) {
217
218 $id_name = array_shift($query_cols);
219 $rows = array();
220 $edit = object_driver_access('update', $class);
221
222 while ($obj = db_fetch_object($result)) {
223
224 $arow = array(
225 l(t('View'), $class ."/". $obj->$id_name));
226 $arow[] = ($edit) ? l(t('Edit'), $class ."/". $obj->$id_name ."/edit") : '&nbsp;';
227 foreach($query_cols as $key) {
228 switch ($fields[$key]['form type']) {
229 case 'radios':
230 case 'select':
231 $output = $fields[$key]['options'][$obj->$key];
232 $type = $key.'_'.$obj->$key;
233 break;
234 default:
235 $output = check_plain(truncate_utf8($obj->$key, 15));
236 $type = $key;
237 }
238 $arow[] = array('data' => $output, 'class' => $type);
239 }
240
241 $rows[] =$arow;
242 }
243
244 if (!$rows) {
245 $rows[] = array(array('data' => t('No data available.'), 'colspan' => '3'));
246 }
247
248 $header = array(array('data' => t('Operations'), 'colspan' => '2'));
249 foreach($query_cols as $key) {
250 $header[] = $fields[$key]['name'];
251 }
252
253 $output = '<div id="object-driver-'. $class .'">';
254 $output .= theme('table', $header, $rows);
255 $output .= theme('pager', array(), 25); // TODO make this number changeable?
256 $output .= '</div>';
257
258 return $output;
259
260 }
261
262 /**
263 * Menu callback; returns the object editing form, or redirects to delete confirmation.
264 */
265 function object_driver_edit_page($obj) {
266
267 $class = $obj->class;
268 $id_name = $class .'_id';
269 if ($_POST['op'] == t('Delete')) {
270 // Note: we redirect to make the tabs disappear.
271 if ($_REQUEST['destination']) {
272 $destination = drupal_get_destination();
273 unset($_REQUEST['destination']);
274 }
275 drupal_goto($class .'/'. $obj->$id_name .'/delete', $destination);
276 }
277
278 return drupal_get_form($class .'_class_edit_form', $obj);
279 }
280
281 /**
282 * Implementation of hook_forms(). All object forms share the same default handler
283 */
284 function object_driver_forms() {
285 foreach (object_driver_class_list() as $class) {
286 $forms[$class .'_class_edit_form']['callback'] = 'object_driver_form';
287 }
288 return $forms;
289 }
290
291 function object_driver_form($obj) {
292 $class = $obj->class;
293
294 $form = array();
295 $obj_def = call_user_func($class .'_class_define');
296 $fields = $obj_def['columns'];
297
298 $id_name = $class .'_id';
299
300 $form['class'] = array(
301 '#type' => 'value',
302 '#value' => $class,
303 );
304
305 if (isset($obj->$id_name)) {
306 $form[$id_name] = array(
307 '#type' => 'value',
308 '#value' => $obj->$id_name,
309 );
310 }
311 foreach ($fields as $key => $data) {
312 switch ($data['form type']) {
313 case 'radios':
314 case 'select':
315 $form[$key] = array(
316 '#type' => $data['form type'],
317 '#default_value' => $obj->$key,
318 '#options' => $data['options'],
319 '#required' => TRUE,
320 );
321 break;
322 case 'textarea':
323 $rows = isset($data['rows']) ? $data['rows'] : 5;
324 $form[$key] = array(
325 '#type' => 'textarea',
326 '#default_value' => $obj->$key,
327 '#rows' => $rows,
328 );
329 break;
330 case 'textfield':
331 default:
332 $maxlength = isset($data['maxlength']) ? $data['maxlength'] : 128;
333 $size = $maxlength < 60 ? $maxlength : 60;
334 $form[$key] = array(
335 '#type' => 'textfield',
336 '#default_value' => $obj->$key,
337 '#size' => $size,
338 '#maxlength' => $maxlength,
339 );
340
341 }
342 $form[$key]['#title'] = $data['name'];
343 $form[$key]['#required'] = !empty($data['required']);
344 }
345
346 $form['#id'] = $class .'-form';
347
348 $form['#base'] = 'object_driver_form';
349
350 $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 45);
351 if (isset ($obj->$id_name) && object_driver_access('delete', $class)) {
352 $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'), '#weight' => 50);
353 }
354
355 return $form;
356 }
357
358 function object_driver_form_validate($form_id, $form_values) {
359 $obj = (object)$form_values;
360 if (function_exists($class .'_validate')) {
361 $function = $class .'_validate';
362 $function($obj); // For pass-by-reference
363 }
364 object_driver_invoke_all('validate', $obj);
365 }
366
367 function object_driver_form_submit($form_id, $form_values) {
368 $obj = (object)$form_values;
369 $class = $obj->class;
370
371 if (function_exists($class .'_class_submit')) {
372 $function = $class .'_class_submit';
373 $function($obj); // For pass-by-reference
374 }
375
376 object_driver_invoke_all('submit', $obj);
377 object_driver_save($obj);
378
379 $id_name = $class.'_id';
380 if (isset($obj->$id_name)) {
381 if (object_driver_access('view', $class)) {
382 return $class .'/'. $obj->$id_name;
383 }
384 }
385 return '';
386 }
387
388 function object_driver_view($obj) {
389 $class = $obj->class;
390
391 $rows = array();
392 $obj_def = call_user_func($class .'_class_define');
393 $fields = $obj_def['columns'];
394
395 foreach($fields as $key => $data) {
396 $arow = array($data['name']);
397 switch ($data['form type']) {
398 case 'radios':
399 case 'select':
400 $output = $data['options'][$obj->$key];
401 $type = $key.'_'.$obj->$key;
402 break;
403 default:
404 $output = check_plain($obj->$key);
405 $type = $key;
406 }
407 $rows[] = array($data['name'], array('data' => $output, 'class' => $type));
408 }
409
410 return theme('table', array(), $rows) . object_driver_navigation($obj);
411 }
412
413 function object_driver_navigation($obj) {
414 //TODO
415 return '';
416 }
417
418 function object_driver_class_list($rebuild = FALSE) {
419 $list = variable_get('object_driver_class_list', NULL);
420
421 if (is_null($list) || $rebuild) {
422 $list = array();
423 foreach (module_implements('data_classes') as $module) {
424 $result = call_user_func($module .'_data_classes');
425 if (isset($result) && is_array($result)) {
426 $list = array_merge($list, $result);
427 }
428 }
429 variable_set('object_driver_class_list', $list);
430 }
431 return $list;
432 }
433
434 function object_driver_enable() {
435 object_driver_class_list(TRUE);
436 }
437
438 function object_driver_disable() {
439 object_driver_class_list(TRUE);
440 }
441
442 function object_driver_load($class, $id, $reset = FALSE) {
443 static $obj_cache = array();
444
445 if ($reset) {
446 unset($obj_cache[$class]);
447 }
448
449 if (isset($obj_cache[$class][$id])) {
450 return is_object($obj_cache[$class][$id]) ? drupal_clone($obj_cache[$class][$id]) : $obj_cache[$class][$id];
451 }
452 $extra = array();
453 $id_name = $class .'_id';
454
455 if (function_exists($class .'_load')) {
456 $obj = call_user_func($class .'_load', $id);
457 }
458 else { // Default functionality
459 $obj_def = call_user_func($class .'_class_define');
460 $table = isset($obj_def['table']) ? $obj_def['table'] : $class;
461
462 $obj = db_fetch_object(db_query("SELECT * FROM {". $table ."} WHERE %s = %d", $id_name, $id));
463
464 }
465 if (isset($obj->$id_name)) {
466 $obj->class = $class;
467 $result = object_driver_invoke_all('load', $obj);
468 $extra = array_merge($extra, $result); // Use + instead?
469
470 foreach ($extra as $key => $value) {
471 $obj->$key = $value;
472 }
473 $obj->class = $class; // Just in case
474 }
475 $obj_cache[$class][$id] = $obj;
476 return is_object($obj_cache[$class][$id]) ? drupal_clone($obj_cache[$class][$id]) : $obj_cache[$class][$id];
477 }
478
479 function object_driver_save(&$obj) {
480 $class = $obj->class;
481 $id_name = $class .'_id';
482 // Is this a new object?
483 $obj->is_new = empty($obj->$id_name);
484
485 if (function_exists($class .'_save')) {
486 call_user_func($class .'_save', $obj);
487 }
488 else { // Default functionality
489 $obj_def = call_user_func($class .'_class_define');
490 $table = isset($obj_def['table']) ? $obj_def['table'] : $class;
491
492 if ($obj->is_new) {
493 // Insert a new object
494 $obj->$id_name = db_next_id("{". $table ."}_id");
495 }
496
497 $column_names = array();
498 $column_placeholders = array();
499 $column_assignments = array();
500 $col_type_map = array(
501 "int" => "%d", "mediumint" => "%d", "tinyint" => "%d", "bigint" => "%d", "smallint" => "%d",
502 "float" => "%f",
503 "blob" => "%b", "longblob" => "%b",
504 'text' => "'%s'", 'longtext' => "'%s'", 'varchar' => "'%s'",
505 );
506
507 foreach ($obj_def['columns'] as $column => $attrib) {
508 $column_names[] = $column;
509 $placeholder = isset($attrib['data type']) ? $col_type_map[$attrib['data type']] : "'%s'";
510 $column_placeholders[] = $placeholder;
511 $column_assignments[] = $column .' = '. $placeholder;
512 $data[] = $obj->$column;
513 }
514 $data[] = $obj->$id_name;
515 if ($obj->is_new) {
516 db_query("INSERT INTO {". $table ."} (". implode(', ', $column_names) .", $id_name) VALUES (". implode(', ', $column_placeholders) .', %d)', $data);
517 }
518 else {
519 db_query("UPDATE {". $table ."} SET ". implode(', ', $column_assignments) ." WHERE $id_name = %d", $data);
520 }
521 }
522 if ($obj->is_new) {
523 object_driver_invoke_all('insert', $obj);
524 }
525 else {
526 object_driver_invoke_all('update', $obj);
527 }
528 cache_clear_all();
529 }
530
531 function object_driver_delete_confirm($obj) {
532 $class = $obj->class;
533 $id_name = $class .'_id';
534 $form['obj'] = array(
535 '#type' => 'value',
536 '#value' => $obj,
537 );
538 $form['#submit'] = array('object_driver_delete_confirm_submit' => array());
539 $output = '';
540 if (object_driver_access('delete', $class)) {
541 $form = confirm_form($form,
542 t('Are you sure you want to delete this %class?', array('%class' => ucfirst($class))),
543 $_GET['destination'] ? $_GET['destination'] : $class .'/'. $obj->$id_name, t('This action cannot be undone.'),
544 t('Delete'), t('Cancel') );
545 }
546
547 return $form;
548 }
549
550 function object_driver_delete_confirm_submit($form_id, $form_values) {
551 object_driver_delete($form_values['obj']);
552
553 return "view-data/" . $form_values['obj']->class;
554 }
555
556 function object_driver_delete($obj) {
557
558 $class = $obj->class;
559
560 if (function_exists($class .'_delete')) {
561 $obj = call_user_func($class .'_delete', $obj);
562 }
563 else { // Default functionality
564 $obj_def = call_user_func($class .'_class_define');
565 $table = isset($obj_def['table']) ? $obj_def['table'] : $class;
566 $id_name = $class .'_id';
567
568 db_query("DELETE FROM {". $table ."} WHERE %s = %d", $id_name, $obj->$id_name);
569 if (isset($obj->type)) {
570 if (function_exists($obj->type .'_'. $class .'_delete')) {
571 call_user_func($obj->type .'_'. $class .'_delete', $obj);
572 }
573 }
574 }
575 object_driver_invoke_all('delete', $obj);
576 cache_clear_all();
577 }
578
579 function object_driver_invoke_all($op, &$obj) {
580 $data = array();
581
582 foreach (module_implements('objectapi_'. $op) as $name) {
583 $function = $name .'_objectapi_'. $op;
584 $result = $function($obj); // For pass-by-reference
585 if (isset($result) && is_array($result)) {
586 $data = array_merge($data, $result);
587 }
588 }
589 return $data;
590 }
591

  ViewVC Help
Powered by ViewVC 1.1.2