'tasks', 'title' => t('Tasks'), 'callback' => '_tasks_advanced_main', 'access' => user_access('access tasks main page') && user_access('access all tasks'), 'type' => MENU_CALLBACK ); $items[] = array( 'path' => 'tasks/user', 'title' => t('My tasks'), 'callback' => '_tasks_advanced_main', 'callback arguments' => array('user'), 'access' => user_access('access tasks main page') && user_access('access all tasks'), 'type' => MENU_CALLBACK ); // dynamic menu items $items[] = array( 'path' => 'tasks', 'title' => t('Tasks'), 'callback' => '_tasks_advanced_main', 'access' => user_access('access tasks main page') && user_access('access all tasks'), 'type' => MENU_DYNAMIC_ITEM ); if ($user->uid) { $items[] = array( 'path' => 'tasks/user/'.$user->uid, 'title' => t('My tasks'), 'callback' => '_tasks_advanced_main', 'callback arguments' => array('user', $user->uid), 'access' => user_access('access tasks main page') && user_access('access own tasks'), 'type' => MENU_DYNAMIC_ITEM ); } } return $items; } /** * Implementation of hook_form_alter(). * * @ingroup tasks_advanced_core */ function tasks_advanced_form_alter($form_id, &$form) { global $user; drupal_add_css(drupal_get_path('module', 'tasks_advanced').'/tasks_advanced.css'); $node = isset($form['#node']) ? $form['#node'] : NULL; $tasks_advanced_visible = (variable_get('event_nodeapi_'. $type, TRUE) ? FALSE : TRUE); $taskcategories = _tasks_advanced_taskcategory(); $tasktypes = _tasks_advanced_tasktype(); if ($form_id == 'tasks_node_form') { $form['tasks_advanced'] = array( '#type' => 'fieldset', '#title' => t('Details'), '#collapsible' => FALSE, '#collapsed' => $tasks_advanced_visible, '#attributes' => array("id" => "tasks_advanced"), '#weight' => -14, ); $form['tasks_advanced']['taskcategory'] = array( '#type' => 'select', '#title' => t('Task Category'), '#default_value' => (isset($node->taskcategory) ? $node->taskcategory : 'unknown'), '#options' => $taskcategories, '#description' => t(''), '#weight' => 0, ); $form['tasks_advanced']['tasktype'] = array( '#type' => 'radios', '#title' => t('Task Type'), '#default_value' => (isset($node->tasktype) ? $node->tasktype : 'action'), '#options' => $tasktypes, '#description' => t(''), '#weight' => 0, ); $form['from'] = array( '#type' => 'hidden', '#value' => arg(3).(arg(4) ? '/'.arg(4).(arg(5) ? '/'.arg(5) : '') : ''), ); // alter parent tasklist to only contain project types, and add a class for styling $parents = _tasks_advanced_projects($node->nid); $form['parent']['#prefix'] = '
User ID: '.$uid.'
'; // Add breadcrumb trail $bc[] = l(t('!name', array('!name' => ucfirst($new_user->name))), 'tasks/user/'.$uid); } } } else { drupal_set_title(t('Tasks')); if (!user_access('access all tasks')) { drupal_goto('tasks/user/'.$user->uid); } if ($uid != NULL) { drupal_goto('tasks'); } } // Finalize breadcrumb drupal_set_breadcrumb($bc); // Look to see if we need to change node order in this list if (($action = $_GET['action']) && ($tid = $_GET['task'])) { $task = node_load(array('nid' => $tid, 'type' => 'tasks')); if ($action == 'up') { $task->order_by -= 1.3; node_save($task); if (_tasks_advanced_tree_sort($task, -1)) { drupal_set_message(t('Task '.$task->title.' has been moved up the tasklist.')); } } elseif ($action == 'down') { $task->order_by += 1.3; node_save($task); if (_tasks_advanced_tree_sort($task, 1)) { drupal_set_message(t('Task '.$task->title.' has been moved down the tasklist.')); } } if ($type == 'user') { drupal_goto('tasks/user/'.$uid); } else { drupal_goto('tasks'); } } // Setup filtering $query = new StdClass(); if (isset($_SESSION['_tasks_advanced_query'])) { $query = $_SESSION['_tasks_advanced_query']; } else { // Set filtering defaults $query->status = array(1); } // Add user to filter if ($uid == NULL) { unset($query->uid); } else { $query->uid = array($uid); } // Populate filter $query = _tasks_advanced_query_parse($query); $_SESSION['_tasks_advanced_query'] = $query; // Make database calls $sql = _tasks_advanced_query_sql($query); $result = db_query($sql); $num_rows = db_num_rows($result); // Enable the list of tasks to be filtered $output .= drupal_get_form('tasks_advanced_view_filter_form', $num_rows, $query); // only proceed if we have tasks to display if ($num_rows > 0) { $rows = array(); $init = TRUE; $userid = -1; $username = -1; // loop through each task while ($task = db_fetch_object($result)) { // is this a new user? if ($username != $task->assigned_name) { // if we're showing all user's tasks, we need to add headers for each user if ($type != 'user') { if ($init == FALSE) { $output .= _tasks_advanced_user_headers($userid, $username); $output .= _tasks_advanced_row_headers($rows); $rows = array(); } else { // initialization complete $init = FALSE; } } $userid = $task->assigned_to; $username = $task->assigned_name; unset($previous_task); } // Is another user the owner of our parent? // If so, we need to insert an 'unknown' parent // to keep hierarchy somewhat consistent. // It's still a bit of a hack, since it doesn't // show the real parent, and doesn't reflect // the depth either. if (isset($previous_task)) { if (($task->task_tree_depth > $previous_task->task_tree_depth && $task->parent != $previous_task->nid) || ($task->task_tree_depth == $previous_task->task_tree_depth && $task->parent != $previous_task->parent)) { if ($task->task_tree_left > $previous_task->task_tree_left && $task->task_tree_right < $previous_task->task_tree_right && $task->task_tree_depth > 2) { // this is a grandchild of this task $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid, TRUE); } else { $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid); } } } else { if ($task->task_tree_depth > 1) { $rows[] =_tasks_advanced_task_placeholder($task, $type, $uid); } } $previous_task = drupal_clone($task); $task_rows = _tasks_advanced_task($task, $type, $uid); $rows[] = $task_rows[0]; $rows[] = $task_rows[1]; } if ($type != 'user') { $output .= _tasks_advanced_user_headers($userid, $username); } $output .= _tasks_advanced_row_headers($rows); $rows = array(); } else { // no tasks to display $output .= ''.t('No tasks found.').'
'; } print theme('page', $output); } /** * Filtering for tasklist */ function tasks_advanced_view_filter_form($num_rows, &$query) { // Create dropdown options lists $status = array(1 => t('Incomplete Tasks'), 2 => t('Complete Tasks'), 0 => t('All Tasks')); $taskcategories = array_merge(array(0 => 'All Tasks'), _tasks_advanced_taskcategory()); $tasktypes = array_merge(array(0 => 'All Tasks'), _tasks_advanced_tasktype()); // Enable the list of tasks to be filtered $form['filter'] = array( '#type' => 'fieldset', '#title' => t('Filter'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => -1, ); $form['filter']['label'] = array( '#value' => t('Showing %num_rows tasks.', array('%num_rows' => $num_rows)), ); $form['filter']['status'] = array( '#type' => 'select', '#title' => t('Status'), '#default_value' => (isset($query->status) ? $query->status[0] : '0'), '#options' => $status, ); $form['filter']['taskcategory'] = array( '#type' => 'select', '#title' => t('Category'), '#default_value' => (isset($query->taskcategory) ? $query->taskcategory[0] : '0'), '#options' => $taskcategories, ); $form['filter']['tasktype'] = array( '#type' => 'select', '#title' => t('Task Type'), '#default_value' => (isset($query->tasktype) ? $query->tasktype[0] : '0'), '#options' => $tasktypes, ); $form['filter']['submit'] = array( '#type' => 'submit', '#value' => t('Filter') ); $form['#redirect'] = FALSE; return $form; } function _tasks_advanced_query_parse($query = NULL) { $fields = array('status', 'taskcategory', 'tasktype'); if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST['form_id'] == 'tasks_advanced_view_filter_form') { foreach ($_POST as $key => $value) { if (!empty($value) && in_array($key, $fields)) { $query->$key = !is_array($value) ? explode(',', $value) : $value; $_POST[$key] = is_array($value) ? implode(',', $value) : $value; } else { unset($query->$key); } } $_POST = array(); } else { foreach ($_GET as $key => $value) { if (!empty($value) && in_array($key, $fields)) { $query->$key = explode(',', $value); } else { unset($query->$key); } } } return $query; } function _tasks_advanced_query_sql_field($field, $values, $operator = ' OR ') { $sql = array(); if (!is_array($values)) { $values = array($values); } foreach ($values as $value) { $value = db_escape_string($value); $sql[] = ($like && $field != 'p.pid') ? "$field LIKE '%$value%'" : "$field = '$value'"; } if ($sql) { return '('. implode($operator, $sql) .')'; } } function _tasks_advanced_query_sql($query) { $order = 'u.name, a.task_tree_left ASC, IF(t.completed = 0, 0, 1), t.completed DESC'; $sql = array(); foreach ($query as $key => $value) { switch ($key) { case 'status': switch ($value[0]) { case 1: $order = 'u.name, a.task_tree_left ASC'; $sql[] = 't.completed = 0000-00-00'; break; case 2: $order = 'u.name, a.task_tree_left ASC, t.completed DESC'; $sql[] = 't.completed != 0000-00-00'; break; } break; case 'taskcategory': $sql[] = _tasks_advanced_query_sql_field('a.taskcategory', $value); break; case 'tasktype': $sql[] = _tasks_advanced_query_sql_field('a.tasktype', $value); break; case 'uid': $sql[] = _tasks_advanced_query_sql_field('u.uid', $value); break; } } $sql_str = "SELECT n.nid, t.task_parent as 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 FROM {node} n INNER JOIN {tasks} t ON t.nid = n.nid INNER JOIN {tasks_advanced} a ON a.nid = n.nid LEFT JOIN {users} u ON u.uid = t.assigned_to WHERE n.status = 1 AND n.type='tasks' ". (count($sql) > 0 ?" AND (". implode(") AND (", $sql) .")" : '').' '." ORDER BY {$order}"; $sql = db_rewrite_sql($sql_str); return $sql; } /** * Rebuild tree structure. * * Based on "Modified Preorder Tree Traversal" article at * http://www.sitepoint.com/article/hierarchical-data-database * * @param $parent object task * @param $left integer left side of tree * @return $right integer */ function tasks_advanced_rebuild_tree($parent, $left, $depth = 0) { // the right value of this node is the left value + 1 $right = $left + 1; // get all children of this node $result = db_query("SELECT nid tas_parent as parent FROM {tasks} WHERE task_parent = %d", $parent); while ($row = db_fetch_object($result)) { // recursive execution of this function for each // child of this node // $right is the current right value, which is // incremented by the rebuild_tree function $right = tasks_advanced_rebuild_tree($row->parent, $right, $depth + 1); } // we've got the left value, and now that we've processed // the children of this node we also know the right value 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); // return the right value of this node + 1 $return = $right + 1; return $return; } /** * @defgroup tasks_advanced_support Functions that support the tasks_advanced system */ /** * Retrieve tasks_advanced part of a node using its nid. * * @ingroup tasks_advanced_support * @param $nid integer Node's nid * @return void */ function _tasks_advanced_get_node($nid) { $record = db_query('SELECT t.nid, t.task_parent as 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); $object = db_fetch_object($record); return $object; } /** * Retrieve tasks_advanced part of a node using its left value. * * @ingroup tasks_advanced_support * @param $left integer Node's left * @return void */ function _tasks_advanced_get_node_by_left($left) { $record = db_query('SELECT * FROM {tasks_advanced} WHERE task_tree_left = %d LIMIT 1', $left); $object = db_fetch_object($record); return $object; } /** * Retrieve tasks_advanced part of a node using its right value. * * @ingroup tasks_advanced_support * @param $right integer Node's right * @return void */ function _tasks_advanced_get_node_by_right($right) { $record = db_query('SELECT * FROM {tasks_advanced} WHERE task_tree_right = %d LIMIT 1', $right); $object = db_fetch_object($record); return $object; } /** * Insert node into tree structure as child of parent. * * @ingroup tasks_advanced_support * @param $parent_node object Node where to insert child into tree * @param $children integer Amount of children you want to make space for; default 1 * @return void */ function _tasks_advanced_tree_insert(&$parent_node, $children = 1) { $space = $children * 2; db_query("UPDATE {tasks_advanced} SET task_tree_right = (task_tree_right + ".$space.") WHERE task_tree_right > %d", $parent_node->task_tree_right - 1); db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left + ".$space.") WHERE task_tree_left > %d", $parent_node->task_tree_right - 1); return TRUE; } /** * Extract node from tree structure. * * @ingroup tasks_advanced_support * @param $node object Node to extract from tree * @param $children integer Amount of children you want to remove space for; default 1 * @return void */ function _tasks_advanced_tree_extract(&$node, $children = 1) { $space = $children * 2; db_query("UPDATE {tasks_advanced} SET task_tree_right = (task_tree_right - ".$space.") WHERE task_tree_right > %d", $node->task_tree_right - 1); db_query("UPDATE {tasks_advanced} SET task_tree_left = (task_tree_left - ".$space.") WHERE task_tree_left > %d", $node->task_tree_right - 1); return TRUE; } /** * Move a branch of the tree to a different parent. * * @ingroup tasks_advanced_support * @param $records recordset Recordset to extract nids from * @return array Array of nids */ function _tasks_advanced_fetch_ids_from_recordset($records) { $ids = array(); while ($row = db_fetch_object($records)) { $ids[] = $row->nid; } return $ids; } /** * Change the order of a node, limited to the same parent. * * @ingroup tasks_advanced_support * @param $node object Node to change order of * @param $direction integer 1 or -1 * @return boolean TRUE on success, FALSE on failure */ function _tasks_advanced_tree_sort(&$node, $direction) { // get node to swap with: WHERE right = left - 1 || WHERE left = right + 1 if ($direction < 0) { $node_b = _tasks_advanced_get_node_by_right($node->task_tree_left - 1); $node_a = $node; } else { $node_a = _tasks_advanced_get_node_by_left($node->task_tree_right + 1); $node_b = $node; } // get list of IDs for branch A $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); $ids_a = _tasks_advanced_fetch_ids_from_recordset($records); if (count($ids_a) == 0) { return FALSE; } $space_a = $node_b->task_tree_right - $node_b->task_tree_left + 1; // get list of IDs for branch B $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); $ids_b = _tasks_advanced_fetch_ids_from_recordset($records); if (count($ids_b) == 0) { return FALSE; } $space_b = $node_a->task_tree_right - $node_a->task_tree_left + 1; // update branch A 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)); // update branch B 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)); return TRUE; } /** * Move a branch of the tree to a different parent. * * @ingroup tasks_advanced_support * @param $node object Node to move * @param $parent integer Parent node's nid * @return boolean TRUE on success, FALSE on failure */ function _tasks_advanced_tree_move(&$node, $parent) { // fetch parent node $parent_node = _tasks_advanced_get_node($parent); // make sure we're not moving to a child of current branch if ($node->task_tree_left < $parent_node->task_tree_left && $node->task_tree_right > $parent_node->task_tree_right) { return FALSE; } // make sure we're not moving a node with an unchanged parent if ($parent_node->task_tree_left < $node->task_tree_left && $parent_node->task_tree_right > $node->task_tree_right && $parent_node->task_tree_depth == $node->task_tree_depth - 1) { /* print "Not doing anything...