function tpl_list_heading($colname, $format = "<th%s>%s</th>") { global $proj, $page; $imgbase = '<img src="%s" alt="%s" />'; $class = ''; $html = eL($colname); if ($colname == 'comments' || $colname == 'attachments') { $html = sprintf($imgbase, $page->get_image(substr($colname, 0, -1)), $html); } if (Get::val('order') == $colname) { $class = ' class="orderby"'; $sort1 = Get::safe('sort', 'desc') == 'desc' ? 'asc' : 'desc'; $sort2 = Get::safe('sort2', 'desc'); $order2 = Get::safe('order2'); $html .= ' ' . 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); $html = sprintf('<a title="%s" href="%s">%s</a>', eL('sortthiscolumn'), Filters::noXSS(CreateURL('index', $proj->id, null, array_merge($_GET, $new_order))), $html); return sprintf($format, $class, $html); }
function beforeCache(&$input, $plugins) { $input = preg_replace('|[[:space:]]+[[:alpha:]]+://[^<>[:space:]]+[[:alnum:]/]|', '<a href="\\0">\\0</a>', $input); $input = preg_replace('/[a-zA-Z0-9._-]+@[a-zA-Z0-9-.]+\\.[a-zA-Z]{2,5}/', '<a href="mailto:\\0">\\0</a>', $input); if (!in_array('WikiSyntax', $plugins)) { $input = '<p>' . nl2br(Filters::noXSS($input)) . '</p>'; } }
function imageAttachment($matches) { global $db, $baseurl, $user; // we'll not blindly make images out of all attachments $ext = substr($matches[1], -3); if (!in_array($ext, array('png', 'jpg', 'gif'))) { return $matches[0]; } $att = $db->x->getRow('SELECT * FROM {attachments} WHERE orig_name = ?', null, $matches[1]); $task = Flyspray::GetTaskDetails($att['task_id']); if ($att && $user->can_view_task($task)) { return sprintf('<img src="%s" alt="%s" />', Filters::noXSS($baseurl . '?getfile=' . $att['attachment_id']), Filters::noXSS($att['orig_name'])); } else { return $matches[0]; } }
/** * Query * * @param mixed $sql * @param mixed $inputarr * @param mixed $numrows * @param mixed $offset * @access public * @return void */ public function Query($sql, $inputarr = false, $numrows = -1, $offset = -1) { // auto add $dbprefix where we have {table} $sql = $this->_add_prefix($sql); // remove conversions for MySQL if (strcasecmp($this->dbtype, 'pgsql') != 0) { $sql = str_replace('::int', '', $sql); $sql = str_replace('::text', '', $sql); } $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; if ($numrows >= 0 or $offset >= 0) { /* adodb drivers are inconsisent with the casting of $numrows and $offset so WE * cast to integer here anyway */ $result = $this->dblink->SelectLimit($sql, (int) $numrows, (int) $offset, $inputarr); } else { $result = $this->dblink->Execute($sql, $inputarr); } if (!$result) { if (function_exists("debug_backtrace") && defined('DEBUG_SQL')) { echo "<pre style='text-align: left;'>"; var_dump(debug_backtrace()); echo "</pre>"; } $query_params = ''; if (is_array($inputarr) && count($inputarr)) { $query_params = implode(',', array_map(array('Filters', 'noXSS'), $inputarr)); } die(sprintf("Query {%s} with params {%s} Failed! (%s)", Filters::noXSS($sql), $query_params, Filters::noXSS($this->dblink->ErrorMsg()))); } return $result; }
/** * XXX: A mess,remove my in 1.0 */ function event_description($history) { $return = ''; global $fs, $baseurl, $details, $page; $translate = array('item_summary' => 'summary', 'project_id' => 'attachedtoproject', 'mark_private' => 'visibility'); // if soemthing gets double escaped, add it here. $noescape = array('new_value', 'old_value'); foreach ($history as $key => $value) { if (!in_array($key, $noescape)) { $history[$key] = Filters::noXSS($value); } } $new_value = $history['new_value']; $old_value = $history['old_value']; switch ($history['event_type']) { case '3': //Field changed if (!$new_value && !$old_value) { $return .= eL('taskedited'); break; } $field = $history['field_changed']; switch ($field) { case 'project_id': $old_value = $history[$field . '1']; $new_value = $history[$field . '2']; case 'item_summary': $field = eL($translate[$field]); $old_value = Filters::noXSS($old_value); $new_value = Filters::noXSS($new_value); break; case 'percent_complete': $field = eL('percentcomplete'); $old_value .= '%'; $new_value .= '%'; break; case 'mark_private': $field = eL($translate[$field]); $old_value = $old_value ? eL('private') : eL('public'); $new_value = $new_value ? eL('private') : eL('public'); break; case 'detailed_desc': $field = sprintf("<a href=\"javascript:getHistory('%d', '%s', 'history', '%d');\n showTabById('history', true);\">%s</a>", $history['task_id'], $baseurl, $history['history_id'], eL('details')); if (!empty($details)) { $details_previous = $page->text->render($old_value); $details_new = $page->text->render($new_value); } $old_value = ''; $new_value = ''; break; } if (is_numeric($field)) { $field = $history['field_name']; $f = new Field($history); if ($f->prefs['field_type'] == FIELD_LIST) { $t = $history['list_type'] == LIST_CATEGORY ? 'c' : 'l'; $new = array('field' . $f->id => $history['new_value_' . $t], 'field' . $f->id . '_name' => $history['new_value_' . $t]); $old = array('field' . $f->id => $history['old_value_' . $t], 'field' . $f->id . '_name' => $history['old_value_' . $t]); $new_value = $f->view($new); $old_value = $f->view($old); } else { if ($f->prefs['field_type'] == FIELD_USER) { $new_value = tpl_userlink($history['new_value']); $old_value = tpl_userlink($history['old_value']); } else { if ($f->prefs['field_type'] == FIELD_DATE) { $new_value = $f->view(array('field' . $f->id => $history['new_value'])); $old_value = $f->view(array('field' . $f->id => $history['old_value'])); } } } } $return .= eL('fieldchanged') . ": {$field}"; if ($old_value || $new_value) { $return .= " ({$old_value} → {$new_value})"; } break; case '1': //Task opened $return .= eL('taskopened'); break; case '2': //Task closed $return .= eL('taskclosed'); $return .= " ({$history['resolution_name']}"; if (!empty($old_value)) { $return .= ': ' . $page->text->render($old_value, true); } $return .= ')'; break; case '4': //Comment added $return .= sprintf('<a href="%s#comment%d">%s</a>', Filters::noXSS(CreateUrl(array('details', 'task' . $history['task_id']))), $history['new_value'], eL('commentadded')); break; case '5': //Comment edited $return .= sprintf("<a href=\"javascript:getHistory('%d', '%s', 'history', '%d');\">%s</a>", $history['task_id'], $baseurl, $history['history_id'], eL('commentedited')); if ($history['c_date_added']) { $return .= sprintf(' (%s %s - %s)', eL('commentby'), tpl_userlink($history['c_user_id']), formatDate($history['c_date_added'], true)); } if ($details) { $details_previous = $page->text->render($old_value); $details_new = $page->text->render($new_value); } break; case '6': //Comment deleted $return .= sprintf("<a href=\"javascript:getHistory('%d', '%s', 'history','%d');\">%s</a>", $history['task_id'], $baseurl, $history['history_id'], eL('commentdeleted')); if (!empty($new_value) && !empty($history['field_changed'])) { $return .= sprintf('(%s %s - %s)', eL('commentby'), tpl_userlink($new_value), formatDate($history['field_changed'], true)); } if (!empty($details)) { $details_previous = $page->text->render($old_value); $details_new = ''; } break; case '7': //Attachment added $return .= eL('attachmentadded'); if ($history['orig_name']) { $return .= sprintf(': <a href="%s?getfile=%d">%s</a>', $baseurl, $new_value, $history['orig_name']); } else { if ($history['old_value']) { $return .= ': ' . $history['old_value']; } } break; case '8': //Attachment deleted $return .= eL('attachmentdeleted') . ': ' . Filters::noXSS($new_value); break; case '9': //Notification added $return .= eL('notificationadded') . ': ' . tpl_userlink($new_value); break; case '10': //Notification deleted $return .= eL('notificationdeleted') . ': ' . tpl_userlink($new_value); break; case '11': //Related task added $return .= eL('relatedadded') . ': ' . tpl_tasklink($new_value); break; case '12': //Related task deleted $return .= eL('relateddeleted') . ': ' . tpl_tasklink($new_value); break; case '13': //Task reopened $return .= eL('taskreopened'); break; case '14': //Task assigned if (empty($old_value)) { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskassigned') . ' '; $return .= implode(', ', $users); } elseif (empty($new_value)) { $return .= eL('assignmentremoved'); } else { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskreassigned') . ' '; $return .= implode(', ', $users); } break; case '17': //Reminder added $return .= eL('reminderadded') . ': ' . tpl_userlink($new_value); break; case '18': //Reminder deleted $return .= eL('reminderdeleted') . ': ' . tpl_userlink($new_value); break; case '19': //User took ownership $return .= eL('ownershiptaken') . ': ' . tpl_userlink($new_value); break; case '20': //User requested task closure $return .= eL('closerequestmade') . ' - ' . $new_value; break; case '21': //User requested task $return .= eL('reopenrequestmade') . ' - ' . $new_value; break; case '22': // Dependency added $return .= eL('depadded') . ' ' . tpl_tasklink($new_value); break; case '23': // Dependency added to other task $return .= eL('depaddedother') . ' ' . tpl_tasklink($new_value); break; case '24': // Dependency removed $return .= eL('depremoved') . ' ' . tpl_tasklink($new_value); break; case '25': // Dependency removed from other task $return .= eL('depremovedother') . ' ' . tpl_tasklink($new_value); break; // 26 and 27 replaced by 0 (mark_private) // 26 and 27 replaced by 0 (mark_private) case '28': // PM request denied $return .= eL('pmreqdenied') . ' - ' . $new_value; break; case '29': // User added to assignees list $return .= eL('addedtoassignees'); break; case '30': // user created $return .= eL('usercreated'); break; case '31': // user deleted $return .= eL('userdeleted'); break; } if (isset($details_previous)) { $GLOBALS['details_previous'] = $details_previous; } if (isset($details_new)) { $GLOBALS['details_new'] = $details_new; } return $return; }
/** * XXX: A mess,remove my in 1.0. No time for that, sorry. */ function event_description($history) { $return = ''; global $fs, $baseurl, $details, $proj; $translate = array('item_summary' => 'summary', 'project_id' => 'attachedtoproject', 'task_type' => 'tasktype', 'product_category' => 'category', 'item_status' => 'status', 'task_priority' => 'priority', 'operating_system' => 'operatingsystem', 'task_severity' => 'severity', 'product_version' => 'reportedversion', 'mark_private' => 'visibility', 'estimated_effort' => 'estimatedeffort'); // if somehing gets double escaped, add it here. $noescape = array('new_value', 'old_value'); foreach ($history as $key => $value) { if (!in_array($key, $noescape)) { $history[$key] = Filters::noXSS($value); } } $new_value = $history['new_value']; $old_value = $history['old_value']; switch ($history['event_type']) { case '3': //Field changed if (!$new_value && !$old_value) { $return .= eL('taskedited'); break; } $field = $history['field_changed']; switch ($field) { case 'item_summary': case 'project_id': case 'task_type': case 'product_category': case 'item_status': case 'task_priority': case 'operating_system': case 'task_severity': case 'product_version': if ($field == 'task_priority') { $old_value = $fs->priorities[$old_value]; $new_value = $fs->priorities[$new_value]; } elseif ($field == 'task_severity') { $old_value = $fs->severities[$old_value]; $new_value = $fs->severities[$new_value]; } elseif ($field == 'item_summary') { $old_value = Filters::noXSS($old_value); $new_value = Filters::noXSS($new_value); } else { $old_value = $history[$field . '1']; $new_value = $history[$field . '2']; } $field = eL($translate[$field]); break; case 'closedby_version': $field = eL('dueinversion'); $old_value = $old_value == '0' ? eL('undecided') : $history['product_version1']; $new_value = $new_value == '0' ? eL('undecided') : $history['product_version2']; break; case 'due_date': $field = eL('duedate'); $old_value = formatDate($old_value, false, eL('undecided')); $new_value = formatDate($new_value, false, eL('undecided')); break; case 'percent_complete': $field = eL('percentcomplete'); $old_value .= '%'; $new_value .= '%'; break; case 'mark_private': $field = eL($translate[$field]); if ($old_value == 1) { $old_value = eL('private'); } else { $old_value = eL('public'); } if ($new_value == 1) { $new_value = eL('private'); } else { $new_value = eL('public'); } break; case 'detailed_desc': $field = "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');showTabById('history', true);\">" . eL('details') . '</a>'; if (!empty($details)) { $details_previous = TextFormatter::render($old_value); $details_new = TextFormatter::render($new_value); } $old_value = ''; $new_value = ''; break; case 'estimated_effort': $field = eL($translate[$field]); $old_value = effort::SecondsToString($old_value, $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); $new_value = effort::SecondsToString($new_value, $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); break; } $return .= eL('fieldchanged') . ": {$field}"; if ($old_value || $new_value) { $return .= " ({$old_value} → {$new_value})"; } break; case '1': //Task opened $return .= eL('taskopened'); break; case '2': //Task closed $return .= eL('taskclosed'); $return .= " ({$history['resolution_name']}"; if (!empty($old_value)) { $return .= ': ' . TextFormatter::render($old_value, true); } $return .= ')'; break; case '4': //Comment added $return .= '<a href="#comments">' . eL('commentadded') . '</a>'; break; case '5': //Comment edited $return .= "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');\">" . eL('commentedited') . "</a>"; if ($history['c_date_added']) { $return .= " (" . eL('commentby') . ' ' . tpl_userlink($history['c_user_id']) . " - " . formatDate($history['c_date_added'], true) . ")"; } if ($details) { $details_previous = TextFormatter::render($old_value); $details_new = TextFormatter::render($new_value); } break; case '6': //Comment deleted $return .= "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');\">" . eL('commentdeleted') . "</a>"; if ($new_value != '' && $history['field_changed'] != '') { $return .= " (" . eL('commentby') . ' ' . tpl_userlink($new_value) . " - " . formatDate($history['field_changed'], true) . ")"; } if (!empty($details)) { $details_previous = TextFormatter::render($old_value); $details_new = ''; } break; case '7': //Attachment added $return .= eL('attachmentadded'); if ($history['orig_name']) { $return .= ": <a href=\"{$baseurl}?getfile=" . intval($new_value) . '">' . "{$history['orig_name']}</a>"; } else { if ($history['old_value']) { $return .= ': ' . $history['old_value']; } } break; case '8': //Attachment deleted $return .= eL('attachmentdeleted') . ': ' . Filters::noXSS($new_value); break; case '9': //Notification added $return .= eL('notificationadded') . ': ' . tpl_userlink($new_value); break; case '10': //Notification deleted $return .= eL('notificationdeleted') . ': ' . tpl_userlink($new_value); break; case '11': //Related task added $return .= eL('relatedadded') . ': ' . tpl_tasklink($new_value); break; case '12': //Related task deleted $return .= eL('relateddeleted') . ': ' . tpl_tasklink($new_value); break; case '13': //Task reopened $return .= eL('taskreopened'); break; case '14': //Task assigned if (empty($old_value)) { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskassigned') . ' '; $return .= implode(', ', $users); } elseif (empty($new_value)) { $return .= eL('assignmentremoved'); } else { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskreassigned') . ' '; $return .= implode(', ', $users); } break; // Mentioned in docs, not used anywhere. Will implement if suitable // translations already exist, otherwise leave to 1.1. (Found translations) // Mentioned in docs, not used anywhere. Will implement if suitable // translations already exist, otherwise leave to 1.1. (Found translations) case '15': // This task was added to another task's related list $return .= eL('addedasrelated') . ': ' . tpl_tasklink($new_value); break; case '16': // This task was removed from another task's related list $return .= eL('deletedasrelated') . ': ' . tpl_tasklink($new_value); break; case '17': //Reminder added $return .= eL('reminderadded') . ': ' . tpl_userlink($new_value); break; case '18': //Reminder deleted $return .= eL('reminderdeleted') . ': ' . tpl_userlink($new_value); break; case '19': //User took ownership $return .= eL('ownershiptaken') . ': ' . tpl_userlink($new_value); break; case '20': //User requested task closure $return .= eL('closerequestmade') . ' - ' . $new_value; break; case '21': //User requested task $return .= eL('reopenrequestmade') . ' - ' . $new_value; break; case '22': // Dependency added $return .= eL('depadded') . ' ' . tpl_tasklink($new_value); break; case '23': // Dependency added to other task $return .= eL('depaddedother') . ' ' . tpl_tasklink($new_value); break; case '24': // Dependency removed $return .= eL('depremoved') . ' ' . tpl_tasklink($new_value); break; case '25': // Dependency removed from other task $return .= eL('depremovedother') . ' ' . tpl_tasklink($new_value); break; // 26 and 27 replaced by 0 (mark_private) // 26 and 27 replaced by 0 (mark_private) case '28': // PM request denied $return .= eL('pmreqdenied') . ' - ' . $new_value; break; case '29': // User added to assignees list $return .= eL('addedtoassignees'); break; case '30': // user created $return .= eL('usercreated'); break; case '31': // user deleted $return .= eL('userdeleted'); break; case '32': // Subtask added $return .= eL('subtaskadded') . ' ' . tpl_tasklink($new_value); break; case '33': // Subtask removed $return .= eL('subtaskremoved') . ' ' . tpl_tasklink($new_value); break; case '34': // supertask added $return .= eL('supertaskadded') . ' ' . tpl_tasklink($new_value); break; case '35': // supertask removed $return .= eL('supertaskremoved') . ' ' . tpl_tasklink($new_value); break; } if (isset($details_previous)) { $GLOBALS['details_previous'] = $details_previous; } if (isset($details_new)) { $GLOBALS['details_new'] = $details_new; } return $return; }
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 .= ' ' . 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); }
/** * Redirects the browser to the page in $url * This function is based on PEAR HTTP class * @param string $url * @param bool $exit * @param bool $rfc2616 * @license BSD * @access public static * @return bool * @version 1.0 */ function Redirect($url, $exit = true, $rfc2616 = true) { @ob_clean(); if (isset($_SESSION) && count($_SESSION)) { session_write_close(); } if (headers_sent()) { die('Headers are already sent, this should not have happened. Please inform Flyspray developers.'); } $url = FlySpray::absoluteURI($url); header('Location: ' . $url); if ($rfc2616 && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'HEAD') { $url = Filters::noXSS($url); printf('%s to: <a href="%s">%s</a>.', eL('Redirect'), $url, $url); } if ($exit) { exit; } return true; }
/** * show * * @access public * @return void */ function show() { global $user, $page, $fs, $conf, $db, $proj, $baseurl; $path_to_dot = array_get($conf['general'], 'dot_path', ''); //php 4 on windows does not have is_executable.. $func = function_exists('is_executable') ? 'is_executable' : 'is_file'; $path_to_dot = $func($path_to_dot) ? $path_to_dot : ''; $useLocal = !Flyspray::function_disabled('shell_exec') && $path_to_dot; $fmt = Filters::enum(array_get($conf['general'], 'dot_format', 'png'), array('png', 'svg')); $id = $this->task['task_id']; $page->assign('task_id', $id); $prunemode = Get::num('prune', 0); $selfurl = CreateURL(array('depends', 'task' . $id)); $pmodes = array(L('none'), L('pruneclosedlinks'), L('pruneclosedtasks')); foreach ($pmodes as $mode => $desc) { if ($mode == $prunemode) { $strlist[] = $desc; } else { $strlist[] = "<a href='" . Filters::noXSS($selfurl) . ($mode != 0 ? "&prune={$mode}" : "") . "'>{$desc}</a>\n"; } } $page->assign('strlist', $strlist); $starttime = microtime(); $sql = 'SELECT t1.task_id AS id1, t1.prefix_id AS pxid1, p1.project_prefix AS ppx1, t1.item_summary AS sum1, t1.percent_complete AS pct1, t1.is_closed AS clsd1, t1.closure_comment AS com1, u1c.real_name AS clsdby1, r1.item_name as res1, t2.task_id AS id2, t2.prefix_id AS pxid2, p2.project_prefix AS ppx2, t2.item_summary AS sum2, t2.percent_complete AS pct2, t2.is_closed AS clsd2, t2.closure_comment AS com2, u2c.real_name AS clsdby2, r2.item_name as res2 FROM {dependencies} AS d JOIN {tasks} AS t1 ON d.task_id=t1.task_id LEFT JOIN {users} AS u1c ON t1.closed_by=u1c.user_id LEFT JOIN {projects} AS p1 ON t1.project_id = p1.project_id LEFT JOIN {list_items} AS r1 ON t1.resolution_reason=r1.list_item_id JOIN {tasks} AS t2 ON d.dep_task_id=t2.task_id LEFT JOIN {users} AS u2c ON t2.closed_by=u2c.user_id LEFT JOIN {projects} AS p2 ON t2.project_id = p2.project_id LEFT JOIN {list_items} AS r2 ON t2.resolution_reason=r2.list_item_id WHERE t1.project_id= ? ORDER BY d.task_id, d.dep_task_id'; $edges = $db->x->getAll($sql, null, $proj->id); $edge_list = array(); $rvrs_list = array(); $node_list = array(); foreach ($edges as $row) { extract($row, EXTR_REFS); $edge_list[$id1][] = $id2; $rvrs_list[$id2][] = $id1; if (!isset($node_list[$id1])) { $node_list[$id1] = array('id' => $id1, 'sum' => $sum1, 'pct' => $pct1, 'clsd' => $clsd1, 'ppx' => $ppx1, 'pxid' => $pxid1, 'com' => $com1, 'clsdby' => $clsdby1, 'res' => $res1); } if (!isset($node_list[$id2])) { $node_list[$id2] = array('id' => $id2, 'sum' => $sum2, 'pct' => $pct2, 'clsd' => $clsd2, 'ppx' => $ppx2, 'pxid' => $pxid2, 'com' => $com2, 'clsdby' => $clsdby2, 'res' => $res2); } } // Now we have our lists of nodes and edges, along with a helper // list of reverse edges. Time to do the graph coloring, so we know // which ones are in our particular connected graph. We'll set up a // list and fill it up as we visit nodes that are connected to our // main task. $connected = array(); $levelsdown = 0; $levelsup = 0; function ConnectsTo($id, $down, $up, &$connected, &$edge_list, &$rvrs_list, &$levelsdown, &$levelsup, &$prunemode, &$node_list) { if (!isset($connected[$id])) { $connected[$id] = 1; } if ($down > $levelsdown) { $levelsdown = $down; } if ($up > $levelsup) { $levelsup = $up; } $selfclosed = $node_list[$id]['clsd']; if (isset($edge_list[$id])) { foreach ($edge_list[$id] as $neighbor) { $neighborclosed = $node_list[$neighbor]['clsd']; if (!isset($connected[$neighbor]) && !($prunemode == 1 && $selfclosed && $neighborclosed) && !($prunemode == 2 && $neighborclosed)) { ConnectsTo($neighbor, $down, $up + 1, $connected, $edge_list, $rvrs_list, $levelsdown, $levelsup, $prunemode, $node_list); } } } if (isset($rvrs_list[$id])) { foreach ($rvrs_list[$id] as $neighbor) { $neighborclosed = $node_list[$neighbor]['clsd']; if (!isset($connected[$neighbor]) && !($prunemode == 1 && $selfclosed && $neighborclosed) && !($prunemode == 2 && $neighborclosed)) { ConnectsTo($neighbor, $down + 1, $up, $connected, $edge_list, $rvrs_list, $levelsdown, $levelsup, $prunemode, $node_list); } } } } ConnectsTo($id, 0, 0, $connected, $edge_list, $rvrs_list, $levelsdown, $levelsup, $prunemode, $node_list); $connected_nodes = array_keys($connected); sort($connected_nodes); // Now lets get rid of the extra junk in our arrays. // In prunemode 0, we know we're only going to have to get rid of // whole lists, and not elements in the lists, because if they were // in the list, they'd be connected, so we wouldn't be removing them. // In prunemode 1 or 2, we may have to remove stuff from the list, because // you can have an edge to a node that didn't end up connected. foreach (array("edge_list", "rvrs_list", "node_list") as $l) { foreach (${$l} as $n => $list) { if (!isset($connected[$n])) { unset(${$l}[$n]); } if ($prunemode != 0 && $l != "node_list" && isset(${$l}[$n])) { // Only keep entries that appear in the $connected_nodes list ${$l}[$n] = array_intersect(${$l}[$n], $connected_nodes); } } } // Now we've got everything we need... let's draw the pretty pictures //Open the graph, and print global options $lj = 'n'; // label justification - l, r, or n (for center) $graphname = "task_{$id}_dependencies"; $dotgraph = "digraph {$graphname} {\n" . "node [width=1.1, shape=ellipse, border=10, color=\"#00E11E\", style=\"filled\", " . "fontsize=10.0, pencolor=black, margin=\"0.1, 0.0\"];\n"; // define the nodes foreach ($node_list as $n => $r) { $col = ""; if ($r['clsd'] && $n != $id) { $r['pct'] = 120; } // color code: shades of gray for % done $x = dechex(255 - ($r['pct'] + 10)); $col = "#{$x}{$x}{$x}"; // Make sure label terminates in \n! $label = $r['ppx'] . '#' . $r['pxid'] . " \n" . ($useLocal ? addslashes(utf8_substr($r['sum'], 0, 15)) . "\n" : '') . ($r['clsd'] ? L('closed') : "{$r['pct']}% " . L('complete')); $tooltip = $r['clsd'] ? L('closed') . ": {$r['res']}" . (!empty($r['clsdby']) ? " ({$r['clsdby']})" : '') . ($r['com'] != '' ? ' - ' . str_replace(array("\r", "\n"), '', $r['com']) : '') : $r['pct']; $dotgraph .= "FS{$n} [label=\"" . str_replace("\n", "\\{$lj}", $label) . "\", " . ($r['clsd'] ? 'color=black,' : '') . ($r['clsd'] ? 'fillcolor=white,' : "fillcolor=\"{$col}\",") . ($n == $id ? 'shape=box,' : '') . "href=\"javascript:top.window.location.href='" . CreateURL(array("details", 'task' . $n)) . "'\", target=\"_top\" " . "tooltip=\"{$tooltip}\"];\n"; } // Add edges foreach ($edge_list as $src => $dstlist) { foreach ($dstlist as $dst) { $dotgraph .= "FS{$src} -> FS{$dst};\n"; } } // all done $dotgraph .= "}\n"; // All done with the graph. Save it to a temp file (new name if the data has changed) $dotfilename = sprintf('cache/fs_depends_dot_%d_%s.dot', $id, md5($dotgraph)); $imgfilename = sprintf('%s/%s.%s', BASEDIR, $dotfilename, $fmt); $mapfilename = sprintf('%s/%s.%s', BASEDIR, $dotfilename, 'map'); //cannot use tempnam( ) as file has to end with $ftm extension if (!$useLocal) { //cannot use tempnam() as file has to end with $ftm extension $tname = $dotfilename; } else { // we are operating on the command line, avoid races. $tname = tempnam(Flyspray::get_tmp_dir(), md5(uniqid(mt_rand(), true))); } //get our dot done.. file_put_contents($tname, $dotgraph, LOCK_EX); // Now run dot on it, if target file does not already exist if (!is_file($imgfilename)) { if (!$useLocal) { require_once 'Zend/Rest/Client.php'; $client = new Zend_Rest_Client('http://webdot.flyspray.org/'); $data = base64_decode($client->getGraph(base64_encode($dotgraph), $fmt)->post()); file_put_contents($imgfilename, $data, LOCK_EX); $data = base64_decode($client->getGraph(base64_encode($dotgraph), 'cmapx')->post()); file_put_contents($mapfilename, $data, LOCK_EX); } else { $tfn = escapeshellarg($tname); shell_exec(sprintf('%s -T %s -o %s %s', $path_to_dot, escapeshellarg($fmt), escapeshellarg($imgfilename), $tfn)); $data['map'] = shell_exec(sprintf('%s -T cmapx %s', $path_to_dot, $tfn)); file_put_contents($mapfilename, $data['map'], LOCK_EX); // Remove files so that they are not exposed to the public unlink($tname); } } $page->assign('map', file_get_contents($mapfilename)); $page->assign('image', sprintf('%s%s.%s', $baseurl, $dotfilename, $fmt)); // we have to find out the image size if it is SVG if ($fmt == 'svg') { if (!$remote) { $data = file_get_contents(BASEDIR . '/' . $file_name . '.' . $fmt); } preg_match('/<svg width="([0-9.]+)([a-zA-Z]+)" height="([0-9.]+)([a-zA-Z]+)"/', $data, $matches); $page->assign('width', round($matches[1] * ($matches[2] == 'pt' ? 1.4 : ($matches[2] == 'in' ? 1.33 * 72.27 : 1)), 0)); $page->assign('height', round($matches[3] * ($matches[4] == 'pt' ? 1.4 : ($matches[4] == 'in' ? 1.35 * 72.27 : 1)), 0)); } /* [TC] We cannot have this stuff outputting here, so I put it in a quick template */ $page->assign('taskid', $id); $page->assign('fmt', $fmt); $page->assign('graphname', $graphname); $endtime = microtime(); list($startusec, $startsec) = explode(' ', $starttime); list($endusec, $endsec) = explode(' ', $endtime); $diff = $endsec - $startsec + ($endusec - $startusec); $page->assign('time', round($diff, 2)); $page->setTitle($this->task['project_prefix'] . '#' . $this->task['prefix_id'] . ': ' . L('dependencygraph')); $page->pushTpl('depends.tpl'); }
/** * Returns (safe) HTML which displays a field to edit a value * @access public * @param bool $use_default use default field value or not * @param bool $lock lock the field depending on the users perms ornot * @param array $task task data * @param array $add_options add options to the select? * @param array $attrs add attributes to the select * @return string */ function edit($use_default = true, $lock = false, $task = array(), $add_options = array(), $attrs = array(), $prefix = '') { global $user, $proj; if ($use_default) { $task['field' . $this->id] = $this->prefs['default_value']; } else { if (!isset($task['field' . $this->id])) { $task['field' . $this->id] = ''; } } // determine whether or not to lock inputs $lock = $lock && $this->prefs['force_default'] && (count($task) > 3 && !$user->can_edit_task($task) || !$user->perms('modify_all_tasks')); $html = ''; switch ($this->prefs['field_type']) { case FIELD_LIST: if (!$this->prefs['list_id']) { return ''; } $html .= sprintf('<select id="%sfield%d" name="%sfield%d%s" %s ', $prefix, $this->id, $prefix, $this->id, isset($attrs['multiple']) ? '[]' : '', join_attrs($attrs)); $html .= tpl_disableif($lock) . '>'; $html .= tpl_options(array_merge($add_options, $proj->get_list($this->prefs, $task['field' . $this->id])), Req::val('field' . $this->id, $task['field' . $this->id])); $html .= '</select>'; break; case FIELD_DATE: $attrs = array(); if ($lock) { $attrs = array('readonly' => 'readonly'); } $html .= tpl_datepicker($prefix . 'field' . $this->id, '', Req::val('field' . $this->id, $task['field' . $this->id]), $attrs); break; case FIELD_TEXT: $html .= sprintf('<input type="text" class="text" id="%sfield%d" name="%sfield%d" value="%s"/>', $prefix, $this->id, $prefix, $this->id, Filters::noXSS(Req::val('field' . $this->id, $task['field' . $this->id]))); break; case FIELD_USER: $html .= tpl_userselect($prefix . 'field' . $this->id, Req::val('field' . $this->id, $task['field' . $this->id])); break; } return $html; }
exit; } $task = Flyspray::GetTaskDetails(Post::val('task_id')); // we better not forget this one ;) if (!$user->can_edit_task($task)) { header('HTTP/1.1 400 Bad Request'); exit; } // pre build some HTML $task['num_assigned'] = count($task['assigned_to']); $task['assigned_to_name'] = reset($task['assigned_to_name']); $prev = Filters::noXSS(str_replace("'", "\\'", tpl_draw_cell($task, $field, '<span class="%s %s">%s</span>'))); $id = sprintf('id="task%d_%s" name="task%d_%s"', $task['task_id'], $field, $task['task_id'], $field); switch ($field) { case 'summary': echo '<input type="text" class="text" ' . $id . ' value="' . Filters::noXSS($task['item_summary']) . '" />'; break; case 'project': echo '<select ' . $id . '>' . tpl_options($fs->projects, $task['project_id']) . ' </select>'; break; case 'progress': $arr = array(); for ($i = 0; $i <= 100; $i += 10) { $arr[$i] = $i . '%'; } echo '<select ' . $id . '>' . tpl_options($arr, $task['percent_complete']) . ' </select>'; break; case 'assignedto': // additional permission check is needed
/** * html escaped variant of the previous * return $string */ function eL($key) { return Filters::noXSS(L($key)); }
/** * Give user input free from potentially mailicious html and JS insertions * @param mixed $data * @return string * @access public static */ function noJsXSS($data) { if (empty($data) || is_numeric($data)) { return $data; } elseif (is_string($data)) { return Filters::noXSS(preg_replace("/[-]|Â[€-Ÿ]/", "", addcslashes($data, "\t\"'\\"))); } return ''; }
function textarea($name, $rows, $cols, $attrs = null, $content = null, $plugins = array()) { global $page, $proj, $user; $name = Filters::noXSS($name); $return = sprintf('<textarea name="%s" id="%s" cols="%s" rows="%s" ', $name, $name, intval($cols), intval($rows)); if (is_array($attrs) && count($attrs)) { $return .= join_attrs($attrs); } $return .= '>'; if (is_string($content) && strlen($content)) { $return .= Filters::noXSS($content); } $return .= '</textarea>'; // does the user have any personal preference? if (!count($plugins) && !$user->isAnon()) { $plugins = explode(' ', $user->infos['syntax_plugins']); } // [BC] if no plugins are set, we assume a project's default plugins if (!count($plugins)) { $plugins = explode(' ', $proj->prefs['syntax_plugins']); } return str_replace('%id', $name, $this->htmlbefore . $page->fetch('pluginoptions.tpl', 'plugins', $plugins)) . $return . str_replace('%id', $name, $this->htmlafter); }
function pagenums($pagenum, $perpage, $totalcount) { global $proj; $pagenum = intval($pagenum); $perpage = intval($perpage); $totalcount = intval($totalcount); // Just in case $perpage is something weird, like 0, fix it here: if ($perpage < 1) { $perpage = $totalcount > 0 ? $totalcount : 1; } $pages = ceil($totalcount / $perpage); $output = sprintf(eL('page'), $pagenum, $pages); if ($totalcount / $perpage > 1) { $params = $_GET; # unset unneeded params for shorter urls unset($params['do']); unset($params['project']); unset($params['switch']); $output .= '<span class="pagenums DoNotPrint">'; $start = max(1, $pagenum - 4 + min(2, $pages - $pagenum)); $finish = min($start + 4, $pages); if ($start > 1) { $url = Filters::noXSS(CreateURL('tasklist', $proj->id, null, array_merge($params, array('pagenum' => 1)))); $output .= sprintf('<a href="%s"><<%s </a>', $url, eL('first')); } if ($pagenum > 1) { $url = Filters::noXSS(CreateURL('tasklist', $proj->id, null, array_merge($params, array('pagenum' => $pagenum - 1)))); $output .= sprintf('<a id="previous" accesskey="p" href="%s">< %s</a> - ', $url, eL('previous')); } for ($pagelink = $start; $pagelink <= $finish; $pagelink++) { if ($pagelink != $start) { $output .= ' - '; } if ($pagelink == $pagenum) { $output .= sprintf('<strong>%d</strong>', $pagelink); } else { $url = Filters::noXSS(CreateURL('tasklist', $proj->id, null, array_merge($params, array('pagenum' => $pagelink)))); $output .= sprintf('<a href="%s">%d</a>', $url, $pagelink); } } if ($pagenum < $pages) { $url = Filters::noXSS(CreateURL('tasklist', $proj->id, null, array_merge($params, array('pagenum' => $pagenum + 1)))); $output .= sprintf(' - <a id="next" accesskey="n" href="%s">%s ></a>', $url, eL('next')); } if ($finish < $pages) { $url = Filters::noXSS(CreateURL('tasklist', $proj->id, null, array_merge($params, array('pagenum' => $pages)))); $output .= sprintf('<a href="%s"> %s >></a>', $url, eL('last')); } $output .= '</span>'; } return $output; }
function tpl_draw_cell($task, $colname, $format = "<td class='%s %s'>%s</td>") { global $fs, $proj, $page; $indexes = array('id' => 'task_id', 'project' => 'project_title', 'summary' => 'item_summary', 'dateopened' => 'date_opened', 'openedby' => 'opened_by_real_name', 'closedby' => 'closed_by_real_name', 'changedby' => 'last_changed_by_real_name', 'assignedto' => 'assigned_to_name', 'lastedit' => 'last_changed_time', 'comments' => 'comment_count', 'votes' => 'vote_count', 'attachments' => 'attachment_count', 'dateclosed' => 'date_closed', 'projectlevelid' => 'prefix_id', 'progress' => '', 'state' => '', 'private' => 'mark_private'); //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 ''; } switch ($colname) { case 'id': $value = tpl_tasklink($task, $task['task_id']); break; case 'projectlevelid': $value = tpl_tasklink($task, $task['project_prefix'] . '#' . $task['prefix_id']); break; case 'summary': $value = tpl_tasklink($task, utf8_substr($task['item_summary'], 0, 55), false, array(), array('state', 'age', 'percent_complete')); if (utf8_strlen($task['item_summary']) > 55) { $value .= '...'; } break; case 'lastedit': case 'dateopened': case 'dateclosed': $value = formatDate($task[$indexes[$colname]]); break; case 'progress': $value = '<div class="taskpercent"><div style="width:' . $task['percent_complete'] . '%"> </div></div>'; break; case 'assignedto': $value = Filters::noXSS($task[$indexes[$colname]]); if ($task['num_assigned'] > 1) { $value .= ', +' . ($task['num_assigned'] - 1); } break; case 'state': if ($task['is_closed']) { $value = L('closed'); } elseif ($task['closed_by']) { $value = L('reopened'); } else { $value = L('open'); } break; case 'private': $value = $task[$indexes[$colname]] ? L('yes') : L('no'); break; default: if (isset($indexes[$colname])) { $value = $task[$indexes[$colname]]; } elseif (isset($task[$colname . '_name'])) { $value = $task[$colname . '_name']; } else { if (isset($proj->fields[$colname])) { $value = $proj->fields[$colname]->view($task, array(), true); } else { $value = $task[$colname]; } } $value = Filters::noXSS($value); break; } $wsvalue = str_replace(' ', '', $value); return sprintf($format, 'task_' . $colname, Filters::isAlnum($wsvalue) ? $colname . '_' . $wsvalue : '', $value); }
function tpl_list_heading($colname, $coldisplay, $format = "<th%s>%s</th>") { global $proj, $page; $imgbase = '<img src="%s" alt="%s" />'; $class = ''; $html = $coldisplay; $colname = strtolower($colname); if ($colname == 'comments' || $colname == 'attachments') { $html = sprintf($imgbase, $page->get_image(substr($colname, 0, -1)), $html); } if (Get::val('order') == $colname) { $class = ' class="orderby"'; $sort1 = Get::safe('sort', 'desc') == 'desc' ? 'asc' : 'desc'; $sort2 = Get::safe('sort2', 'desc'); $order2 = Get::safe('order2'); $html .= ' ' . sprintf($imgbase, $page->get_image(Get::val('sort')), Get::safe('sort')); } else { $sort1 = 'desc'; $sort2 = Get::safe('sort', 'desc'); $order2 = Get::safe('order'); } $new_order = array('order' => $colname, 'sort' => $sort1, 'order2' => $order2, 'sort2' => $sort2); $html = sprintf('<a title="%s" href="%s">%s</a>', L('sortthiscolumn'), Filters::noXSS($page->url(array('index', 'proj' . $proj->id), array_merge($_GET, $new_order))), $html); return sprintf($format, $class, $html); }