summaryrefslogtreecommitdiff
path: root/scripts/index.php
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/index.php')
-rw-r--r--scripts/index.php534
1 files changed, 534 insertions, 0 deletions
diff --git a/scripts/index.php b/scripts/index.php
new file mode 100644
index 0000000..28e194e
--- /dev/null
+++ b/scripts/index.php
@@ -0,0 +1,534 @@
+<?php
+
+/*
+ This script sets up and shows the tasklist page.
+ It is for historical reason called index.php, because it was also the frontpage.
+ But now there can be a different pagetype set up as frontpage in Flyspray.
+*/
+
+
+if (!defined('IN_FS')) {
+ die('Do not access this file directly.');
+}
+
+// Need to get function ConvertSeconds
+require_once(BASEDIR . '/includes/class.effort.php');
+
+if (!$user->can_select_project($proj->id)) {
+ $proj = new Project(0);
+}
+
+$perpage = '50';
+if (isset($user->infos['tasks_perpage']) && $user->infos['tasks_perpage'] > 0) {
+ $perpage = $user->infos['tasks_perpage'];
+}
+
+$pagenum = Get::num('pagenum', 1);
+if ($pagenum < 1) {
+ $pagenum = 1;
+}
+$offset = $perpage * ($pagenum - 1);
+
+// Get the visibility state of all columns
+$visible = explode(' ', trim($proj->id ? $proj->prefs['visible_columns'] : $fs->prefs['visible_columns']));
+if (!is_array($visible) || !count($visible) || !$visible[0]) {
+ $visible = array('id');
+}
+
+// Remove columns the user is not allowed to see
+if (in_array('estimated_effort', $visible) && !$user->perms('view_estimated_effort')) {
+ unset($visible[array_search('estimated_effort', $visible)]);
+}
+if (in_array('effort', $visible) && !$user->perms('view_current_effort_done')) {
+ unset($visible[array_search('effort', $visible)]);
+}
+
+# for csv export no paging limits
+if (Get::has('export_list')) {
+ $offset = -1;
+ $perpage = -1;
+}
+
+list($tasks, $id_list, $totalcount, $forbiddencount) = Backend::get_task_list($_GET, $visible, $offset, $perpage);
+
+if (Get::has('export_list')) {
+ export_task_list();
+}
+
+$page->uses('tasks', 'offset', 'perpage', 'pagenum', 'visible');
+
+// List of task IDs for next/previous links
+# Mmh the result is persistent in $_SESSION a bit for the length of each user session and can lead to a DOS quite fast on bigger installs?
+# Do we really need prev-next on task details view or can we find an alternative solution?
+# And using the $_SESSION for that is currently not working correct if someone uses 2 browser tabs for 2 different projects.
+$_SESSION['tasklist'] = $id_list;
+
+$page->assign('total', $totalcount);
+$page->assign('forbiddencount', $forbiddencount);
+
+// Send user variables to the template
+
+$result = $db->query('SELECT DISTINCT u.user_id, u.user_name, u.real_name, g.group_name, g.project_id
+ FROM {users} u
+ LEFT JOIN {users_in_groups} uig ON u.user_id = uig.user_id
+ LEFT JOIN {groups} g ON g.group_id = uig.group_id
+ WHERE (g.show_as_assignees = 1 OR g.is_admin = 1)
+ AND (g.project_id = 0 OR g.project_id = ?) AND u.account_enabled = 1
+ ORDER BY g.project_id ASC, g.group_name ASC, u.user_name ASC', ($proj->id || -1)); // FIXME: -1 is a hack. when $proj->id is 0 the query fails
+$userlist = array();
+while ($row = $db->fetchRow($result)) {
+ $userlist[$row['group_name']][] = array(0 => $row['user_id'],
+ 1 => sprintf('%s (%s)', $row['user_name'], $row['real_name']));
+}
+
+$page->assign('userlist', $userlist);
+
+/**
+ * tpl function that Displays a header cell for report list
+ */
+function tpl_list_heading($colname, $format = "<th%s>%s</th>")
+{
+ global $proj, $page;
+ $imgbase = '<img src="%s" alt="%s" />';
+ $class = $colname;
+ $html = eL($colname);
+/*
+ if ($colname == 'comments' || $colname == 'attachments') {
+ $html = sprintf($imgbase, $page->get_image(substr($colname, 0, -1)), $html);
+ }
+*/
+ if ($colname == 'attachments') {
+ $html='<i class="fa fa-paperclip fa-lg" title="'.$html.'"></i>';
+ }
+ if ($colname == 'comments') {
+ $html='<i class="fa fa-comments fa-lg" title="'.$html.'"></i>';
+ }
+ if ($colname == 'votes') {
+ $html='<i class="fa fa-star-o fa-lg" title="'.$html.'"></i>';
+ }
+
+ if (Get::val('order') == $colname) {
+ $class .= ' orderby';
+ $sort1 = Get::safe('sort', 'desc') == 'desc' ? 'asc' : 'desc';
+ $sort2 = Get::safe('sort2', 'desc');
+ $order2 = Get::safe('order2');
+ $html .= '&nbsp;&nbsp;'.sprintf($imgbase, $page->get_image(Get::val('sort')), Get::safe('sort'));
+ }
+ else {
+ $sort1 = 'desc';
+ if (in_array($colname,
+ array('project', 'tasktype', 'category', 'openedby', 'assignedto')))
+ {
+ $sort1 = 'asc';
+ }
+ $sort2 = Get::safe('sort', 'desc');
+ $order2 = Get::safe('order');
+ }
+
+
+ $new_order = array('order' => $colname, 'sort' => $sort1, 'order2' => $order2, 'sort2' => $sort2);
+ # unneeded params from $_GET for the sort links
+ $params=array_merge($_GET, $new_order);
+ unset($params['do']);
+ unset($params['project']);
+ unset($params['switch']);
+ $html = sprintf('<a title="%s" href="%s">%s</a>',
+ eL('sortthiscolumn'), Filters::noXSS(createURL('tasklist', $proj->id, null, $params )), $html);
+
+ return sprintf($format, ' class="'.$class.'"', $html);
+}
+
+
+/**
+ * tpl function that draws a cell
+ */
+function tpl_draw_cell($task, $colname, $format = "<td class='%s'>%s</td>") {
+ global $fs, $db, $proj, $page, $user;
+
+ $indexes = array (
+ 'id' => 'task_id',
+ 'project' => 'project_title',
+ 'tasktype' => 'task_type',
+ 'tasktypename'=> 'tasktype_name',
+ 'category' => 'category_name',
+ 'severity' => '',
+ 'priority' => '',
+ 'summary' => 'item_summary',
+ 'dateopened' => 'date_opened',
+ 'status' => 'status_name',
+ 'openedby' => 'opened_by',
+ 'openedbyname'=> 'opened_by_name',
+ 'assignedto' => 'assigned_to_name',
+ 'lastedit' => 'max_date',
+ 'editedby' => 'last_edited_by',
+ 'reportedin' => 'product_version_name',
+ 'dueversion' => 'closedby_version_name',
+ 'duedate' => 'due_date',
+ 'comments' => 'num_comments',
+ 'votes' => 'num_votes',
+ 'attachments'=> 'num_attachments',
+ 'dateclosed' => 'date_closed',
+ 'closedby' => 'closed_by',
+ 'commentedby'=> 'commented_by',
+ 'progress' => '',
+ 'os' => 'os_name',
+ 'private' => 'mark_private',
+ 'parent' => 'supertask_id',
+ 'estimatedeffort' => 'estimated_effort',
+ );
+
+ //must be an array , must contain elements and be alphanumeric (permitted "_")
+ if(!is_array($task) || empty($task) || preg_match('![^A-Za-z0-9_]!', $colname)) {
+ //run away..
+ return '';
+ }
+ $class= 'task_'.$colname;
+
+ switch ($colname) {
+ case 'id':
+ $value = tpl_tasklink($task, $task['task_id']);
+ break;
+ case 'summary':
+ $value = tpl_tasklink($task, utf8_substr($task['item_summary'], 0, 55));
+ if (utf8_strlen($task['item_summary']) > 55) {
+ $value .= '...';
+ }
+
+ if($task['tagids']!=''){
+ #$tags=explode(',', $task['tags']);
+ $tagids=explode(',', $task['tagids']);
+ #$tagclass=explode(',', $task['tagclass']);
+ $tgs='';
+ for($i=0;$i< count($tagids); $i++){
+ $tgs.=tpl_tag($tagids[$i]);
+ }
+ $value.=$tgs;
+ }
+ break;
+
+ case 'tasktype':
+ $value = htmlspecialchars($task['tasktype_name'], ENT_QUOTES, 'utf-8');
+ $class.=' typ'.$task['task_type'];
+ break;
+
+ case 'severity':
+ $value = $task['task_severity']==0 ? '' : $fs->severities[$task['task_severity']];
+ $class.=' sev'.$task['task_severity'];
+ break;
+
+ case 'priority':
+ $value = $task['task_priority']==0 ? '' : $fs->priorities[$task['task_priority']];
+ $class.=' pri'.$task['task_priority'];
+ break;
+
+ case 'attachments':
+ case 'comments':
+ case 'votes':
+ $value = $task[$indexes[$colname]]>0 ? $task[$indexes[$colname]]:'';
+ break;
+
+ case 'lastedit':
+ case 'duedate':
+ case 'dateopened':
+ case 'dateclosed':
+ $value = formatDate($task[$indexes[$colname]]);
+ break;
+
+ case 'status':
+ if ($task['is_closed']) {
+ $value = eL('closed');
+ } else {
+ $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8');
+ }
+ $class.=' sta'.$task['item_status'];
+ break;
+
+ case 'progress':
+ $value = tpl_img($page->get_image('percent-' . $task['percent_complete'], false),
+ $task['percent_complete'] . '%');
+ break;
+
+ case 'assignedto':
+ # group_concat-ed for mysql/pgsql
+ #$value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8');
+ $value='';
+ $anames=explode(',',$task[$indexes[$colname]]);
+ $aids=explode(',',$task['assignedids']);
+ $aimages=explode(',',$task['assigned_image']);
+ for($a=0;$a < count($anames);$a++){
+ if($aids[$a]){
+ # deactivated: avatars looks too ugly in the tasklist, user's name needs to be visible on a first look here, without needed mouse hovering..
+ #if ($fs->prefs['enable_avatars']==1 && $aimages[$a]){
+ # $value.=tpl_userlinkavatar($aids[$a],30);
+ #} else{
+ $value.=tpl_userlink($aids[$a]);
+ #}
+ #$value.='<a href="'.$aids[$a].'">'.htmlspecialchars($anames[$a], ENT_QUOTES, 'utf-8').'</a>';
+ }
+ }
+
+ # fallback for DBs we haven't written sql string aggregation yet (currently with group_concat() mysql and array_agg() postgresql)
+ if( ('postgres' != $db->dblink->dataProvider) && ('mysql' != $db->dblink->dataProvider) && ($task['num_assigned'] > 1)) {
+ $value .= ', +' . ($task['num_assigned'] - 1);
+ }
+ break;
+
+ case 'private':
+ $value = $task[$indexes[$colname]] ? L('yes') : L('no');
+ break;
+
+ case 'commentedby':
+ case 'openedby':
+ case 'editedby':
+ case 'closedby':
+ $value = '';
+ # a bit expensive! tpl_userlinkavatar() an additional sql query for each new user in the output table
+ # at least tpl_userlink() uses a $cache array so query for repeated users
+ if ($task[$indexes[$colname]] > 0) {
+ # deactivated: avatars looks too ugly in the tasklist, user's name needs to be visible on a first look here, without needed mouse hovering..
+ #if ($fs->prefs['enable_avatars']==1){
+ # $value = tpl_userlinkavatar($task[$indexes[$colname]],30);
+ #} else{
+ $value = tpl_userlink($task[$indexes[$colname]]);
+ #}
+ }
+ break;
+
+ case 'parent':
+ $value = '';
+ if ($task['supertask_id'] > 0) {
+ $value = tpl_tasklink($task, $task['supertask_id']);
+ }
+ break;
+
+ case 'estimatedeffort':
+ $value = '';
+ if ($user->perms('view_estimated_effort')) {
+ if ($task['estimated_effort'] > 0){
+ $value = effort::secondsToString($task['estimated_effort'], $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']);
+ }
+ }
+ break;
+
+ case 'effort':
+ $value = '';
+ if ($user->perms('view_current_effort_done')) {
+ if ($task['effort'] > 0){
+ $value = effort::secondsToString($task['effort'], $proj->prefs['hours_per_manday'], $proj->prefs['current_effort_done_format']);
+ }
+ }
+ break;
+
+ default:
+ $value = '';
+ // $colname here is NOT column name in database but a name that can appear
+ // both in a projects visible fields and as a key in language translation
+ // file, which is also used to draw a localized heading. Column names in
+ // database customarily use _ t to separate words, translation file entries
+ // instead do not and can be also be quite different. If you do see an empty
+ // value when you expected something, check your usage, what visible fields
+ // in database actually constains, and maybe add a mapping from $colname to
+ // to the database column name to array $indexes at the beginning of this
+ // function. Note that inconsistencies between $colname, database column
+ // name, translation entry key and name in visible fields do occur sometimes
+ // during development phase.
+ if (array_key_exists($colname, $indexes)) {
+ $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8');
+ }
+ break;
+ }
+ return sprintf($format, $class, $value);
+}
+
+$sort;
+$orderby;
+
+/**
+ *
+ * comparison function used by export_task_list
+ *
+ */
+function do_cmp($a, $b)
+{
+ global $sort,$orderby;
+
+ if ($a[ $orderby ] == $b[ $orderby ]) { return 0; }
+
+ if ($sort == 'asc')
+ return ($a[ $orderby ] < $b[ $orderby ]) ? -1 : 1;
+ else
+ return ($a[ $orderby ] > $b[ $orderby ]) ? -1 : 1;
+}
+
+/**
+* workaround fputcsv() bug https://bugs.php.net/bug.php?id=43225
+*/
+function my_fputcsv($handle, $fields)
+{
+ $out = array();
+
+ foreach ($fields as $field) {
+ if (empty($field)) {
+ $out[] = '';
+ }
+ elseif (preg_match('/^\d+(\.\d+)?$/', $field)) {
+ $out[] = $field;
+ }
+ else {
+ $out[] = '"' . preg_replace('/"/', '""', $field) . '"';
+ }
+ }
+
+ return fwrite($handle, implode(',', $out) . "\n");
+}
+
+
+/**
+ * Export the tasks as a .csv file
+ * Currently only a fixed list of task fields
+ */
+function export_task_list()
+{
+ global $tasks, $fs, $user, $sort, $orderby, $proj;
+
+ if (!is_array($tasks)){
+ return;
+ }
+
+ # TODO enforcing user permissions on allowed fields
+ # TODO Flyspray 1.1 or later: selected fields by user request, saved user settings, tasklist settings or project defined list which fields should appear in an export
+ # TODO Flyspray 1.1 or later: export in .ods open document spreadsheet, .xml ....
+ $indexes = array (
+ 'id' => 'task_id',
+ 'project' => 'project_title',
+ 'tasktype' => 'task_type',
+ 'category' => 'category_name',
+ 'severity' => 'task_severity',
+ 'priority' => 'task_priority',
+ 'summary' => 'item_summary',
+ 'dateopened' => 'date_opened',
+ 'status' => 'status_name',
+ 'openedby' => 'opened_by_name',
+ 'assignedto' => 'assigned_to_name',
+ 'lastedit' => 'max_date',
+ 'reportedin' => 'product_version',
+ 'dueversion' => 'closedby_version',
+ 'duedate' => 'due_date',
+ 'comments' => 'num_comments',
+ 'votes' => 'num_votes',
+ 'attachments'=> 'num_attachments',
+ 'dateclosed' => 'date_closed',
+ 'progress' => 'percent_complete',
+ 'os' => 'os_name',
+ 'private' => 'mark_private',
+ 'supertask' => 'supertask_id',
+ 'detailed_desc'=>'detailed_desc',
+ );
+
+
+ # we can put this info also in the filename ...
+ #$projectinfo = array('Project ', $tasks[0]['project_title'], date("H:i:s d-m-Y") );
+
+ // sort the tasks into the order selected by the user. Set
+ // global vars for use by sort comparison function
+
+ $sort = Get::safe('sort','desc') == 'desc' ? 'desc' : 'asc';
+ $field = Get::safe('order', 'id');
+
+ if ($field == '') $field = 'id';
+ $orderby = $indexes[ $field ];
+
+ usort($tasks, "do_cmp");
+
+ $outfile = str_replace(' ', '_', $proj->prefs['project_title']).'_'.date("Y-m-d").'.csv';
+
+ #header('Content-Type: application/csv');
+ header('Content-Type: text/csv');
+ header('Content-Disposition: attachment; filename='.$outfile);
+ header('Content-Transfer-Encoding: text');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate');
+ #header('Pragma: public');
+ #header('Content-Length: '.strlen($result)); # unknown at this time..
+ ob_clean();
+ flush();
+
+ $output = fopen('php://output', 'w');
+ $headings= array(
+ 'ID',
+ 'Category',
+ 'Task Type',
+ 'Severity',
+ 'Summary',
+ 'Status',
+ 'Progress',
+ 'date_opened',
+ 'date_closed',
+ 'due_date',
+ 'supertask_id'
+ );
+ if($user->perms('view_estimated_effort') && $proj->id>0 && $proj->prefs['use_effort_tracking']){
+ $headings[]='Estimated Effort';
+ }
+ $headings[]='Description';
+ //if($user->perms('view_current_effort_done') && $proj->id>0 && $proj->prefs['use_effort_tracking']){ $headings[]='Done Effort'; }
+
+ #fputcsv($output, $headings);
+ my_fputcsv($output, $headings); # fixes 'SYLK' FS#2123 Excel problem
+ foreach ($tasks as $task) {
+ $row = array(
+ $task['task_id'],
+ $task['category_name'],
+ $task['task_type'],
+ $fs->severities[ $task['task_severity'] ],
+ $task['item_summary'],
+ $task['status_name'],
+ $task['percent_complete'],
+ $task['date_opened'],
+ $task['date_closed'],
+ $task['due_date'],
+ $task['supertask_id']
+ );
+ if( $user->perms('view_estimated_effort') && $proj->id>0 && $proj->prefs['use_effort_tracking']){
+ $row[]=$task['estimated_effort'];
+ }
+ $row[]=$task['detailed_desc'];
+ //if( $user->perms('view_current_effort_done') && $proj->id>0 && $proj->prefs['use_effort_tracking']){ $row=$task['effort']; }
+
+ my_fputcsv($output, $row); # fputcsv() is buggy
+ }
+ fclose($output);
+ exit();
+}
+
+// Javascript replacement
+if (Get::val('toggleadvanced')) {
+ $advanced_search = intval(!Req::val('advancedsearch'));
+ Flyspray::setCookie('advancedsearch', $advanced_search, time()+60*60*24*30);
+ $_COOKIE['advancedsearch'] = $advanced_search;
+}
+
+/**
+ * Update check
+ */
+if(Get::has('hideupdatemsg')) {
+ unset($_SESSION['latest_version']);
+} else if ($conf['general']['update_check']
+ && $user->perms('is_admin')
+ && $fs->prefs['last_update_check'] < time()-60*60*24*3) {
+ if (!isset($_SESSION['latest_version'])) {
+ $latest = Flyspray::remote_request('http://www.flyspray.org/version.txt', GET_CONTENTS);
+ # if for some silly reason we get an empty response, we use the actual version
+ $_SESSION['latest_version'] = empty($latest) ? $fs->version : $latest ;
+ $db->query('UPDATE {prefs} SET pref_value = ? WHERE pref_name = ?', array(time(), 'last_update_check'));
+ }
+}
+if (isset($_SESSION['latest_version']) && version_compare($fs->version, $_SESSION['latest_version'] , '<') ) {
+ $page->assign('updatemsg', true);
+}
+
+
+$page->setTitle($fs->prefs['page_title'] . $proj->prefs['project_title'] . ': ' . L('tasklist'));
+$page->pushTpl('index.tpl');
+
+?>