lists/list_tasks.inc.php

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         /*$this->add_col( new listBlockColDate(array(
00128             'key'=>'date_start',
00129             'name'=>__('Started'),
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         /*if($this->filters) {
00334             echo '<span class=filters>Filters:';
00335             foreach($this->filters as $f) {
00336                 echo '<span class=filter>'.$f->render().'</span> ';
00337             }
00338             echo '</span>';
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>&gt;</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 '&nbsp;';   # dummy content to keep floating
00468                         }
00469 
00470 
00471                         echo '</div>';
00472                     }
00473 
00474                     ### block description ###
00475                     {
00476                         echo '<div class=description>&nbsp;';
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 '&nbsp;</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 '&nbsp;</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