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

Contents of /contributions/modules/tasks_advanced/tasks_advanced.module

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


Revision 1.20 - (show annotations) (download) (as text)
Thu Apr 3 18:26:56 2008 UTC (19 months, 3 weeks ago) by moonray
Branch: MAIN
CVS Tags: DRUPAL-5--1-1, HEAD
Changes since 1.19: +2 -2 lines
File MIME type: text/x-php
#175378: Changed weight of items in form to accomodate integration with event module. Added documentation about enabling dates to tasks through event module.
1 <?php
2 // $Id: tasks_advanced.module,v 1.19 2008/04/03 18:10:17 moonray Exp $
3
4 /**
5 * @file tasks_advanced.module
6 *
7 * The tasks_advanced module allows extends the tasks module. It provides more
8 * detailed overviews, and additional categorization and filters.
9 *
10 * Some of the code is loosely based on:
11 * - project.module
12 * - tasks.module
13 */
14
15
16 /**
17 * @defgroup tasks_advanced_core Core drupal hooks
18 */
19
20
21 /**
22 * Implementation of hook_perm().
23 *
24 * @ingroup tasks_advanced_core
25 */
26 function tasks_advanced_perm() {
27 return array('access own tasks', 'access all tasks');
28 }
29
30
31 /**
32 * Implementation of hook_menu().
33 *
34 * @ingroup tasks_advanced_core
35 */
36 function tasks_advanced_menu($may_cache) {
37 global $user;
38
39 $items = array();
40
41 if ($may_cache) {
42 // callbacks
43 $items[] = array(
44 'path' => 'tasks',
45 'title' => t('Tasks'),
46 'callback' => '_tasks_advanced_main',
47 'access' => user_access('access tasks main page') && user_access('access all tasks'),
48 'type' => MENU_CALLBACK
49 );
50 $items[] = array(
51 'path' => 'tasks/user',
52 'title' => t('My tasks'),
53 'callback' => '_tasks_advanced_main',
54 'callback arguments' => array('user'),
55 'access' => user_access('access tasks main page') && user_access('access all tasks'),
56 'type' => MENU_CALLBACK
57 );
58
59 // dynamic menu items
60 $items[] = array(
61 'path' => 'tasks',
62 'title' => t('Tasks'),
63 'callback' => '_tasks_advanced_main',
64 'access' => user_access('access tasks main page') && user_access('access all tasks'),
65 'type' => MENU_DYNAMIC_ITEM
66 );
67 if ($user->uid) {
68 $items[] = array(
69 'path' => 'tasks/user/'.$user->uid,
70 'title' => t('My tasks'),
71 'callback' => '_tasks_advanced_main',
72 'callback arguments' => array('user', $user->uid),
73 'access' => user_access('access tasks main page') && (user_access('access all tasks') || user_access('access own tasks')),
74 'type' => MENU_DYNAMIC_ITEM
75 );
76 }
77 }
78
79 return $items;
80 }
81
82
83 /**
84 * Implementation of hook_form_alter().
85 *
86 * @ingroup tasks_advanced_core
87 */
88 function tasks_advanced_form_alter($form_id, &$form) {
89 global $user;
90 drupal_add_css(drupal_get_path('module', 'tasks_advanced').'/tasks_advanced.css');
91
92 $node = isset($form['#node']) ? $form['#node'] : NULL;
93
94 $tasks_advanced_visible = (variable_get('event_nodeapi_'. $type, TRUE) ? FALSE : TRUE);
95 $taskcategories = _tasks_advanced_taskcategory();
96 $tasktypes = _tasks_advanced_tasktype();
97
98 if ($form_id == 'tasks_node_form') {
99 $form['tasks_advanced'] = array(
100 '#type' => 'fieldset',
101 '#title' => t('Details'),
102 '#collapsible' => FALSE,
103 '#collapsed' => $tasks_advanced_visible,
104 '#attributes' => array("id" => "tasks_advanced"),
105 '#weight' => -13,
106 );
107 $form['tasks_advanced']['taskcategory'] = array(
108 '#type' => 'select',
109 '#title' => t('Task Category'),
110 '#default_value' => (isset($node->taskcategory) ? $node->taskcategory : 'unknown'),
111 '#options' => $taskcategories,
112 '#description' => t(''),
113 '#weight' => 0,
114 );
115 $form['tasks_advanced']['tasktype'] = array(
116 '#type' => 'radios',
117 '#title' => t('Task Type'),
118 '#default_value' => (isset($node->tasktype) ? $node->tasktype : 'action'),
119 '#options' => $tasktypes,
120 '#description' => t(''),
121 '#weight' => 0,
122 );
123 $form['from'] = array(
124 '#type' => 'hidden',
125 '#value' => arg(3).(arg(4) ? '/'.arg(4).(arg(5) ? '/'.arg(5) : '') : ''),
126 );
127
128 // alter parent tasklist to only contain project types, and add a class for styling
129 $parents = _tasks_advanced_projects($node->nid);
130
131 $form['task_parent']['#prefix'] = '<div id="parent-tasklist">';
132 $form['task_parent']['#suffix'] = '</div>';
133 $form['task_parent']['#options'] = $parents;
134 }
135 }
136
137
138 function _tasks_advanced_projects($nid) {
139 if (!$nid) {
140 $nid = 0;
141 }
142 $result = db_query("SELECT n.nid, n.title, t.task_parent FROM {node} n INNER JOIN {tasks} t ON t.nid = n.nid INNER JOIN {tasks_advanced} a ON a.nid = n.nid WHERE n.type='tasks' AND t.completed = '0000-00-00' AND a.tasktype = 'project' AND n.nid != {$nid} ORDER BY IF(t.task_parent = 0,0,1), n.title ASC");
143
144 while ($tasklist = db_fetch_object($result)) {
145 if ($tasklist->task_parent == 0) {
146 $tasklist->title = t('-- MASTER TASKLIST --');
147 }
148 $tasklists[$tasklist->nid] = $tasklist->title;
149 $not_master = 1;
150 }
151 if (!$not_master) {
152 $tasklists[0] = t('NO PARENT - THIS IS THE MASTER TASKLIST');
153 }
154 return $tasklists;
155 }
156
157
158 function _tasks_advanced_taskcategory($taskcategory = NULL) {
159 $taskcategories = array('unknown' => t('Unknown'), 'next' => t('Next Action'), 'waiting' => t('Waiting For'), 'someday' => t('Some Day'), 'list' => t('List'), 'reference' => t('Reference'), 'inactive' => t('Inactive'));
160 return ($taskcategory != NULL ? $taskcategories[$taskcategory] : $taskcategories);
161 }
162
163
164 function _tasks_advanced_tasktype($tasktype = NULL) {
165 $tasktypes = array('action' => t('Action'), 'project' => t('Project'));
166 return ($tasktype != NULL ? $tasktypes[$tasktype] : $tasktypes);
167 }
168
169
170 /**
171 * Implementation of hook_nodeapi().
172 *
173 * @ingroup tasks_advanced_core
174 */
175 function tasks_advanced_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
176 // only apply to task nodes
177 if ($node->type == 'tasks') {
178 switch ($op) {
179 case 'validate':
180 case 'submit':
181 // lets make sure we get good values
182 if ($node->task_parent == 0) {
183 // this is a root node
184 $node->taskcategory = 'unknown';
185 $node->tasktype = 'project';
186 }
187
188 $taskcategories = _tasks_advanced_taskcategory();
189 $tasktypes = _tasks_advanced_tasktype();
190
191 if (!array_key_exists($node->taskcategory, $taskcategories)) {
192 $node->taskcategory = 'unknown';
193 }
194 if (!array_key_exists($node->tasktype, $tasktypes)) {
195 $node->tasktype = 'action';
196 }
197 break;
198
199 case 'insert':
200 if ($node->task_parent == 0) {
201 // this is a root node
202 $task_tree_left = 0;
203 $task_tree_right = 1;
204 $task_tree_depth = 0;
205 }
206 else {
207 // fetch parent for tree info
208 $parent_node = _tasks_advanced_get_node($node->task_parent);
209
210 // update tree to make place for new node
211 _tasks_advanced_tree_insert($parent_node);
212 $task_tree_left = $parent_node->task_tree_right;
213 $task_tree_right = $parent_node->task_tree_right + 1;
214 $task_tree_depth = $parent_node->task_tree_depth + 1;
215 }
216
217 // insert new record
218 db_query("INSERT INTO {tasks_advanced} (nid, taskcategory, tasktype, task_tree_left, task_tree_right, task_tree_depth) VALUES (%d, '%s', '%s', %d, %d, %d)", $node->nid, $node->taskcategory, $node->tasktype, $task_tree_left, $task_tree_right, $task_tree_depth);
219 break;
220
221 case 'update':
222 // update the tree if the parent has changed
223 if (($_GET['action'] == 'up' || $_GET['action'] == 'down') == FALSE) {
224 if (_tasks_advanced_tree_move($node, $node->task_parent)) {
225 drupal_set_message(t('Task <b>'.$node->title.'</b> has been moved to another location on the tasklist.'));
226 }
227 }
228
229 // update record
230 db_query("UPDATE {tasks_advanced} SET taskcategory = '%s', tasktype = '%s' WHERE nid = %d", $node->taskcategory, $node->tasktype, $node->nid);
231
232 if ($node->from) {
233 $_REQUEST['destination'] = $node->from;
234 }
235 break;
236
237 case 'delete':
238 // update tree to fill place of missing node
239 _tasks_advanced_tree_extract($node);
240
241 // delete record
242 db_query("DELETE FROM {tasks_advanced} WHERE nid = %d", $node->nid);
243 break;
244
245 case 'load':
246 $record = db_query('SELECT taskcategory, tasktype, task_tree_left, task_tree_right, task_tree_depth FROM {tasks_advanced} WHERE nid = %d', $node->nid);
247 $object = db_fetch_object($record);
248
249 return array(
250 'taskcategory' => $object->taskcategory,
251 'tasktype' => $object->tasktype,
252 'task_tree_left' => $object->task_tree_left,
253 'task_tree_right' => $object->task_tree_right,
254 'task_tree_depth' => $object->task_tree_depth,
255 );
256 break;
257
258 case 'view':
259 case 'rss item':
260 break;
261 }
262 }
263 }
264
265
266 /**
267 * Implementation of hook_user().
268 *
269 * @ingroup tasks_advanced_core
270 */
271 function tasks_advanced_user($op, &$edit, &$user, $category = NULL) {
272 if($op == "view" && user_access('access tasks main page') && (user_access('access all tasks') || user_access('access own tasks'))) {
273 $items[] = array(
274 'title' => t("Tasks"),
275 'value' => l(t('View !s\'s tasks', array('!s' => $user->name)), "tasks/user/".$user->uid),
276 'class' => 'tasks',
277 );
278 return array(t('Tasks') => $items);
279 }
280 }
281
282 /**
283 * @defgroup tasks_advanced_callback Functions which are the menu callbacks for this module
284 */
285
286
287 /**
288 * Output user headers.
289 *
290 * @ingroup tasks_advanced_callback
291 * @param $type string 'user' for individual user page
292 * @param $uid integer User's id
293 * @return string html
294 */
295 function _tasks_advanced_main($type = NULL, $uid = NULL) {
296 global $user;
297
298 drupal_add_js(drupal_get_path('module', 'tasks').'/tasks_view.js');
299 drupal_add_css(drupal_get_path('module', 'tasks_advanced').'/tasks_advanced.css');
300
301 // Set proper breadcrumb
302 $bc = drupal_get_breadcrumb();
303 if ($type == NULL) {
304 $bc[] = l(t('Tasks'), 'tasks');
305 }
306 elseif ($type == 'user' && $uid == $user->uid) {
307 // Need to remove 'my tasks' part off breadcrumb
308 array_pop($bc);
309 }
310
311 if ($type == 'user') {
312 if ($uid == NULL) {
313 // Need to redirect to current user
314 drupal_goto('tasks/user/'.$user->uid);
315 }
316 else {
317 if ($uid === 0 || $uid == '0') {
318 // Add title and special headers
319 drupal_set_title(t('Unassigned Tasks'));
320
321 // Add breadcrumb trail
322 $bc[] = l(t('Unassigned'), 'tasks/user/'.$uid);
323 }
324 else {
325 $new_user = user_load(array('uid' => $uid));
326 if (!$new_user->uid) {
327 return drupal_not_found();
328 }
329
330 // Add title and special headers
331 drupal_set_title(t('%name&#8217;s Tasks', array('%name' => ucfirst($new_user->name))));
332 $output = '<p>User ID: '.$uid.'</p>';
333
334 // Add breadcrumb trail
335 $bc[] = l(t('!name', array('!name' => ucfirst($new_user->name))), 'tasks/user/'.$uid);
336 }
337
338 }
339 }
340 else {
341 drupal_set_title(t('Tasks'));
342
343 if (!user_access('access all tasks')) {
344 drupal_goto('tasks/user/'.$user->uid);
345 }
346 if ($uid != NULL) {
347 drupal_goto('tasks');
348 }
349 }
350
351 // Finalize breadcrumb
352 drupal_set_breadcrumb($bc);
353
354 // Look to see if we need to change node order in this list
355 if (($action = $_GET['action']) && ($tid = $_GET['task'])) {
356 $task = node_load(array('nid' => $tid, 'type' => 'tasks'));
357 if ($action == 'up') {
358 $task->order_by -= 1.3;
359 node_save($task);
360 if (_tasks_advanced_tree_sort($task, -1)) {
361 drupal_set_message(t('Task <b>'.$task->title.'</b> has been moved <b>up</b> the tasklist.'));
362 }
363 }
364 elseif ($action == 'down') {
365 $task->order_by += 1.3;
366 node_save($task);
367 if (_tasks_advanced_tree_sort($task, 1)) {
368 drupal_set_message(t('Task <b>'.$task->title.'</b> has been moved <b>down</b> the tasklist.'));
369 }
370 }
371
372 if ($type == 'user') {
373 drupal_goto('tasks/user/'.$uid);
374 }
375 else {
376 drupal_goto('tasks');
377 }
378 }
379
380 // Setup filtering
381 $query = new StdClass();
382 if (isset($_SESSION['_tasks_advanced_query'])) {
383 $query = $_SESSION['_tasks_advanced_query'];
384 }
385 else {
386 // Set filtering defaults
387 $query->status = array(1);
388 }
389
390 // Add user to filter
391 if ($uid == NULL) {
392 unset($query->uid);
393 }
394 else {
395 $query->uid = array($uid);
396 }
397
398 // Populate filter
399 $query = _tasks_advanced_query_parse($query);
400 $_SESSION['_tasks_advanced_query'] = $query;
401
402 // Make database calls
403 $sql = _tasks_advanced_query_sql($query);
404 $result = db_query($sql);
405 $num_rows = db_num_rows($result);
406
407 // Enable the list of tasks to be filtered
408 $output .= drupal_get_form('tasks_advanced_view_filter_form', $num_rows, $query);
409
410 // only proceed if we have tasks to display
411 if ($num_rows > 0) {
412 $rows = array();
413
414 $init = TRUE;
415 $userid = -1;
416 $username = -1;
417
418 // loop through each task
419 while ($task = db_fetch_object($result)) {
420 // is this a new user?
421 if ($username != $task->assigned_name) {
422 // if we're showing all user's tasks, we need to add headers for each user
423 if ($type != 'user') {
424 if ($init == FALSE) {
425 $output .= _tasks_advanced_user_headers($userid, $username);
426 $output .= _tasks_advanced_row_headers($rows);
427 $rows = array();
428 }
429 else {
430 // initialization complete
431 $init = FALSE;
432 }
433 }
434 $userid = $task->assigned_to;
435 $username = $task->assigned_name;
436 unset($previous_task);
437 }
438
439 // Is another user the owner of our parent?
440 // If so, we need to insert an 'unknown' parent
441 // to keep hierarchy somewhat consistent.
442 // It's still a bit of a hack, since it doesn't
443 // show the real parent, and doesn't reflect
444 // the depth either.
445 if (isset($previous_task)) {
446 if (($task->task_tree_depth > $previous_task->task_tree_depth && $task->task_parent != $previous_task->nid) ||
447 ($task->task_tree_depth == $previous_task->task_tree_depth && $task->task_parent != $previous_task->task_parent)) {
448 if ($task->task_tree_left > $previous_task->task_tree_left && $task->task_tree_right < $previous_task->task_tree_right && $task->task_tree_depth > 2) {
449 // this is a grandchild of this task
450 $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid, TRUE);
451 }
452 else {
453 $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid);
454 }
455 }
456 }
457 else {
458 if ($task->task_tree_depth > 1) {
459 $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid);
460 }
461 }
462 $previous_task = drupal_clone($task);
463
464 $task_rows = _tasks_advanced_task($task, $type, $uid);
465 $rows[] = $task_rows[0];
466 $rows[] = $task_rows[1];
467 }
468
469 if ($type != 'user') {
470 $output .= _tasks_advanced_user_headers($userid, $username);
471 }
472 $output .= _tasks_advanced_row_headers($rows);
473 $rows = array();
474 }
475 else {
476 // no tasks to display
477 $output .= '<p>'.t('No tasks found.').'</p>';
478 }
479
480 print theme('page', $output);
481 }
482
483 /**
484 * Filtering for tasklist
485 */
486 function tasks_advanced_view_filter_form($num_rows, &$query) {
487 // Create dropdown options lists
488 $status = array(1 => t('Incomplete Tasks'), 2 => t('Complete Tasks'), 0 => t('All Tasks'));
489 $taskcategories = array_merge(array(0 => 'All Tasks'), _tasks_advanced_taskcategory());
490 $tasktypes = array_merge(array(0 => 'All Tasks'), _tasks_advanced_tasktype());
491
492 // Enable the list of tasks to be filtered
493 $form['filter'] = array(
494 '#type' => 'fieldset',
495 '#title' => t('Filter'),
496 '#collapsible' => TRUE,
497 '#collapsed' => FALSE,
498 '#weight' => -1,
499 );
500 $form['filter']['label'] = array(
501 '#value' => t('<strong>Showing %num_rows tasks.</strong>', array('%num_rows' => $num_rows)),
502 );
503 $form['filter']['status'] = array(
504 '#type' => 'select',
505 '#title' => t('Status'),
506 '#default_value' => (isset($query->status) ? $query->status[0] : '0'),
507 '#options' => $status,
508 );
509 $form['filter']['taskcategory'] = array(
510 '#type' => 'select',
511 '#title' => t('Category'),
512 '#default_value' => (isset($query->taskcategory) ? $query->taskcategory[0] : '0'),
513 '#options' => $taskcategories,
514 );
515 $form['filter']['tasktype'] = array(
516 '#type' => 'select',
517 '#title' => t('Task Type'),
518 '#default_value' => (isset($query->tasktype) ? $query->tasktype[0] : '0'),
519 '#options' => $tasktypes,
520 );
521 $form['filter']['submit'] = array(
522 '#type' => 'submit',
523 '#value' => t('Filter')
524 );
525 $form['#redirect'] = FALSE;
526
527 return $form;
528 }
529
530
531 function _tasks_advanced_query_parse($query = NULL) {
532 $fields = array('status', 'taskcategory', 'tasktype');
533
534 if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST['form_id'] == 'tasks_advanced_view_filter_form') {
535 foreach ($_POST as $key => $value) {
536 if (!empty($value) && in_array($key, $fields)) {
537 $query->$key = !is_array($value) ? explode(',', $value) : $value;
538 $_POST[$key] = is_array($value) ? implode(',', $value) : $value;
539 }
540 else {
541 unset($query->$key);
542 }
543 }
544 $_POST = array();
545 }
546 else {
547 foreach ($_GET as $key => $value) {
548 if (!empty($value) && in_array($key, $fields)) {
549 $query->$key = explode(',', $value);
550 }
551 else {
552 unset($query->$key);
553 }
554 }
555 }
556
557 return $query;
558 }
559
560 function _tasks_advanced_query_sql_field($field, $values, $operator = ' OR ') {
561 $sql = array();
562 if (!is_array($values)) {
563 $values = array($values);
564 }
565 foreach ($values as $value) {
566 $value = db_escape_string($value);
567 $sql[] = ($like && $field != 'p.pid') ? "$field LIKE '%$value%'" : "$field = '$value'";
568 }
569 if ($sql) {
570 return '('. implode($operator, $sql) .')';
571 }
572 }
573
574 function _tasks_advanced_query_sql($query) {
575 $order = 'u.name, a.task_tree_left ASC, IF(t.completed = 0, 0, 1), t.completed DESC';
576 $sql = array();
577
578 foreach ($query as $key => $value) {
579 switch ($key) {
580 case 'status':
581 switch ($value[0]) {
582 case 1:
583 $order = 'u.name, a.task_tree_left ASC';
584 $sql[] = 't.completed = 0000-00-00';
585 break;
586 case 2:
587 $order = 'u.name, a.task_tree_left ASC, t.completed DESC';
588 $sql[] = 't.completed != 0000-00-00';
589 break;
590 }
591 break;
592 case 'taskcategory':
593 $sql[] = _tasks_advanced_query_sql_field('a.taskcategory', $value);
594 break;
595 case 'tasktype':
596 $sql[] = _tasks_advanced_query_sql_field('a.tasktype', $value);
597 break;
598 case 'uid':
599 $sql[] = _tasks_advanced_query_sql_field('u.uid', $value);
600 break;
601 }
602 }
603
604 $sql_str = "SELECT n.nid, t.task_parent, u.name AS assigned_name, t.assigned_to, a.taskcategory, a.tasktype, a.task_tree_left, a.task_tree_right, a.task_tree_depth, u.data
605 FROM {node} n
606 INNER JOIN {tasks} t ON t.nid = n.nid
607 INNER JOIN {tasks_advanced} a ON a.nid = n.nid
608 LEFT JOIN {users} u ON u.uid = t.assigned_to
609 WHERE n.status = 1
610 AND t.task_parent > 0
611 AND n.type='tasks' ".
612 (count($sql) > 0 ?" AND (". implode(") AND (", $sql) .")" : '').' '."
613 ORDER BY {$order}";
614
615 $sql = db_rewrite_sql($sql_str);
616
617 return $sql;
618 }
619
620
621 /**
622 * Rebuild tree structure.
623 *
624 * Based on "Modified Preorder Tree Traversal" article at
625 * http://www.sitepoint.com/article/hierarchical-data-database
626 *
627 * @param $parent object task
628 * @param $left integer left side of tree
629 * @return $right integer
630 */
631 function tasks_advanced_rebuild_tree($parent, $left, $depth = 0) {
632 // the right value of this node is the left value + 1
633 $right = $left + 1;
634
635 // get all children of this node
636 $result = db_query("SELECT nid task_parent FROM {tasks} WHERE task_parent = %d", $parent);
637
638 while ($row = db_fetch_object($result)) {
639 // recursive execution of this function for each
640 // child of this node
641 // $right is the current right value, which is
642 // incremented by the rebuild_tree function
643 $right = tasks_advanced_rebuild_tree($row->task_parent, $right, $depth + 1);
644 }
645
646 // we've got the left value, and now that we've processed
647 // the children of this node we also know the right value
648 db_query("UPDATE {tasks_advanced} SET task_tree_left = %d, task_tree_right = %d, task_tree_depth = %d WHERE nid = %d", $left, $right, $depth, $parent);
649
650 // return the right value of this node + 1
651 $return = $right + 1;
652 return $return;
653 }
654
655
656 /**
657 * @defgroup tasks_advanced_support Functions that support the tasks_advanced system
658 */
659
660
661 /**
662 * Retrieve tasks_advanced part of a node using its nid.
663 *
664 * @ingroup tasks_advanced_support
665 * @param $nid integer Node's nid
666 * @return void
667 */
668 function _tasks_advanced_get_node($nid) {
669 $record = db_query('SELECT t.nid, t.task_parent, a.tasktype, a.task_tree_left, a.task_tree_right, a.task_tree_depth FROM {tasks} t INNER JOIN {tasks_advanced} a ON a.nid = t.nid WHERE t.nid = %d LIMIT 1', $nid);
670 $object = db_fetch_object($record);
671 return $object;
672 }
673
674
675 /**
676 * Retrieve tasks_advanced part of a node using its left value.
677 *
678 * @ingroup tasks_advanced_support
679 * @param $left integer Node's left
680 * @return void
681 */
682 function _tasks_advanced_get_node_by_left($left) {
683 $record = db_query('SELECT * FROM {tasks_advanced} WHERE task_tree_left = %d LIMIT 1', $left);
684 $object = db_fetch_object($record);
685 return $object;
686 }
687
688
689 /**
690 * Retrieve tasks_advanced part of a node using its right value.
691 *
692 * @ingroup tasks_advanced_support
693 * @param $right integer Node's right
694 * @return void
695 */
696 function _tasks_advanced_get_node_by_right($right) {
697 $record = db_query('SELECT * FROM {tasks_advanced} WHERE task_tree_right = %d LIMIT 1', $right);
698 $object = db_fetch_object($record);
699 return $object;
700 }
701
702
703 /**
704 * Insert node into tree structure as child of parent.
705 *
706 * @ingroup tasks_advanced_support
707 * @param $parent_node object Node where to insert child into tree
708 * @param $children integer Amount of children you want to make space for; default 1
709 * @return void
710 */
711 function _tasks_advanced_tree_insert(&$parent_node, $children = 1) {
712 $space = $children * 2;
713 db_query("UPDATE {tasks_advanced} SET task_tree_right = (task_tree_right + ".$space.") WHERE task_tree_right > %d", $parent_node->task_tree_right - 1);
714 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left + ".$space.") WHERE task_tree_left > %d", $parent_node->task_tree_right - 1);
715
716 return TRUE;
717 }
718
719
720 /**
721 * Extract node from tree structure.
722 *
723 * @ingroup tasks_advanced_support
724 * @param $node object Node to extract from tree
725 * @param $children integer Amount of children you want to remove space for; default 1
726 * @return void
727 */
728 function _tasks_advanced_tree_extract(&$node, $children = 1) {
729 $space = $children * 2;
730 db_query("UPDATE {tasks_advanced} SET task_tree_right = (task_tree_right - ".$space.") WHERE task_tree_right > %d", $node->task_tree_right - 1);
731 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left - ".$space.") WHERE task_tree_left > %d", $node->task_tree_right - 1);
732
733 return TRUE;
734 }
735
736
737 /**
738 * Move a branch of the tree to a different parent.
739 *
740 * @ingroup tasks_advanced_support
741 * @param $records recordset Recordset to extract nids from
742 * @return array Array of nids
743 */
744 function _tasks_advanced_fetch_ids_from_recordset($records) {
745 $ids = array();
746 while ($row = db_fetch_object($records)) {
747 $ids[] = $row->nid;
748 }
749
750 return $ids;
751 }
752
753
754 /**
755 * Change the order of a node, limited to the same parent.
756 *
757 * @ingroup tasks_advanced_support
758 * @param $node object Node to change order of
759 * @param $direction integer 1 or -1
760 * @return boolean TRUE on success, FALSE on failure
761 */
762 function _tasks_advanced_tree_sort(&$node, $direction) {
763 // get node to swap with: WHERE right = left - 1 || WHERE left = right + 1
764 if ($direction < 0) {
765 $node_b = _tasks_advanced_get_node_by_right($node->task_tree_left - 1);
766 $node_a = $node;
767 }
768 else {
769 $node_a = _tasks_advanced_get_node_by_left($node->task_tree_right + 1);
770 $node_b = $node;
771 }
772 if (!$node_a || !$node_b) {
773 return FALSE;
774 }
775
776 // get list of IDs for branch A
777 $records = db_query("SELECT nid FROM {tasks_advanced} WHERE task_tree_left BETWEEN %d AND %d", $node_a->task_tree_left, $node_a->task_tree_right);
778 $ids_a = _tasks_advanced_fetch_ids_from_recordset($records);
779 if (count($ids_a) == 0) {
780 return FALSE;
781 }
782 $space_a = $node_b->task_tree_right - $node_b->task_tree_left + 1;
783
784 // get list of IDs for branch B
785 $records = db_query("SELECT nid FROM {tasks_advanced} WHERE task_tree_left BETWEEN %d AND %d", $node_b->task_tree_left, $node_b->task_tree_right);
786 $ids_b = _tasks_advanced_fetch_ids_from_recordset($records);
787 if (count($ids_b) == 0) {
788 return FALSE;
789 }
790 $space_b = $node_a->task_tree_right - $node_a->task_tree_left + 1;
791
792 // update branch A
793 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left - %d), task_tree_right = (task_tree_right - %d) WHERE nid IN (%s)", $space_a, $space_a, implode(', ', $ids_a));
794
795 // update branch B
796 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left + %d), task_tree_right = (task_tree_right + %d) WHERE nid IN (%s)", $space_b, $space_b, implode(', ', $ids_b));
797
798 return TRUE;
799 }
800
801
802 /**
803 * Move a branch of the tree to a different parent.
804 *
805 * @ingroup tasks_advanced_support
806 * @param $node object Node to move
807 * @param $parent integer Parent node's nid
808 * @return boolean TRUE on success, FALSE on failure
809 */
810 function _tasks_advanced_tree_move(&$node, $parent) {
811 // fetch parent node
812 $parent_node = _tasks_advanced_get_node($parent);
813
814 // make sure we're not moving to a child of current branch
815 if ($node->task_tree_left < $parent_node->task_tree_left && $node->task_tree_right > $parent_node->task_tree_right) {
816 return FALSE;
817 }
818
819 // make sure we're not moving a node with an unchanged parent
820 if ($parent_node->task_tree_left < $node->task_tree_left &&
821 $parent_node->task_tree_right > $node->task_tree_right &&
822 $parent_node->task_tree_depth == $node->task_tree_depth - 1) {
823 /*
824 print "Not doing anything...<br />\n";
825 print "Level: {$node->task_tree_depth}<br />\n";
826 print "Parent Level: {$parent_node->task_tree_depth}<br />\n";
827 */
828 return FALSE;
829 }
830
831 // fetch ids for branch we're moving
832 $records = db_query("SELECT nid FROM {tasks_advanced} WHERE task_tree_left BETWEEN %d AND %d", $node->task_tree_left, $node->task_tree_right);
833 $ids = _tasks_advanced_fetch_ids_from_recordset($records);
834 $ids_str = implode(', ', $ids);
835
836 // calculate size of extracted node
837 $size_of_node = $node->task_tree_right - $node->task_tree_left + 1;
838 $depth_diff = $parent_node->task_tree_depth + 1 - $node->task_tree_depth;
839
840 // calculate min and max for left and right that we need to move
841 // this makes sure we don't affect any records needlessly
842 if ($parent_node->task_tree_left < $node->task_tree_left &&
843 $parent_node->task_tree_right > $node->task_tree_right &&
844 $parent_node->task_tree_depth < $node->task_tree_depth - 1) {
845
846 // moving up the chain
847 $minmax_a = $node->task_tree_right + 1;
848 $minmax_b = $parent_node->task_tree_right - 1;
849 $direction = -1;
850 $size_of_move = $parent_node->task_tree_right - 1 - $node->task_tree_right;
851 }
852 elseif ($parent_node->task_tree_left < $node->task_tree_left) {
853 // moving left
854 $minmax_a = $parent_node->task_tree_right;
855 $minmax_b = $node->task_tree_left;
856 $direction = 1;
857 $size_of_move = $parent_node->task_tree_right + $size_of_node - 1 - $node->task_tree_right;
858 }
859 else {
860 // moving right
861 $minmax_a = $parent_node->task_tree_right - 1;
862 $minmax_b = $node->task_tree_right;
863 $direction = -1;
864 $size_of_move = $parent_node->task_tree_right - 1 - $node->task_tree_right;
865 }
866 $min = min($minmax_a, $minmax_b);
867 $max = max($minmax_a, $minmax_b);
868
869 // remove space for item we're moving
870 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left + %d) WHERE task_tree_left >= %d AND task_tree_left <= %d AND nid NOT IN (%s)", $size_of_node * $direction, $min, $max, $ids_str);
871 db_query("UPDATE {tasks_advanced} SET task_tree_right = (task_tree_right + %d) WHERE task_tree_right >= %d AND task_tree_right <= %d AND nid NOT IN (%s)", $size_of_node * $direction, $min, $max, $ids_str);
872
873 // insert node as last of new parent node's children
874 db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left + %d), task_tree_right = (task_tree_right + %d), task_tree_depth = (task_tree_depth + %d) WHERE nid IN (%s)", $size_of_move, $size_of_move, $depth_diff, $ids_str);
875
876 return TRUE;
877 }
878
879
880 /**
881 * Output user headers.
882 *
883 * @ingroup tasks_advanced_support
884 * @param $uid integer User's id
885 * @param $username string User's name
886 * @return string html
887 */
888 function _tasks_advanced_user_headers($uid, $username) {
889 if ($username) {
890 $output .= '<h3>'.l(t('%name&#8217;s Tasks', array('%name' => ucfirst($username))), 'tasks/user/'.$uid, array(), NULL, NULL, FALSE, TRUE).'</h3>';
891 }
892 else {
893 $output .= '<h3>'.l(t('Unassigned Tasks'), 'tasks/user/'.$uid, array(), NULL, NULL, FALSE, TRUE).'</h3>';
894 }
895
896 return $output;
897 }
898
899
900 /**
901 * Output row headers.
902 *
903 * @ingroup tasks_advanced_support
904 * @param $rows array row
905 * @return string html
906 */
907 function _tasks_advanced_row_headers(&$rows) {
908 $header = array(
909 array('data' => '', 'class' => 'tasks_advanced-colourcode'),
910 array('data' => t('Assigned'), 'class' => 'tasks_advanced-assigned'),
911 array('data' => t('Task'), 'class' => 'tasks_advanced-title'),
912 array('data' => t('Complete?'), 'class' => 'tasks_advanced-completed'),
913 array('data' => t('Details'), 'class' => 'tasks_advanced-details'),
914 array('data' => t('Edit'), 'class' => 'tasks_advanced-edit'),
915 array('data' => t('Order'), 'class' => 'tasks_advanced-order'),
916 );
917 $output = theme('table', $header, $rows, array('class' => 'task-list'));
918
919 return $output;
920 }
921
922
923 /**
924 * Output a placeholder parent task.
925 *
926 * @ingroup tasks_advanced_support
927 * @param $object object task
928 * @param $type string "user" for individual user page
929 * @param $uid integer User's id
930 * @return array of table row
931 */
932 function _tasks_advanced_task_placeholder(&$object, $type = NULL, $uid = NULL, $subtask = FALSE) {
933 $assigned_name = $object->assigned_name;
934 if ($object->assigned_to == 0) {
935 $assigned_name = t('none');
936 }
937 $extra = drupal_unpack($object);
938 $tasks_colour = $extra->tasks_colour;
939 $task = node_load(array('nid' => $object->nid, 'type' => 'tasks'));
940
941 $row = array(
942 'data' => array(
943 array(
944 'data' => '<span'.($tasks_colour ? ' style="background-color: '.$tasks_colour.';"' : '').'>+</span>',
945 'class' => 'tasks_advanced-colourcode',
946 ),
947 array(
948 'data' => l($assigned_name, "tasks/user/{$task->assigned_to}"),
949 'class' => 'tasks_advanced-assigned',
950 ),
951 array(
952 'data' => ($subtask ? '<span style="font-family: monospace;">'.'&#160;&#x2514;&#x2500; '.'</span>'.t('Unknown Subtask') : t('Unknown Task')),
953 'class' => 'tasks_advanced-title',
954 ),
955 array(
956 'data' => '&#160;',
957 'class' => 'tasks_advanced-completed',
958 ),
959 array(
960 'data' => '&#160;',
961 'class' => 'tasks_advanced-details',
962 ),
963 array(
964 'data' => '&#160;',
965 'class' => 'tasks_advanced-edit',
966 ),
967 array(
968 'data' => '&#160;',
969 'class' => 'tasks_advanced-order',
970 ),
971 ),
972 );
973
974 return $row;
975 }
976
977
978 /**
979 * Output a single task.
980 *
981 * @ingroup tasks_advanced_support
982 * @param $object object task
983 * @param $type string "user" for individual user page
984 * @param $uid integer User's id
985 * @return array of table rows
986 */
987 function _tasks_advanced_task(&$object, $type = NULL, $uid = NULL) {
988 $rows = array();
989
990 $assigned_name = $object->assigned_name;
991 if ($object->assigned_to == 0) {
992 $assigned_name = t('none');
993 }
994 $extra = drupal_unpack($object);
995 $tasks_colour = $extra->tasks_colour;
996 $task = node_load(array('nid' => $object->nid, 'type' => 'tasks'));
997 $tasks_indent = ($object->task_tree_depth > 1 ? ((($object->task_tree_depth - 2) * 3) + 3) : 0);
998
999 $rows[] = array(
1000 'data' => array(
1001 array(
1002 'data' => l('<span'.($tasks_colour ? ' style="background-color: '.$tasks_colour.';"' : '').'>'.($task->tasktype == 'project' ? '+' : '&#160;').'</span>', "node/{$task->nid}", array(), NULL, NULL, FALSE, TRUE ),
1003 'class' => 'tasks_advanced-colourcode',
1004 ),
1005 array(
1006 'data' => l($assigned_name, "tasks/user/{$task->assigned_to}"),
1007 'class' => 'tasks_advanced-assigned',
1008 ),
1009 array(
1010 'data' => ($object->task_tree_depth > 1 ? '<span style="font-family: monospace;">'.str_repeat('&#160;', ($object->task_tree_depth - 2) * 4).'&#160;&#x2514;&#x2500; '.'</span>' : '').l($task->title, "node/{$task->nid}", array('name' => 'task'.$task->nid)), //"[{$task->task_tree_left}, {$task->task_tree_right}]".
1011 'class' => 'tasks_advanced-title',
1012 'style' => ($task->completed > 0 ? 'text-decoration: line-through; ' : '').($tasks_indent > 0 ? "padding-left: {$tasks_indent}ex; text-indent: -{$tasks_indent}ex;" : ''),
1013 ),
1014 array(
1015 'data' => ($task->completed > 0 ? $task->completed : t('No')),
1016 'class' => 'tasks_advanced-completed',
1017 ),
1018 array(
1019 'data' => "<a href='javascript: void(true);' onClick=\"javascript:toggleVisibility('row-{$task->nid}');\">".t('View')."</a> ",
1020 'class' => 'tasks_advanced-details',
1021 ),
1022 array(
1023 'data' => l("<img src=\"".base_path().drupal_get_path('module', 'tasks')."/icon_edit.gif\" border=\"0\" />", "node/{$task->nid}/edit/tasks".($type ? "/{$type}" : '').($uid ? "/{$uid}" : ''),array(),null,null,false,true),
1024 'class' => 'tasks_advanced-edit',
1025 ),
1026 array(
1027 'data' => l(t('Up'), $_GET['q'], array(), "action=up&task={$task->nid}").' '.l(t('Down'), $_GET['q'], array(), "action=down&task={$task->nid}"),
1028 'class' => 'tasks_advanced-order',
1029 ),
1030 ),
1031 );
1032 $task = node_prepare($task);
1033 $rows[] = array(
1034 'data' => array(
1035 '',
1036 array(
1037 'data' => $task->body,
1038 'colspan' => '6',
1039 ),
1040 ),
1041 'id' => "row-$task->nid",
1042 'class' => 'task-expand',
1043 'style' => 'display:none;',
1044 );
1045
1046 return $rows;
1047 }

  ViewVC Help
Powered by ViewVC 1.1.2