00001 <?php if(!function_exists('startedIndexPhp')) { header("location:../index.php"); exit();}
00002 # streber - a php based project management system
00003 # Copyright (c) 2005 Thomas Mann - thomas@pixtur.de
00004 # Distributed under the terms and conditions of the GPL as stated in docs/license.txt
00005
00006
00007 require_once(confGet('DIR_STREBER') . 'db/class_issue.inc.php');
00008
00009 define('TEXT_LEN_PREVIEW', 120);
00010
00011
00012 class ListGroupingFolder extends ListGrouping
00013 {
00014
00015 public function __construct($args=NULL) {
00016 $this->id = 'parent_task';
00017 parent::__construct($args);
00018 }
00019
00023 public function render(&$item)
00024 {
00025 if(! ($item instanceof Task)) {
00026 trigger_error("can't group for status",E_USER_NOTICE);
00027 return "---";
00028 }
00029 else {
00030 return $item->getFolderLinks(false);
00031 }
00032 }
00033 }
00034
00035
00036
00037
00048 class ListBlock_tasks extends ListBlock
00049 {
00050
00051 public $filters = array(); # assoc arry of ListFilters
00052 #public $active_block_function = 'tree'; #@@@ HACK
00053 public $tasks_assigned_to = NULL;
00054 public $use_short_names = false;
00055 public $show_summary = false;
00056
00057 public function __construct($args=NULL)
00058 {
00059 global $PH;
00060 parent::__construct(array());
00061
00062 $this->id = 'tasks';
00063 $this->bg_style ='bg_projects';
00064 $this->no_items_html=NULL;
00065 $this->title = __("Tasks");
00066
00067 ### filter params ###
00068 if($args) {
00069 foreach($args as $key=>$value) {
00070 if(!isset($this->$key) && !is_null($this->$key) && !$this->$key==="") {
00071 trigger_error("unknown parameter",E_USER_NOTICE);
00072 }
00073 else {
00074 $this->$key= $value;
00075 }
00076 }
00077 }
00078
00079
00080
00084 #$this->filters=array(
00085 # new ListFilter_status_min(),
00086 # new ListFilter_status_max(),
00087 # new ListFilter_not_older(array(
00088 # 'not_older' => 13*25*60*60,
00089 # )),
00090 #);
00091
00092 ### columns ###
00093 $this->add_col( new ListBlockColSelect());
00094 $this->add_col( new ListBlockColPrio(array(
00095 'key'=>'prio',
00096 'name'=>"P",
00097 'tooltip'=>__("Priority of task"),
00098 'sort'=>1
00099 )));
00100 $this->add_col(new ListBlockColMethod(array(
00101 'key'=>'project',
00102 'name'=>__('Project'),
00103 'func'=>'getProjectLink'
00104 )));
00105
00106 $this->add_col( new ListBlockColStatus(array(
00107 'key'=>'status',
00108 'name'=>__("Status","Columnheader"),
00109 'tooltip'=>__("Task-Status"),
00110 'sort'=>0
00111 )));
00112
00113 $this->add_col( new ListBlockCol_TaskLabel());
00114 $this->add_col( new ListBlockColMethod(array(
00115 'key'=>'parent_task',
00116 'name'=>__('Folder'),
00117 'func'=>'getFolderLinks'
00118 )));
00119
00120 $this->add_col( new ListBlockCol_TasknameWithFolder());
00121 $this->add_col( new ListBlockCol_TaskName(array(
00122 'use_short_names'=>$this->use_short_names,
00123 'indention'=>true
00124 )));
00125 $this->add_col( new ListBlockCol_TaskAssignedTo(array('use_short_names'=>false )));
00126
00127
00128
00129
00130
00131 $this->add_col( new listBlockColDate(array(
00132 'key'=>'modified',
00133 'name'=>__('Modified','Column header'),
00134 )));
00135
00136 $this->add_col( new listBlockColDate(array(
00137 'key'=>'date_closed',
00138 'name'=>__('Closed'),
00139 )));
00140 #$this->add_col( new ListBlockCol_TaskCreatedBy( array('use_short_names'=>false,'indention'=>true)));
00141
00142 $this->add_col( new ListBlockColTime(array(
00143 'key'=>'estimated',
00144 'name'=>__("Est."),
00145 'tooltip'=>__("Estimated time in hours"),
00146 'sort'=>0,
00147 )));
00148 $this->add_col( new ListBlockCol_Milestone);
00149 $this->add_col( new ListBlockCol_EstimatedComplete);
00150 $this->add_col( new ListBlockCol_DaysLeft);
00151 $this->add_col( new ListBlockCol_TaskSumEfforts());
00152 if(confget('TASK_LIST_EFFORT_RELATION_COLUMN')) {
00153 $this->add_col( new ListBlockCol_TaskRelationEfforts);
00154 }
00155 $this->add_col( new ListBlockColPubLevel());
00156
00157 ### functions ###
00158 $this->add_function(new ListFunction(array(
00159 'target'=>$PH->getPage('taskEdit')->id,
00160 'name' =>__('Edit'),
00161 'id' =>'taskEdit',
00162 'icon' =>'edit',
00163 'context_menu'=>'submit',
00164 )));
00165
00166 $this->add_function(new ListFunction(array(
00167 'target'=>$PH->getPage('taskNew')->id,
00168 'name' =>__('Add new Task'),
00169 'id' =>'taskNew',
00170 'icon' =>'new',
00171 'context_menu'=>'submit',
00172 )));
00173 $this->add_function(new ListFunction(array(
00174 'target'=>$PH->getPage('taskNewBug')->id,
00175 'name' =>__('Report new Bug'),
00176 'id' =>'taskNewBug',
00177 'icon' =>'new',
00178 'context_menu'=>'submit',
00179 'dropdown_menu'=>false,
00180 )));
00181 $this->add_function(new ListFunction(array(
00182 'target'=>$PH->getPage('commentNew')->id,
00183 'name' =>__('Add comment'),
00184 'id' =>'commentNew',
00185 'context_menu'=>'submit',
00186 )));
00187 $this->add_function(new ListFunction(array(
00188 'target'=>$PH->getPage('tasksDelete')->id,
00189 'name' =>__('Delete'),
00190 'id' =>'tasksDelete',
00191 'icon' =>'delete',
00192 'context_menu'=>'submit',
00193 )));
00194 $this->add_function(new ListFunction(array(
00195 'target'=>$PH->getPage('tasksComplete')->id,
00196 'name' =>__('Status->Completed'),
00197 'id' =>'tasksCompleted',
00198 'icon' =>'complete',
00199 'context_menu'=>'submit',
00200 )));
00201 $this->add_function(new ListFunction(array(
00202 'target'=>$PH->getPage('tasksApproved')->id,
00203 'name' =>__('Status->Approved'),
00204 'id' =>'tasksApproved',
00205 'icon' =>'approve',
00206 'context_menu'=>'submit',
00207 )));
00208 $this->add_function(new ListFunction(array(
00209 'target'=>$PH->getPage('tasksClosed')->id,
00210 'name' =>__('Status->Closed'),
00211 'id' =>'tasksClosed',
00212 'icon' =>'close',
00213 'context_menu'=>'submit',
00214 )));
00215
00216 $this->add_function(new ListFunction(array(
00217 'target'=>$PH->getPage('tasksMoveToFolder')->id,
00218 'name' =>__('Move tasks'),
00219 'id' =>'tasksMoveToFolder',
00220 'context_menu'=>'submit'
00221 )));
00222
00223 $this->add_function(new ListFunction(array(
00224 'target'=>$PH->getPage('effortNew')->id,
00225 'name' =>__('Log hours for select tasks'),
00226 'id' =>'effortNew',
00227 'icon' =>'loghours',
00228 'context_menu'=>'submit',
00229 )));
00230
00231 $this->add_function(new ListFunction(array(
00232 'target'=>$PH->getPage('itemsAsBookmark')->id,
00233 'name' =>__('Mark as bookmark'),
00234 'id' =>'itemsAsBookmark',
00235 'context_menu'=>'submit',
00236 )));
00237
00238
00239 ### block style functions ###
00240 $this->add_blockFunction(new BlockFunction(array(
00241 'target'=>'changeBlockStyle',
00242 'key'=>'list',
00243 'name'=>__('List','List sort mode'),
00244 'params'=>array(
00245 'style'=>'list',
00246 'block_id'=>$this->id,
00247 'page_id'=>$PH->cur_page->id,
00248 # 'use_collapsed'=>true,
00249 ),
00250 )));
00251 $this->add_blockFunction(new BlockFunction(array(
00252 'target'=>'changeBlockStyle',
00253 'key'=>'tree',
00254 'name'=>__('Tree','List sort mode'),
00255 'params'=>array(
00256 'style'=>'tree',
00257 'block_id'=>$this->id,
00258 'page_id'=>$PH->cur_page->id,
00259 ),
00260 #'default'=>true,
00261 )));
00262 $this->groupings= new BlockFunction_grouping(array(
00263 'target'=>'changeBlockStyle',
00264 'key'=>'grouped',
00265 'name'=>__('Grouped','List sort mode'),
00266 'params'=>array(
00267 'style'=>'grouped',
00268 'block_id'=>$this->id,
00269 'page_id'=>$PH->cur_page->id,
00270 ),
00271 ));
00272 $this->add_blockFunction($this->groupings);
00273
00274
00275
00276 ### list groupings ###
00277
00278 $this->groupings->groupings= array(
00279 new ListGroupingFolder(),
00280 new ListGroupingStatus(),
00281 new ListGroupingPrio(),
00282 new ListGroupingCreatedBy(),
00283 );
00284
00285
00286 $this->initOrderQueryOption('order_id');
00287 }
00288
00292 public function render_list(&$tasks=NULL)
00293 {
00294 #global $PH;
00295 #require_once(confGet('DIR_STREBER') . 'render/render_wiki.inc.php');
00296
00297 switch($this->page->format){
00298 case FORMAT_CSV:
00299 #$this->renderListCsv($list);
00300 $this->renderListCSV($tasks);
00301 break;
00302
00303 default:
00304 $this->renderListHtml($tasks);
00305 break;
00306 }
00307 }
00308
00312 public function renderListHtml(&$tasks=NULL)
00313 {
00314 global $PH;
00315 require_once(confGet('DIR_STREBER') . 'render/render_wiki.inc.php');
00316
00317 $this->render_header();
00318 if($this->groupings) {
00319 $this->groupings->getActiveFromCookie();
00320 }
00321
00326 $last_project_id= 0;
00327 $project= NULL;
00328
00329
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343 $style='';
00344 if(!$tasks && $this->no_items_html) {
00345 $this->render_tfoot_empty();
00346 }
00347 else {
00348
00349 ### render table lines ###
00350 $this->render_thead();
00351 $count_estimated=0;
00352
00353 $last_group= NULL;
00354 foreach($tasks as $t) {
00355
00356 ### grouped style ###
00357 if($this->groupings && $this->active_block_function == 'grouped') {
00358 $gr= $this->groupings->active_grouping_key;
00359
00360 if($t->project != $last_project_id) {
00361 $project= Project::getVisibleById($t->project);
00362 $last_project_id= $t->project;
00363 }
00364
00365
00366 if($gr=='parent_task') {
00367 if($t->category == TCATEGORY_FOLDER) {
00368 echo '<tr class=group><td colspan='. count($this->columns) .'>';
00369 #. $this->groupings->active_grouping_obj->render($t)
00370
00371
00372 ### toggle ###
00373 $description= '';
00374 if($t->view_collapsed) {
00375 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_closed.gif") .'">',array('tsk'=>$t->id),'folder_collapsed', true);
00376
00377 ### number of subtasks with folded ###
00378 $description='<span class=diz>( ' . sprintf(__('%s hidden'), $t->getNumSubtasks()) .')</span>';
00379 }
00380 else {
00381 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_open.gif") . '">',array('tsk'=>$t->id),'folder_open', true);
00382 }
00383 echo $toggle;
00384
00385 if($parents= $t->getFolderLinks(false)) {
00386 echo $parents. '<em>></em>';
00387 }
00388 echo $t->getLink();
00389 echo $description;
00390 echo '</td></tr>';
00391 continue;
00392 }
00393
00394 }
00395 else {
00396 if($last_group != $t->$gr) {
00397 echo '<tr class=group><td colspan='. count($this->columns) .'>'. $this->groupings->active_grouping_obj->render($t).'</td></tr>';
00398 $last_group = $t->$gr;
00399 }
00400 }
00401 }
00402
00403 $style=($t->category == TCATEGORY_FOLDER)
00404 ?' isFolder'
00405 :'';
00406
00407 ### done ###
00408 if(@intval($t->status) >= STATUS_COMPLETED) {
00409 $style.= ' isDone';
00410 }
00411 else {
00412 $count_estimated+=$t->estimated;
00413 }
00414
00415 $this->render_trow(&$t,$style);
00416
00417
00418 ### render additional information ###
00419 if($this->groupings && $this->active_block_function == 'grouped') {
00420 echo '<tr class=details><td colspan='. count($this->columns) .'>';
00421
00422
00423 ### block 1 ###
00424 {
00425 $html_buffer='';
00426 if($t->issue_report) {
00427 $ir=Issue::getById($t->issue_report);
00428 }
00429 else {
00430 $ir= NULL;
00431 }
00432 global $g_severity_names;
00433 global $g_reproducibility_names;
00434 global $g_prio_names;
00435 global $g_status_names;
00436
00437
00438
00439 #if($t->prio != PRIO_NORMAL && isset($g_prio_names[$t->prio])) {
00440 # echo "<p>".$g_prio_names[$t->prio]."</p>";
00441 #}
00442
00443 if($ir && $ir->severity && isset($g_severity_names[$ir->severity])) {
00444 $html_buffer.= "<p>".$g_severity_names[$ir->severity]."</p>";
00445 }
00446 if($ir && $ir->version) {
00447 $html_buffer.= "<p>".$ir->version."</p>";
00448 }
00449
00450 if($ir && $ir->production_build) {
00451 $html_buffer.= "<p>".$ir->production_build."</p>";
00452 }
00453
00454 if($ir && $ir->reproducibility && isset($g_reproducibility_names[$ir->reproducibility])) {
00455 $html_buffer.= "<p>".$g_reproducibility_names[$ir->reproducibility]."</p>";
00456 }
00457
00458 if($t->status != STATUS_OPEN && $t->status != STATUS_NEW && isset($g_status_names[$t->status])) {
00459 $html_buffer.= "<p>".$g_status_names[$t->status]."</p>";
00460 }
00461
00462 echo '<div class=severity>';
00463 if($html_buffer) {
00464 echo $html_buffer;
00465 }
00466 else {
00467 echo ' '; # dummy content to keep floating
00468 }
00469
00470
00471 echo '</div>';
00472 }
00473
00474 ### block description ###
00475 {
00476 echo '<div class=description> ';
00477 if($t->description) {
00478 echo "<p>".wiki2html($t->description, $project)."</p>";
00479 }
00480
00481
00482
00483 ### steps ###
00484 if($ir && isset($ir->steps_to_reproduce) && $ir->steps_to_reproduce) {
00485 echo "<p>".wiki2html($ir->steps_to_reproduce, $project)."</p>";
00486 }
00487 if($ir && isset($ir->expected_result) && $ir->expected_result) {
00488 echo "<p>".wiki2html($ir->expected_result, $project)."</p>";
00489 }
00490 if($ir && isset($ir->suggested_solution) && $ir->suggested_solution) {
00491 echo "<p>".wiki2html($ir->suggested_solution, $project)."</p>";
00492 }
00493
00494
00495 echo ' </div>';
00496 }
00497
00498
00499 ### block last comment ###
00500 {
00501 echo '<div class=steps>';
00502
00503 if($c = $t->getLatestComment()) {
00504
00505 echo "<p>".__('Latest Comment')." ";
00506 if($person= Person::getVisibleById($c->created_by)) {
00507 echo __("by").' '.$person->getLink();
00508 }
00509 echo " (". renderDateHtml($c->modified). "):";
00510 echo "</p>";
00511
00512 if($c->name) {
00513 echo '<p>'. asHtml($c->name) . '</p>';
00514 }
00515 if($c->description) {
00516 echo "<p>". wiki2html($c->description, $project). "</p>";
00517 }
00518 }
00519 echo ' </div>';
00520 }
00521
00522 ### assigned to ###
00523 {
00524 echo '<div class=assigned_to>';
00525
00526 $persons= $t->getAssignedPersons();
00527 if($persons) {
00528 $sep='';
00529 echo "<p>".__('for')." ";
00530 foreach($persons as $p) {
00531 echo $sep . $p->getLink();
00532 $sep= ', ';
00533 }
00534 echo "</p>";
00535 }
00536 echo '</div>';
00537 }
00538
00539
00540
00541
00542 echo '</td></tr>';
00543 }
00544 }
00545 #$this->render_trow(&$t);
00546
00547 if($this->show_summary) {
00548 $this->summary=sprintf(__("%s open tasks / %s h"), count($tasks), $count_estimated);
00549 }
00550 else {
00551 $this->summary= '';
00552 }
00553 $this->render_tfoot();
00554 }
00555 }
00556
00557
00567 public function print_automatic($project=NULL, $parent_task=NULL, $filter_empty_folders=false)
00568 {
00569 global $PH;
00570 global $auth;
00571
00572 if($project) {
00573 $this->query_options['project']= $project->id;
00574 }
00575
00576 if(!$this->no_items_html && $parent_task) {
00577 $this->no_items_html=
00578 $PH->getLink('taskNewFolder',__('New Folder'),array('parent_task'=>$parent_task->id, 'prj'=>$project->id))
00579 ." ". __("or")." "
00580 . $PH->getLink('taskNew','',array('parent_task'=>$parent_task->id));
00581 }
00582 else if(!$this->no_items_html && $project) {
00583 $this->no_items_html=
00584 $PH->getLink('taskNewFolder',__('New Folder'),array('prj'=>$project->id))
00585 ." ". __("or")." "
00586 . $PH->getLink('taskNew','',array('prj'=>$project->id));
00587 }
00588
00589
00590 if(!$this->active_block_function=$this->getBlockStyleFromCookie()) {
00591 $this->active_block_function = 'list';
00592 }
00593
00594 if($parent_task) {
00595 $this->query_options['parent_task']= $parent_task->id;
00596 }
00597
00598 $this->group_by= get("blockstyle_{$PH->cur_page->id}_{$this->id}_grouping");
00599
00600 #$filter_milestone= new ListFilter_for_milestone();
00601 #$this->filters[]= new ListFilter_for_milestone();
00602
00603
00604
00605
00606 ### add filter options ###
00607 foreach($this->filters as $f) {
00608 foreach($f->getQuerryAttributes() as $k=>$v) {
00609 $this->query_options[$k]= $v;
00610 }
00611 }
00612
00613 $sort_cookie= "sort_{$PH->cur_page->id}_{$this->id}_{$this->active_block_function}";
00614 if($sort= get($sort_cookie)) {
00615 $this->query_options['order_by']= $sort;
00616 }
00617
00618
00619 if($auth->cur_user->user_rights & RIGHT_VIEWALL) {
00620 $this->query_options['visible_only']=false;
00621 }
00622 $tasks = array();
00623
00624
00625 ### hide columns depending on project options ###
00626 if($project && !($project->settings & PROJECT_SETTING_MILESTONES)) {
00627 unset($this->columns['for_milestone']);
00628 }
00629
00630 if($project && !($project->settings & PROJECT_SETTING_EFFORTS)) {
00631 unset($this->columns['efforts']);
00632 }
00633
00634
00635 ### list view ###
00636 if($this->active_block_function == 'list') {
00637
00638 if($this->tasks_assigned_to) {
00639 $this->query_options['assigned_to_person']= $this->tasks_assigned_to;
00640 }
00641 $this->query_options['show_folders'] = false;
00642
00643 $this->query_options['order_by'] = $this->groupings->getActiveFromCookie() . ",".$this->query_options['order_by'];
00644
00645 unset($this->columns['parent_task']);
00646 unset($this->columns['date_closed']);
00647 # unset($this->columns['pub_level']);
00648 unset($this->columns['name']);
00649 unset($this->columns['estimated']);
00650 $tasks= Task::getAll($this->query_options);
00651 }
00652
00653 ### grouped ###
00654 else if($this->active_block_function == 'grouped') {
00655 if($this->tasks_assigned_to) {
00656 $this->query_options['assigned_to_person']= $this->tasks_assigned_to;
00657 }
00658
00659 unset($this->columns['date_closed']);
00660 unset($this->columns['parent_task']);
00661 unset($this->columns['name']);
00662 # unset($this->columns['pub_level']);
00663 unset($this->columns['created_by']);
00664 unset($this->columns['estimated']);
00665 unset($this->columns['planned_start']);
00666 unset($this->columns['assigned_to']);
00667 unset($this->columns['status']);
00668 #unset($this->columns['prio']);
00669
00670 ### prepend key to sorting ###
00671 if(isset($this->query_options['order_by'])) {
00672 $this->query_options['order_by'] = $this->groupings->getActiveFromCookie() . ",".$this->query_options['order_by'];
00673 }
00674 else {
00675 $this->query_options['order_by'] = $this->groupings->getActiveFromCookie();
00676 }
00677
00678 ### sort folders ? ###
00679
00684 if($this->groupings->active_grouping_key == 'parent_task') {
00685 $this->query_options['sort_hierarchical']= true;
00686 $this->query_options['use_collapsed'] = true;
00687 $this->query_options['show_folders'] = true;
00688 $this->query_options['order_by'] = 'is_folder' . ",".$this->query_options['order_by'];
00689 $this->query_options['parent_task']= NULL;
00690 }
00691 else {
00692
00693
00694 #$this->query_options['sort_hierarchical']= true;
00695 #$this->query_options['use_collapsed'] = true;
00696 $this->query_options['show_folders']= false;
00697 }
00698
00699 ### remove current grouping key as column ###
00700 unset($this->columns[$this->groupings->getActiveFromCookie()]);
00701 $tasks= Task::getAll($this->query_options);
00702
00703 }
00704
00705 ### tree view ###
00706 else {
00707
00708 ### first get only folders ###
00709 $parent_task_id = $parent_task
00710 ? $parent_task->id
00711 : NULL;
00712
00713 $t_order_by= isset($this->query_options['order_by'])
00714 ? $this->query_options['order_by']
00715 : 'order_id,name';
00716
00717
00718 $tmp_options= array(
00719 'visible_only' => true,
00720 'folders_only' => true,
00721 'sort_hierarchical' => true,
00722 'use_collapsed' => true,
00723 'order_by' => $t_order_by,
00724 'parent_task' => $parent_task_id,
00725 'status_min' => STATUS_NEW,
00726 'status_max' => STATUS_CLOSED,
00727 );
00728 #if(isset($this->query_options['status_min'])) { $tmp_options['status_min']= $this->query_options['status_min'];};
00729 #if(isset($this->query_options['status_max'])) { $tmp_options['status_max']= $this->query_options['status_max'];};
00730
00731
00732
00733
00734 if(isset($project)) {
00735 $tmp_options['project']= $project->id;
00736 }
00737
00738 $tmp_folders= Task::getAll($tmp_options);
00739
00740 $folders= array();
00741 foreach($tmp_folders as $f) {
00742 $folders[$f->id]= $f;
00743 }
00744
00745 # $this->query_options['sort_hierarchical']= true;
00746
00747 # if(!$this->tasks_assigned_to) {
00748 # $this->query_options['use_collapsed'] = true;
00749 # }
00750
00756 unset($this->columns['parent_task']);
00757 unset($this->columns['taskwithfolder']);
00758 unset($this->columns['date_closed']);
00759 # unset($this->columns['pub_level']);
00760 unset($this->columns['estimated']);
00761 if(isset($this->query_options['folders_only'])) {
00762 $full_list= $folders;
00763 }
00764 else {
00765 $this->query_options['show_folders']= false;
00766 $this->query_options['parent_task']= NULL;
00767 $tasks= Task::getAll($this->query_options);
00768
00769 ### build hash of tasks appended to a folder ###
00770 $tasks_for_folder= array();
00771 foreach($tasks as $t) {
00772 $tasks_for_folder[$t->parent_task][]= $t;
00773 }
00774
00775 ### build full list ###
00776 $full_list = array();
00777 foreach($folders as $f) {
00778 $full_list[]=$f;
00779 #if(!$f->view_collapsed && isset($tasks_for_folder[$f->id])) {
00780 if(isset($tasks_for_folder[$f->id])) {
00781 if($f->view_collapsed) {
00782
00783 $f->num_subtasks= count($tasks_for_folder[$f->id]);
00784 }
00785 else {
00786 foreach($tasks_for_folder[$f->id] as $t) {
00787 $t->level= $f->level+1;
00788 $full_list[]= $t;
00789 }
00790 }
00791 unset($tasks_for_folder[$f->id]);
00792 }
00793 }
00794
00795 ### add tasks as project root / parent-task ###
00796 if($parent_task && isset($tasks_for_folder[$parent_task->id])) {
00797 foreach($tasks_for_folder[$parent_task->id] as $t) {
00798 $t->level= 0;
00799 $full_list[]= $t;
00800 }
00801
00802 }
00803 else if(!$parent_task && isset($tasks_for_folder[0])){
00804 foreach($tasks_for_folder[0] as $t) {
00805 $t->level= 0;
00806 $full_list[]= $t;
00807 }
00808 }
00809
00810 ### for files that have not be added to visible folders yet, try to increase count of visible parent-folders ###
00811 foreach($tasks_for_folder as $id=>$ar) {
00812
00813 if(isset($folders[$id])) {
00814 $folders[$id]->num_subtasks= count($tasks_for_folder[$id]);
00815 }
00816
00817 $parents= $ar[0]->getFolder();
00818
00819 foreach($parents as $pa) {
00820 if(isset($folders[$pa->id])) {
00821
00822 $folders[$pa->id]->num_subtasks= count($tasks_for_folder[$id]);
00823 }
00824 }
00825 }
00826 }
00827
00828
00829 ### filter empty folders with wrong status ###
00830 # (or all empty, if set)
00831
00832 $t_min= isset($this->query_options['status_min'])
00833 ? $this->query_options['status_min']
00834 : STATUS_NEW;
00835
00836 $t_max= isset($this->query_options['status_max'])
00837 ? $this->query_options['status_max']
00838 : STATUS_CLOSED;
00839
00840 {
00841 $new_list=array();
00842 $last_level=0;
00843 foreach(array_reverse($full_list) as $t) {
00844 if($t->category == TCATEGORY_FOLDER) {
00845
00846
00847 if($t->level < $last_level) {
00848 $new_list[]=$t;
00849 $last_level--;
00850 }
00851 else if($t->num_subtasks) {
00852 $new_list[]=$t;
00853 $last_level= $t->level;
00854 }
00855 else if(!$filter_empty_folders) {
00856 if($t->status <= $t_max && $t->status >= $t_min) {
00857 $new_list[]=$t;
00858 $last_level= $t->level;
00859 }
00860 }
00861 else {
00862 if($t->num_subtasks) {
00863 if($t->status <= $t_max && $t->status >= $t_min) {
00864 $new_list[]=$t;
00865 $last_level= $t->level;
00866 }
00867 }
00868 }
00869 }
00870 else {
00871 $new_list[]=$t;
00872 $last_level= $t->level;
00873 }
00874 }
00875 $tasks= array_reverse($new_list);
00876 }
00877 }
00878 $this->render_list(&$tasks);
00879 }
00880 }
00881
00882
00883
00884
00885 class ListBlockCol_TaskLabel extends ListBlockCol
00886 {
00887 public $id='label';
00888 public $key='label';
00889
00890
00891 public function __construct($args=NULL) {
00892 parent::__construct($args);
00893 $this->name=__('Label','Columnheader');
00894 }
00895
00896 function render_tr(&$obj, $style="")
00897 {
00898 measure_start('col_label');
00899 if(!isset($obj) || !$obj instanceof Task) {
00900 trigger_error("ListBlock->render_tr() called without valid object", E_USER_WARNING);
00901 return;
00902 }
00903 if($obj->category == TCATEGORY_FOLDER) {
00904 print "<td></td>";
00905 }
00906
00907 else if($str_label= $obj->getLabel()) {
00908 $class_label= $obj->label
00909 ? "class=label$obj->label"
00910 : '';
00911 print "<td class=label><span {$class_label}>$str_label</span></td>";
00912
00913 }
00914 else {
00915 print "<td></td>";
00916 }
00917
00918 measure_stop('col_label');
00919 }
00920 }
00921
00922
00923
00924 class ListBlockCol_Milestone extends ListBlockCol
00925 {
00926 public $name;
00927 public $id ='for_milestone';
00928 public $key ='for_milestone';
00929
00930 public function __construct($args=NULL) {
00931 parent::__construct($args);
00932 $this->name=__('Milestone');
00933 }
00934
00935 function render_tr(&$obj, $style='') {
00936 if(!isset($obj) || !$obj instanceof Task) {
00937 return;
00938 }
00939 $value="";
00940 if($obj->for_milestone) {
00941 if($milestone= Task::getVisibleById($obj->for_milestone)) {
00942 $value= $milestone->getLink();
00943 }
00944 }
00945 print "<td>$value</td>";
00946 }
00947 }
00948
00949
00950
00951 class ListBlockCol_TaskCreatedBy extends ListBlockCol
00952 {
00953 public $name;
00954 public $id='created_by';
00955 public $key='created_by';
00956
00957 public function __construct($args=NULL) {
00958 parent::__construct($args);
00959 $this->name=__('Created by');
00960 }
00961
00962 function render_tr(&$obj, $style="nowrap") {
00963 if(!isset($obj) || !$obj instanceof Task) {
00964 return;
00965 }
00966 $value="";
00967 if($obj->created_by) {
00968 if($person= Person::getVisibleById($obj->created_by)) {
00969 $value=$person->getLink();
00970 }
00971 }
00972 print "<td class=nowrap>$value</td>";
00973 }
00974 }
00975
00976
00977
00978 class ListBlockCol_TaskAssignedTo extends ListBlockCol
00979 {
00980 public $name;
00981
00982 public function __construct($args=NULL) {
00983 parent::__construct($args);
00984 $this->name=__('Assigned to');
00985 $this->id='assigned_to';
00986 }
00987
00988 function render_tr(&$obj, $style="nowrap") {
00989 if(!isset($obj) || !$obj instanceof Task) {
00990 return;
00991 }
00992 $value="";
00993 if($tps= $obj->getAssignedPersons()) {
00994 $sep="";
00995 foreach($tps as $tp) {
00996 $value.= $sep.$tp->getLink();
00997 $sep=", ";
00998 }
00999 }
01000 print "<td class=nowrap>$value</td>";
01001 }
01002 }
01003
01004
01005
01009 class ListBlockCol_TaskName extends ListBlockCol
01010 {
01011 public $use_short_names=false;
01012 public $name;
01013 public $indention=false;
01014 public $key='name';
01015 public $show_toggles= true;
01016
01017 public function __construct($args=NULL) {
01018 parent::__construct($args);
01019 $this->width='90%';
01020 $this->name=__('Task name');
01021 $this->id='name';
01022 }
01023
01024 function render_tr(&$task, $style="")
01025 {
01026 measure_start('col_taskname');
01027
01028 global $PH;
01029 if(!isset($task) || !is_object($task)) {
01030 trigger_error("ListBlock->render_tr() called without valid object", E_USER_WARNING);
01031 return;
01032 }
01033
01034 ### descriptions ###
01035 if($task->description && !$this->use_short_names) {
01036 $html_diz= asHtml($task->description);
01037 if(strlen($html_diz) > TEXT_LEN_PREVIEW) {
01038 $html_diz= substr($html_diz, 0, TEXT_LEN_PREVIEW);
01039 }
01040 $html_diz=preg_replace("/\r\n/",'',$html_diz);
01041 $description= '<span class=diz title="'.$html_diz.'">diz</span>';
01042 }
01043 else {
01044 $description='';
01045 }
01046
01047 ### comments ###
01048 $discussion='';
01049 if(($num_comments=$task->getNumComments()) && !$this->use_short_names) {
01050 $diz=sprintf(__("has %s comments"),$num_comments);
01051 $discussion= ' <span class=comments title="' .$diz. '">' . $num_comments . '</span>';
01052 }
01053
01054 ### attached files? ###
01055 $attachments='';
01056 {
01057 require_once(confGet('DIR_STREBER') . 'db/class_file.inc.php');
01058
01059 if($files= File::getAll(array(
01060 'parent_item'=> $task->id,
01061 'project'=> $task->project,
01062 ))) {
01063 $attachments='<img title="' . sprintf(__('Task has %s attachments'), count($files)) . '" src="' . getThemeFile("items/item_attachment.png"). '">';
01064 }
01065 }
01066
01067
01068
01069 ### task with zero-id is project-root ###
01070 if(!$task->id) {
01071 $link=$PH->getLink('projView',"...project...",array('prj'=>$task->project));
01072 echo '<td>'.$link."</td>";
01073 }
01074
01075 ### name ###
01076 else {
01077 $name= $this->use_short_names
01078 ? $task->getShort()
01079 : $task->name;
01080
01081
01082 if(!$name) {
01083 $name=__("- no name -","in task lists");
01084 }
01085
01086 $link= $PH->getLink('taskView',$name,array('tsk'=>$task->id));
01087
01088
01089 if($this->indention) {
01090 $toggle='';
01091 if($this->show_toggles) {
01092 if($task->category == TCATEGORY_FOLDER) {
01093 if($task->view_collapsed) {
01094 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_closed.gif") . '">',array('tsk'=>$task->id),'folder_collapsed', true);
01095
01096 ### number of subtasks with folded ###
01097 $description='<span class=diz title="'.__("number of subtasks").'">('.$task->getNumSubtasks().')</span>';
01098 }
01099 else {
01100 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_open.gif"). '">',array('tsk'=>$task->id),'folder_open', true);
01101 }
01102 }
01103 }
01104 $html_indention='';
01105 if($task->level) {
01106 $no_folder= ($task->category == TCATEGORY_FOLDER)
01107 ? 0
01108 : 1;
01109 $html_indention= "style='padding-left:".(1.2 * ($no_folder+intval($task->level)))."em;'";
01110 }
01111
01112 echo "<td $html_indention>{$toggle}$link{$description}{$discussion}{$attachments}</td>";
01113
01114 }
01115 else {
01116 echo "<td>{$link}{$description}{$discussion}{$attachments}</td>";
01117 }
01118 }
01119 measure_stop('col_taskname');
01120 }
01121 }
01122
01123
01124
01128 class ListBlockCol_TaskAsDocu extends ListBlockCol
01129 {
01130 public $name;
01131 public $key='name';
01132 public $show_toggles= true;
01133
01134 public function __construct($args=NULL) {
01135 parent::__construct($args);
01136 $this->width='90%';
01137 $this->name=__('Page name');
01138 $this->id='name';
01139 }
01140
01141 function render_tr(&$task, $style="")
01142 {
01143 global $PH;
01144 if(!isset($task) || !is_object($task)) {
01145 trigger_error("ListBlock->render_tr() called without valid object", E_USER_WARNING);
01146 return;
01147 }
01148
01149 $link= $PH->getLink('taskViewAsDocu',$task->name,array('tsk'=>$task->id));
01150
01151 $toggle='';
01152 if($task->category == TCATEGORY_FOLDER) {
01153 if($task->view_collapsed) {
01154 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_closed.gif") . '">',array('tsk'=>$task->id),'folder_collapsed', true);
01155 }
01156 else {
01157 $toggle=$PH->getLink('taskToggleViewCollapsed','<img src="' . getThemeFile("img/toggle_folder_open.gif"). '">',array('tsk'=>$task->id),'folder_open', true);
01158 }
01159 }
01160
01161 $html_indention='';
01162 if($task->level) {
01163 $no_folder= ($task->category == TCATEGORY_FOLDER)
01164 ? 0
01165 : 1;
01166 $html_indention= "style='padding-left:".(1.2 * ($no_folder+intval($task->level)))."em;'";
01167 }
01168 echo "<td $html_indention>{$toggle}$link</td>";
01169 }
01170 }
01171
01172
01173
01174 class ListBlockCol_TaskSumEfforts extends ListBlockCol
01175 {
01176
01177 public function __construct($args=NULL) {
01178 parent::__construct($args);
01179 $this->name=__('Efforts');
01180 $this->id='efforts';
01181 $this->tooltip=__("Sum of all booked efforts (including subtasks)");
01182 }
01183
01184
01185 function render_tr(&$obj, $style="nowrap")
01186 {
01187 global $PH;
01188 if(!isset($obj) || !$obj instanceof Task) {
01189 return;
01190 }
01191 $sum=$obj->getSumEfforts();
01192
01193 $str= $PH->getLink('taskViewEfforts',round($sum/60/60,1)."h", array('task'=>$obj->id));
01194
01195 print "<td class=nowrap title='" .__("Effort in hours") . "'>$str</td>";
01196 }
01197 }
01198
01199 class ListBlockCol_TaskRelationEfforts extends ListBlockCol
01200 {
01201 public function __construct($args=NULL) {
01202 parent::__construct($args);
01203 $this->name=__('Estimated/Booked (Diff.)');
01204 $this->id='efforts_estimated';
01205 $this->tooltip=__("Relation between estimated time and booked efforts");
01206 }
01207
01208
01209 function render_tr(&$obj, $style="nowrap")
01210 {
01211 global $PH;
01212 if(!isset($obj) || !$obj instanceof Task) {
01213 return;
01214 }
01215 $diff_str = '';
01216 $estimated_str = '';
01217
01218 $sum = $obj->getSumTaskEfforts();
01219
01220 $estimated = $obj->estimated;
01221 $estimated_max = $obj->estimated_max;
01222
01223 if($estimated_max){
01224 $estimated_str = round($estimated_max/60/60,1) . "h";
01225 $diff = $estimated_max - $sum;
01226 }
01227 else{
01228 $estimated_str = round($estimated/60/60,1) . "h";
01229 $diff = $estimated - $sum;
01230 }
01231
01232 if($diff){
01233 $diff_str = "(" .round($diff/60/60,1). "h)";
01234 }
01235
01236 $str = $PH->getLink('taskViewEfforts', $estimated_str . " / " . round($sum/60/60,1) . "h {$diff_str}", array('task'=>$obj->id));
01237 $percent = __('Completion:') . " " . $obj->completion . "%";
01238
01239 print "<td class=nowrap title='" .__("Relation between estimated time and booked efforts") . "'>$str<br><span class='sub who'>$percent</span></td>";
01240 }
01241 }
01242
01243
01244 class ListBlockCol_DaysLeft extends ListBlockCol
01245 {
01246 public $key='planned_start';
01247
01248 public function __construct($args=NULL) {
01249 parent::__construct($args);
01250 $this->tooltip=__("Days until planned start");
01251 $this->name=__("Due","column header, days until planned start");
01252 }
01253
01254 function render_tr(&$obj, $style="")
01255 {
01256 measure_start('col_timedue');
01257 $due_str=$obj->planned_start;
01258
01259 if($due_str == "0000-00-00" || $due_str == "0000-00-00 00:00:00") {
01260 print "<td></td>";
01261 }
01262 else {
01263 $due_days= floor( (strToGMTime($obj->planned_start) - time())/24/60/60)+1;
01264 if($due_days==0) {
01265 $value="Today";
01266 $title="title='Hurry up!'";
01267 $class='';
01268 }
01269 else {
01270 $class='';
01271 if($due_days<0) {
01272 $class='overDue';
01273 }
01274 $value="$due_days<span class='entity'>D</span>";
01275 if(!$obj->planned_end || $obj->planned_end !='0000-00-00' || '0000-00-00 00:00:00') {
01276 $title="title='".renderDate($obj->planned_end)." - ".renderDate($obj->planned_start). "'";
01277 $value.="+";
01278 }
01279 else {
01280 $title="'".sprintf(__("planned for %s","a certain date"), renderTimestamp($obj->planned_start))."'";
01281 }
01282 }
01283 print "<td class='timeDue $class' $title>$value</td>";
01284 }
01285 measure_stop('col_timedue');
01286
01287 }
01288 }
01289
01290
01291 class ListBlockCol_EstimatedComplete extends ListBlockCol
01292 {
01293
01294 public function __construct($args=NULL) {
01295 parent::__construct($args);
01296 $this->name=__('Est/Compl');
01297 $this->id='estimate_complete';
01298 $this->tooltip=__("Estimated time / completed");
01299 }
01300
01301
01302 function render_tr(&$obj, $style="nowrap") {
01303 if(!isset($obj) || !$obj instanceof Task) {
01304 return;
01305 }
01306
01307 $str= renderEstimationGraph($obj->estimated, $obj->estimated_max, $obj->completion );
01308
01309 print "<td>$str</td>";
01310 }
01311 }
01312
01313
01314
01315
01316
01317
01318 class ListBlockCol_TasknameWithFolder extends ListBlockCol
01319 {
01320