/[drupal]/contributions/modules/draggableviews/draggableviews.inc
ViewVC logotype

Contents of /contributions/modules/draggableviews/draggableviews.inc

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


Revision 1.12 - (show annotations) (download) (as text)
Thu Jan 8 22:38:36 2009 UTC (10 months, 2 weeks ago) by sevi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +113 -264 lines
File MIME type: text/x-php
Fundamental changes:
*) Introduction of handlers (handle saving and outputting)
1 <?php
2 // $Id: draggableviews.inc,v 1.7.2.1 2008/09/21 21:17:33 sevi Exp $
3
4 /**
5 * @file
6 * Draggableviews processing functions.
7 */
8
9 include_once('draggableviews_theme.inc');
10
11 /**
12 * Build the form
13 */
14 function draggableviews_view_draggabletable_form($form_state, $view) {
15 $form['submit'] = array(
16 '#type' => 'submit',
17 '#value' => t('Save'),
18 );
19
20 return $form;
21 }
22
23
24 /**
25 * Implementing hook_submit
26 */
27 function draggableviews_view_draggabletable_form_submit($vars) {
28 // check permissions
29 if (!user_access('Allow Reordering')) {
30 drupal_set_message(t('You are not allowed to reorder nodes.'), 'error');
31 return;
32 }
33
34 // gather all needed information
35 $view = $vars['#parameters'][2]->view;
36 $results = $view->result;
37 // get input
38 $input = $vars['submit']['#post'];
39
40 $info = _draggableviews_info($view);
41
42 if (!isset($info['order'])) return;
43
44
45 // loop through all resulting nodes
46 foreach ($results as $row) {
47
48 if (isset($info['order']['fields'])) {
49 $info['input'][$row->nid]['order'][] = $input[$info['order']['fields'][0]['field_name'] .'_'. $row->nid];
50 }
51
52 if (isset($info['hierarchy'])) {
53 $info['input'][$row->nid]['parent'] = $input[$info['hierarchy']['field']['field_name'] .'_'. $row->nid];
54 }
55 }
56
57 // build hierarchy
58 _draggableviews_build_hierarchy($info);
59
60 // save structure
61 _draggableviews_save_hierarchy($info);
62
63 if (isset($info['hierarchy'])) {
64
65 // save expanded/collapsed state
66 global $user;
67
68 foreach ($vars['submit']['#post'] AS $key => $val) {
69 if (ereg('draggableviews_collapsed_', $key)) {
70 $parent_nid = substr($key, 25);
71 db_query("DELETE FROM {draggableviews_collapsed}
72 WHERE uid=%d AND parent_nid=%d",
73 $user->uid, $parent_nid);
74
75 db_query("INSERT INTO {draggableviews_collapsed}
76 (uid, parent_nid, collapsed) VALUES (%d, %d, %d)",
77 $user->uid, $parent_nid, $val);
78 }
79 }
80 }
81 }
82
83 /**
84 * Collect all known information in a handy array
85 *
86 * @param $view
87 * The views object
88 *
89 * @return
90 * An structured array containt the extracted draggableviews settings
91 * and additional field information.
92 * array(
93 * 'order' => array(
94 * 'fields' => array(
95 * 0 => array(
96 * 'field_name' => field_name
97 * 'real_field_name' => real_field_name,
98 * 'field_alias' => field_alias
99 * 'field_type' => field_type,
100 * 'minimum_value' => minimum
101 * ),
102 * ..
103 * ),
104 * 'visible' => True/False
105 * ),
106 * 'hierarchy' => array(
107 * 'field' => array(
108 * 'field_name' => field_name
109 * 'real_field_name' => real_field_name,
110 * 'field_alias' => field_alias
111 * 'field_type' => field_type,
112 * ),
113 * 'visible' => True/False
114 * ),
115 *
116 * 'nodes' => array(
117 * nid1 => array(
118 * field_name1 => value
119 * field_name2 => value
120 * ..
121 * ),
122 * ..
123 * ),
124 * );
125 */
126 function _draggableviews_info(&$view, $prepare_results = TRUE) {
127 $options = &$view->display['default']->display_options['style_options'];
128 $fields = &$view->field;
129 $results = &$view->result;
130
131 // build information array
132 $info = array();
133 if (isset($options['tabledrag_order'])) {
134 foreach ($options['tabledrag_order'] as $field) {
135 $info['order']['fields'][] = array(
136 'handler' => _draggableviews_init_handler($field, $view),
137 'field_name' => $field['field'],
138 'field_alias' => $fields[$field['field']]->field_alias,
139 );
140 }
141 $info['order']['visible'] = strcmp($options['tabledrag_order_visible']['visible'], 'visible') == 0 ? TRUE : FALSE;
142 $info['order']['minimum_value'] = $info['order']['fields'][0]['handler']->get_minimum_value();
143
144 if (count($info['order']['fields']) >= 2 && $options['tabledrag_hierarchy']['field'] != 'none') {
145 $info['hierarchy'] = array(
146 'field' => array(
147 'handler' => _draggableviews_init_handler($options['tabledrag_hierarchy'], $view),
148 'field_name' => $options['tabledrag_hierarchy']['field'],
149 'field_alias' => $fields[$options['tabledrag_hierarchy']['field']]->field_alias,
150 ),
151 'visible' => strcmp($options['tabledrag_hierarchy_visible']['visible'], 'visible') == 0 ? TRUE : FALSE,
152 );
153 }
154 }
155 if (isset($options['tabledrag_types'])) {
156 foreach ($options['tabledrag_types'] as $type) {
157 $info['types'][$type['node_type']] = $type['type'];
158 }
159 }
160 if ($info['hierarchy']) {
161 $info['expand_links'] = array(
162 'show' => strcmp($options['tabledrag_expand']['expand_links'], 'expand_links') == 0 ? TRUE : FALSE,
163 'default_collapsed' => strcmp($options['tabledrag_expand']['collapsed'], 'collapsed') == 0 ? TRUE : FALSE,
164 );
165 }
166
167 $info['nodes'] = array();
168
169 if ($options['tabledrag_order'] && $prepare_results) {
170 // loop through all resulting nodes
171 foreach ($results as $row) {
172 foreach ($info['order']['fields'] as $field) {
173 $info['nodes'][$row->nid]['order'][] = $row->{$field['field_alias']};
174 }
175 if (isset($info['hierarchy'])) {
176 $info['nodes'][$row->nid]['parent'] = $row->{$info['hierarchy']['field']['field_alias']};
177 }
178 }
179 }
180
181 $gaga=$info;
182 $gaga['order']['fields'][0]['handler']=array();
183 $gaga['order']['fields'][1]['handler']=array();
184 $gaga['hierarchy']['field']['handler']=array();
185 //drupal_set_message("<pre>".print_r($gaga, true)."</pre>");
186 return $info;
187 }
188
189 /**
190 * Get instance from handler
191 *
192 * @param field
193 * The field options specified in the style plugin
194 * @param view
195 * The view object
196 *
197 * @return
198 * An instance of the handler
199 */
200 function _draggableviews_init_handler($field, &$view) {
201 //drupal_set_message("<pre>".print_r(draggableviews_discover_handlers(), true)."</pre>");
202 $handler_info = draggableviews_discover_handlers($field['handler']);
203 require_once($handler_info['path'] .'/'. $handler_info['file']);
204 $handler = new $handler_info['handler'];
205 $handler->init($field['field'], $view);
206
207 return $handler;
208 }
209
210 /**
211 * Build hierarchy
212 *
213 * This function also handles broken structures
214 *
215 * @param info
216 * The structured information array
217 */
218 function _draggableviews_build_hierarchy(&$info) {
219
220 $nodes = &$info['nodes'];
221 $input = &$info['input'];
222
223 foreach ($nodes as $nid => $prop) {
224
225 // get depth
226 if (($depth = _draggableviews_get_hierarchy_depth($nid, $input, $info)) === FALSE) {
227 // Error! The hierarchy structure is broken and could
228 // look like the following: (we're currently processing X)
229 // A
230 // --X
231 // --D
232 //
233 // The next steps:
234 // 1) bring it down to the root level
235 // 2) Set order fields to the minimum
236
237 $nodes[$nid]['parent'] = 0;
238
239 // We gracefully sidestep the order-loop
240 $depth = -1;
241
242 drupal_set_message("Draggableviews: An error was detected. The structure has been repaired.");
243 }
244
245 // Let's take a look at the following expample, to understand
246 // what is beeing done.
247 //
248 // A
249 // --B
250 // --C
251 // --X
252 // --D
253 // E
254 // Imagine we're currently processing X:
255 //
256 // We know that X is in depth=3, so we save the received
257 // weight value in the 3rd order field of node X.
258 //
259 // The 2nd order field must inherit the received weight of
260 // node C (the next parent). And the 1st order field must
261 // inherit the received weight of node A (the parent of C).
262 //
263 // When we finally order the view by weight1, weight2, weight3 then
264 // weight1 and weight2 from node X will always equal with
265 // those from node A and B, and weight3 defines the order of the 3rd level.
266
267 $temp_nid = $nid;
268
269 for ($i = $depth; $i >= 0; $i--) {
270 // we're operating top-down, so we determine the parents nid by the way
271
272 $nodes[$nid]['order'][$i] = $input[$temp_nid]['order'][0];
273
274 if (isset($info['hierarchy']) && $i > 0) {
275 if (!($temp_nid = $input[$temp_nid]['parent'])) {
276 // this line should never be reached assumed the depth
277 // was calculated correctly.
278 drupal_set_message(t('Undefined State called in draggableviews_build_hierarchy(..)'), 'error');
279 break;
280 }
281 }
282 }
283
284 if (isset($info['hierarchy']) && $depth > -1) {
285 // Simply set the parent value
286
287 $nodes[$nid]['parent'] = $input[$nid]['parent'];
288 }
289
290 // Now set all unused weights to a minimum value. Otherwise
291 // it could happen that a child appears above its parent.
292 // The ? can be anything, unfortunately also > 5
293 //
294 // --A (3,5)
295 // B (3,?)
296 //
297 // To guaranteer that the ? is always the lowest, we choose
298 // the minimum.
299 // Due to this it's recommended that all order fields have
300 // the same minimum value!
301
302 $depth = ($depth == -1) ? 0 : $depth;
303
304 for ($i = $depth + 1; $i < count($info['order']['fields']); $i++) {
305 $info['nodes'][$nid]['order'][$i] = $info['order']['fields'][$i]['handler']->get_minimum_value();
306 }
307 }
308 }
309
310 /**
311 * Rebuild hierarchy
312 *
313 * This function is called when the structure is broken
314 *
315 * @param info
316 * The structures information array
317 */
318 function _draggableviews_rebuild_hierarchy(&$info) {
319
320 drupal_set_message("Draggableviews: Rebuilding structure..");
321
322 $nodes = &$info['nodes'];
323 $info['input'] = array();
324 $input = &$info['input'];
325
326 // Build an input-array to simulate the form data we would receive on submit
327
328 // loop through all nodes
329 foreach ($nodes as $nid => $prop) {
330
331 $depth = $prop['depth'] ? $prop['depth'] : 0;
332
333 // use order weight of the hierarchy level the field is situated in
334 $input[$nid][0] = $prop['order'][$depth];
335 $input[$nid]['order'][0] = $prop['order'][$depth];
336
337 if (isset($info['hierarchy'])) {
338 // set parent
339 $input[$nid]['parent'] = $prop['parent'];
340 }
341 }
342
343 // build hierarchy
344 _draggableviews_build_hierarchy($info);
345
346 // save hierarchy
347 _draggableviews_save_hierarchy($info);
348
349 // redirect here
350 // @todo
351 drupal_set_message(t('Draggableviews: Refresh required (..will redirect automatically after testing)'), 'error');
352 }
353
354 /**
355 * Detect and repair order collisions
356 *
357 * @return
358 * TRUE if no collision detected
359 */
360 function _draggableviews_detect_order_collisions(&$info) {
361
362 $nodes = &$info['nodes'];
363
364 $collision = FALSE;
365
366 // Detect order collisions
367 // Check for the following:
368 // 1) The minimum value should not be used as it
369 // should be possible for new nodes to default on top
370 // without order collisions
371 // 2) @todo The last value should not be used...
372 // 3) An order value should only be used once per depth/parent
373
374 // array(
375 // parent => array(order1, ..),
376 // ..
377 // )
378 $order = array();
379
380 $min_value = $info['order']['minimum_value'];
381
382 // loop through all nodes
383 foreach ($nodes as $nid => $prop) {
384
385 if (isset($info['hierarchy'])) {
386 $parent = $prop['parent'];
387 // if no parent set use 0
388 if (!isset($parent)) $parent = 0;
389 }
390 else {
391 $parent = 0;
392 }
393
394 $order_field_value = &$nodes[$nid]['order'][$prop['depth']];
395
396 $begin_search = TRUE;
397
398 // make sure that the minimum value cannot be used
399 if (!is_array($order[$parent])) $order[$parent] = array($min_value);
400
401 // first try to keep current value
402 $tmp_order = $order_field_value;
403
404 while (is_numeric(array_search($tmp_order, $order[$parent]))) {
405 // if there already exists an order value for this depth/parent
406 // we have to find another one.
407 // Try to find a free order:
408
409 if ($begin_search == TRUE) {
410
411 $tmp_order = $min_value + 1;
412
413 $begin_search = FALSE;
414 }
415 else {
416
417 $tmp_order++;
418 }
419
420 $collision = TRUE;
421 }
422
423 $order[$parent] = array_merge(array($tmp_order), $order[$parent]);
424
425 $order_field_value = $tmp_order;
426 }
427
428 return !$collision;
429 }
430
431 /**
432 * Set values and save nodes
433 *
434 * @param $info
435 * The structured information array
436 */
437 function _draggableviews_save_hierarchy($info) {
438
439 // loop through all nodes
440 foreach ($info['nodes'] as $nid => $prop) {
441
442 if (isset($info['hierarchy'])) {
443 $value = $prop['parent'];
444 $handler = $info['hierarchy']['field']['handler'];
445 $handler->save($nid, $value);
446 }
447
448 for ($i = 0; $i < count($info['order']['fields']); $i++) {
449 // loop through all order fields
450
451 $value = $prop['order'][$i];
452 $handler = $info['order']['fields'][$i]['handler'];
453 $handler->save($nid, $value);
454 }
455 }
456 }
457
458 /**
459 * Check order settings
460 *
461 */
462 function _draggableviews_check_order($nid, &$info) {
463
464 $nodes = &$info['nodes'];
465
466 $temp_nid = $nid;
467
468 for ($i = $nodes[$nid]['depth']; $i >= 0; $i--) {
469 // we're operating top-down, so we determine the parents nid by the way
470
471 if ($nodes[$nid]['order'][$i] != $nodes[$temp_nid]['order'][$i]) {
472 return FALSE;
473 }
474
475 if (isset($info['hierarchy']) && $i > 0) {
476 if (!($temp_nid = $nodes[$temp_nid]['parent'])) {
477 // this line should never be reached assumed the depth
478 // was calculated correctly.
479 drupal_set_message(t('Undefined State called in draggableviews_build_hierarchy(..)'), 'error');
480 return FALSE;
481 }
482 }
483 }
484
485 return TRUE;
486 }
487
488 /**
489 * Calculate depth of all nodes
490 *
491 * @param $info
492 * The structured information array
493 */
494 function _draggableviews_calculate_depths(&$info) {
495
496 $error = FALSE;
497
498 // loop through all rows the view returns
499 foreach ($info['nodes'] as $nid => $prop) {
500
501 // determine hierarchy depth of current row
502 $info['nodes'][$nid]['depth'] = _draggableviews_get_hierarchy_depth($nid, $info['nodes'], $info);
503
504 if ($info['nodes'][$nid]['depth'] === FALSE) $error = TRUE;
505 }
506
507 return !$error;
508 }
509
510 /**
511 * Get Hierarchy depth
512 *
513 * This function detects cycles,
514 * broken hierarchy structures
515 * and wrong weight settings
516 *
517 * @param $node
518 * The node from wich we want to know the depth
519 * @param $info
520 * The structured information array
521 * return
522 * The hierarchy depth,
523 * on error FALSE.
524 */
525 function _draggableviews_get_hierarchy_depth($nid, &$nodes, &$info) {
526
527 if (!isset($info['hierarchy'])) return 0;
528
529 $depth = 0;
530 $error = FALSE;
531 $temp_nid = $nid;
532 $used_nids = array();
533 $used_nids[] = $temp_nid;
534
535 while (!($error = !isset($nodes[$temp_nid])) && ($temp_nid = $nodes[$temp_nid]['parent']) > 0) {
536
537 // In order to detect cycles we use an array,
538 // where all used node ids are saved in. If any node id
539 // occurs twice -> return FALSE
540 $cycle_found = array_search($temp_nid, $used_nids);
541
542 if (isset($cycle_found) && $cycle_found !== FALSE) {
543 drupal_set_message(t("Draggableviews: A cycle was found."));
544 return FALSE;
545 }
546
547 $used_nids[] = $temp_nid;
548 $depth++;
549 }
550
551 if ($error) {
552 // If loop breaked caused by an error
553 return FALSE;
554 }
555
556 return $depth;
557 }
558
559 /*
560 * filter handlers by type
561 *
562 * @param $type
563 * the field type
564 * @param $fields
565 * the fields array
566 * return
567 * the available fields
568 */
569 function _draggableviews_filter_fields($types = array(''), $handlers) {
570
571 $available_fields = array();
572
573 foreach ($handlers as $field => $handler) {
574 $available = TRUE;
575 // search given type in content-field-type
576 if (true) {
577 /*if (empty($types)) {
578 $available = TRUE;
579 }
580 else {
581 foreach ($types AS $type) {
582 if (ereg($type, $handler->content_field['type'])) {
583 $available = TRUE;
584 break;
585 }
586 }
587 }*/
588
589 if ($available) {
590 if ($label = $handler->label()) {
591 $available_fields[$field] = $label;
592 }
593 else {
594 $available_fields[$field] = $handler->ui_name();
595 }
596 }
597 }
598 }
599 return $available_fields;
600 }

  ViewVC Help
Powered by ViewVC 1.1.2