--- /dev/null
+<?php
+/**
+ * every class extends this...
+ */
+
+
+class MTrackWeb extends HTML_FlexyFramework_Page
+{
+ var $template = 'wiki.html';
+ var $priorities = array();
+ var $severities = array();
+ var $link = false; // the link handler..
+
+ function hasPerm($what, $cando) {
+ // our whole perm logic sits in here....
+
+ // here's how it works
+ // a) anonymous users - not authenticated.
+ // - can see projects that are in *PUBLIC project "MTrack.Repos", "S"
+ // - can see bugs that are in *PUBLIC project "MTrack.Issue", "S"
+ // - can see bugs that are in *PUBLIC project "MTrack.Wiki", "S"
+ if (!$this->authUser) {
+ if ($cando == 'S' &&
+ in_array( $what , array( 'MTrack.Repos', 'MTrack.Issue', 'MTrack.Wiki'))) {
+ return true; // not a diffinative answer...
+ }
+ return false;
+ }
+
+ return $this->authUser->hasPerm($what, $cando);
+ }
+
+ function projectPerm($project_id, $what, $cando)
+ {
+ if (!$project_id) {
+ return false;
+ }
+ $p = DB_DataObject::factory('Projects');
+ $p->get($project_id);
+ if (!$this->authUser) {
+ if ($p->code != '*PUBLIC') {
+ return false; // only public projects
+ }
+ if ($cando != 'S') {
+ return false;
+ }
+ // all permissions to view public stuff.
+ return true;
+ }
+ if (!$this->authUser->hasPerm($what, $cando)) {
+ echo "NO PERMS $what $cando";
+ echo '<PRE>'; print_r($this->authUser->getPerms());
+ return false;
+ }
+ // membership rules?
+ //echo "COMPTYPE " . $this->authUser->company()->comptype ;
+ if ($this->authUser->company()->comptype == 'OWNER') {
+
+ if ($this->authUser->hasPerm('Core.Projects_All', $cando)) { // they can do what they like on all projects.
+ return true;
+ }
+ // return $p->hasPerm($what, $cando);
+ }
+ // otherwise they have to be a team member of that project.
+
+ $pd = DB_DataObject::factory('ProjectDirectory');
+ $pd->project_id = $project_id;
+ $pd->user_id = $this->authUser->id;
+ if (!$pd->count()) {
+ return false;
+ }
+ return true;
+
+
+
+
+ }
+
+
+ function getAuthUser()
+ {
+ $u = DB_DataObject::factory('Person');
+ if (!$u->isAuth()) {
+ return false;
+ }
+ return $u->getAuthUser();
+ }
+ /**
+ * base getAuth allows everyone in..
+ */
+
+ function getAuth()
+ {
+ $this->registerClasses(); // to be destroyed??
+
+ $ff = HTML_FlexyFramework::get();
+ if ($ff->cli) {
+ return true;
+ }
+
+ // default timezone first..
+ $ff = HTML_FlexyFramework::get();
+ if (isset($ff->MTrack['timezone'])) {
+ date_default_timezone_set($ff->MTrack['timezone']);
+ }
+
+ //MTrackConfig::boot(); // eak.. .remove me...
+
+ $this->authUser = DB_DataObject::factory('Person')->getAuthUser();
+ if (!$this->authUser) {
+ return true; // we do allow people in this far..
+ }
+
+
+ // timezone setting... -- this may be a good addon to our core person class.
+
+ if (!empty($this->authUser->timezone)) {
+ date_default_timezone_set($this->authUser->timezone);
+ return;
+ }
+
+ /// fixme...
+ //$this->authUser =
+ return true; // anyone at present..
+ }
+ function get($loc='')
+ {
+ //
+ if (!empty($loc)) {
+ die ("invalid location". htmlspecialchars($loc));
+ }
+ if (!$this->authUser) {
+ return HTML_FlexyFramework::run('Wiki');
+ }
+ return HTML_FlexyFramework::run('Wiki/Today');
+
+ }
+ function post()
+ {
+ header("Status: 404 Not Found");
+ die("not valid");
+ }
+
+
+ function initOptions()
+ {
+
+
+ $q = MTrackDB::q('select priorityname, value from priorities');
+
+ foreach ($q->fetchAll() as $row) {
+ $this->priorities[$row[0]] = $row[1];
+ }
+ $q = MTrackDB::q('select sevname, ordinal from severities');
+
+ foreach ($q->fetchAll() as $row) {
+ $this->severities[$row[0]] = $row[1];
+ }
+
+ }
+
+ function registerClasses()
+ {
+ require_once 'MTrack/Wiki.php';
+ require_once 'MTrack/Wiki/Item.php';
+ require_once 'MTrack/Milestone.php';
+ //require_once 'MTrack/Report.php';
+ //require_once 'MTrack/SearchDB.php';
+ //require_once 'MTrack/Watch.php';
+ //require_once 'MTrack/CommitChecker.php';
+
+
+ require_once 'MTrackWeb/LinkHandler.php';
+ require_once 'MTrack/Wiki/HTMLFormatter.php';
+
+ $this->link = new MTrackWeb_LinkHandler();
+ MTrack_Wiki_HTMLFormatter::registerLinkHandler($this->link);
+
+
+ $r = DB_DataObject::factory('mtrack_repos');
+ $r->loadFromPath('default/wiki');
+ MTrack_Wiki_Item::$repo = $r->impl();
+
+
+
+ //MTrack_Wiki::register_macro('MilestoneSummary', array('MTrack_Milestone', 'macro_MilestoneSummary'));
+ // MTrack_Wiki::register_macro('BurnDown', array('MTrack_Milestone', 'macro_BurnDown'));
+ //MTrack_Wiki::register_macro('RunReport', array('MTrack_Report', 'macro_RunReport')); << fixme how are we to hanlde this..
+ //MTrack_Wiki::register_macro('TicketQuery', array('MTrack_Report', 'macro_TicketQuery'));
+ MTrack_Wiki::register_macro('IncludeWikiPage', array('MTrack_Wiki', 'macro_IncludeWiki'));
+ MTrack_Wiki::register_macro('IncludeHelpPage', array('MTrack_Wiki', 'macro_IncludeHelp'));
+ MTrack_Wiki::register_macro('Comment', array('MTrack_Wiki', 'macro_comment'));
+ MTrack_Wiki::register_processor('comment', array('MTrack_Wiki', 'processor_comment'));
+ MTrack_Wiki::register_processor('html', array('MTrack_Wiki', 'processor_html'));
+ MTrack_Wiki::register_processor('dataset', array('MTrack_Wiki', 'processor_dataset'));
+
+
+ //MTrackACL::registerAncestry('milestone', 'Roadmap');
+ //MTrackACL::registerAncestry('report', 'Reports');
+ //MTrackACL::registerAncestry('snippet', 'Snippets');
+ //MTrackACL::registerAncestry('repo', 'Browser');
+ //MTrackACL::registerAncestry('enum', 'Enumerations');
+ //MTrackACL::registerAncestry("component", 'Components');
+ //MTrackACL::registerAncestry("project", 'Projects');
+ //MTrackACL::registerAncestry("ticket", "Tickets");
+ //MTrackACL::registerAncestry('wiki', array('MTrack_Wiki_Item', '_get_parent_for_acl'));
+
+ //MTrackSearchDB::register_indexer('ticket', array('MTrackIssue', 'index_issue'));
+ //MTrackSearchDB::register_indexer('wiki', array('MTrack_Wiki_Item', 'index_item'));
+
+
+
+ //MTrackWatch::registerEventTypes('ticket', array( 'ticket' => 'Tickets' ));
+ //MTrackWatch::registerEventTypes('milestone', array( 'ticket' => 'Tickets', 'changeset' => 'Code changes' ));
+ //MTrackWatch::registerEventTypes('repo', array( 'ticket' => 'Tickets', 'changeset' => 'Code changes' ));
+
+ // should this get registered here??
+ //MTrackCommitChecker::addCheck('Wiki');
+
+
+
+ }
+
+ function favicon()
+ {
+ return false;
+ /// FIXME - we should allow upload of a favion...
+ $ff = HTML_FlexyFramework::get();
+
+
+ }
+
+
+ /* renders the attachment list for a given object */
+ // was Attachments::render
+ // move it to MTrackWebAttachemnt...
+
+ function attachmentsToHtml($object)
+ {
+ return 'TODO';
+ if (is_object($object)) {
+ $object = $object->toIdString(); // eg. ticket:1
+ }
+ $atts = MTrackDB::q('
+ select * from attachments
+ left join changes on (attachments.cid = changes.cid)
+ where attachments.object = ? order by changedate, filename',
+ $object)->fetchAll(PDO::FETCH_ASSOC);
+
+ if (count($atts) == 0) return '';
+
+ $max_dim = 150;
+
+ $html = "<div class='attachment-list'><b>Attachments</b><ul>";
+ foreach ($atts as $row) {
+ $url = "{$this->baseURL}/Attachment/$object/". $row['cid'] . '/' . $row['filename'];
+
+ $html .= "<li><a class='attachment'" .
+ " href='$url'>".
+ "$row[filename]</a> ($row[size]) added by " .
+ $this->link->username($row['who'], array(
+ 'no_image' => true
+ )) .
+ " " . $this->link->date($row['changedate']);
+ require_once 'MTrack/Attachment.php';
+ list($width, $height) = getimagesize(MTrackAttachment::local_path($row['hash']));
+ if ($width + $height) {
+ /* limit maximum size */
+ if ($width > $max_dim) {
+ $height *= $max_dim / $width;
+ $width = $max_dim;
+ }
+ if ($height > $max_dim) {
+ $width *= $max_dim / $height;
+ $height = $max_dim;
+ }
+ $html .= "<br><a href='$url'><img src='$url' width='$width' border='0' height='$height'></a>";
+ }
+
+ $html .= "</li>\n";
+ }
+ $html .= "</ul></div>";
+ return $html;
+ }
+ function jerr($str, $errors=array()) // standard error reporting..
+ {
+ require_once 'Services/JSON.php';
+ $json = new Services_JSON();
+
+ // log all errors!!!
+ //$this->addEvent("ERROR", false, $str);
+ /*
+ if (!empty($_REQUEST['returnHTML']) ||
+ (isset($_SERVER['CONTENT_TYPE']) && preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']))
+ ) {
+ header('Content-type: text/html');
+ echo "<HTML><HEAD></HEAD><BODY>";
+ echo $json->encodeUnsafe(array(
+ 'success'=> false,
+ 'message' => $str, // compate with exeption / loadexception.
+
+ 'errors' => $errors ? $errors : true, // used by forms to flag errors.
+ 'authFailure' => !empty($errors['authFailure']),
+ ));
+ echo "</BODY></HTML>";
+ exit;
+ }
+ */
+ echo $json->encode(array(
+ 'success'=> false,
+ 'data'=> array(),
+ 'message' => $str, // compate with exeption / loadexception.
+ 'errors' => $errors ? $errors : true, // used by forms to flag errors.
+ 'authFailure' => !empty($errors['authFailure']),
+ ));
+ exit;
+
+ }
+ function jok($str)
+ {
+
+ require_once 'Services/JSON.php';
+ $json = new Services_JSON();
+ /*
+ if (!empty($_REQUEST['returnHTML']) ||
+ (isset($_SERVER['CONTENT_TYPE']) && preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']))
+
+ ) {
+ header('Content-type: text/html');
+ echo "<HTML><HEAD></HEAD><BODY>";
+ echo $json->encodeUnsafe(array('success'=> true, 'data' => $str));
+ echo "</BODY></HTML>";
+ exit;
+ }
+ */
+
+ echo $json->encode(array('success'=> true, 'data' => $str));
+ exit;
+
+ }
+ /**
+ * output data for grids or tree
+ * @ar {Array} ar Array of data
+ * @total {Number|false} total number of records (or false to return count(ar)
+ * @extra {Array} extra key value list of data to pass as extra data.
+ *
+ */
+ function jdata($ar,$total=false, $extra=array())
+ {
+ // should do mobile checking???
+ if ($total == false) {
+ $total = count($ar);
+ }
+ $extra= $extra ? $extra : array();
+ require_once 'Services/JSON.php';
+ $json = new Services_JSON();
+ echo $json->encode(array('success' => true, 'total'=> $total, 'data' => $ar) + $extra);
+ exit;
+
+
+ }
+
+ /**
+ * ---------------- Logging ---------------
+ */
+
+ /**
+ * addEventOnce:
+ * Log an action (only if it has not been logged already.
+ *
+ * @param {String} action - group/name of event
+ * @param {DataObject|false} obj - dataobject action occured on.
+ * @param {String} any remarks
+ */
+
+ function addEventOnce($act, $obj = false, $remarks = '')
+ {
+ $au = $this->getAuthUser();
+ $e = DB_DataObject::factory('Events');
+ $e->init($act,$obj,$remarks);
+ if ($e->find(true)) {
+ return;
+ }
+ $this->addEvent($act, $obj, $remarks);
+ }
+ /**
+ * addEvent:
+ * Log an action.
+ *
+ * @param {String} action - group/name of event
+ * @param {DataObject|false} obj - dataobject action occured on.
+ * @param {String} any remarks
+ */
+
+ function addEvent($act, $obj = false, $remarks = '')
+ {
+ $au = $this->getAuthUser();
+ $e = DB_DataObject::factory('Events');
+ $e->init($act,$obj,$remarks);
+
+ $e->event_when = date('Y-m-d H:i:s');
+
+ $eid = $e->insert();
+ $ff = HTML_FlexyFramework::get();
+ if (empty($ff->Pman['event_log_dir'])) {
+ return;
+ }
+ $file = $ff->Pman['event_log_dir']. date('/Y/m/d/'). $eid . ".php";
+ if (!file_exists(dirname($file))) {
+ mkdir(dirname($file),0700,true);
+ }
+ file_put_contents($file, var_export(array(
+ 'REQUEST_URI' => empty($_SERVER['REQUEST_URI']) ? 'cli' : $_SERVER['REQUEST_URI'],
+ 'GET' => empty($_GET) ? array() : $_GET,
+ 'POST' => empty($_POST) ? array() : $_POST,
+ ), true));
+
+
+
+ }
+
+
+
+
+}
\ No newline at end of file
--- /dev/null
+<?php # vim:ts=2:sw=2:et:
+/* For licensing and copyright terms, see the file named LICENSE */
+
+//require_once 'MTrack/Captcha.php';
+
+
+require_once 'MTrackWeb.php';
+class MTrackWeb_Ticket extends MTrackWeb
+{
+ var $id; // 0 = new
+ var $issue;
+ var $preview;
+ var $error;
+ var $editable;
+ var $tid = 0; // or the MD5 rep.
+
+
+
+ function getAuth()
+ {
+ parent::getAuth();
+ //require_once 'MTrack/ACL.php';
+ // MTrackACL::requireAllRights('Browser', 'read');
+ return true;
+
+ }
+
+ function get($pi= 0)
+ {
+
+ if (!isset($_REQUEST['ajax_body'])) {
+ return;
+ }
+ $this->masterTemplate = 'ticket.html';
+ $this->id = $pi ? $pi: (isset($_GET['id']) ? $_GET['id'] : 0);
+ $this->id = $this->id == 'new' ? 0 : $this->id;
+ $this->id = (int) $this->id;
+
+
+ // -- load issue..
+
+ $this->issue = DB_DataObject::factory('mtrack_ticket');
+
+ if ($this->id) {
+ if (!$this->issue->get($this->id)) {
+ throw new Exception("Invalid ticket $this->id");
+ }
+ }
+
+
+
+
+
+ //$this->issue->augmentFormFields($this->fieldset());
+
+
+ $this->preview = false;
+ $this->error = array();
+
+
+ if (!$this->id && !$this->hasPerm('MTrack.Issue','A')) {
+ return HTML_FlexyFramework::run('Noperm');
+ }
+ if ($this->id && (
+ !$this->hasPerm('MTrack.Issue','S') || // general permission to view
+ !$this->ticket->hasPerm($this->authUser,'S') // specific permission on this bug.
+
+ )) {
+ return HTML_FlexyFramework::run('Noperm');
+ }
+
+ // new is always editable..????
+ $this->editable = $this->id ?
+ $this->ticket->hasPerm($this->authUser,'E') : true;
+
+ $this->issue->milestoneURL = $this->baseURL.'/Milestone'; // fix me later..
+
+ $this->showEditBar = false;
+
+ if ($this->editable && $this->id && !$this->preview) {
+ $this->showEditBar = true;
+ }
+
+ $this->initEditForm();
+
+
+
+ }
+
+ function post($pi=0) // handle the post...
+ {
+ die("TODO");
+ $this->get($pi);
+
+ if (isset($_POST['cancel'])) {
+ header("Location: {$$this->baseURL}/Ticket/$this->issue->nsident");
+ exit;
+ }
+
+ if (!MTrack_Captcha::check('ticket')) {
+ $this->error[] = "CAPTCHA failed, please try again";
+ }
+ $this->preview = isset($_POST['preview']) ? true : false;
+
+ $comment = '';
+ try {
+ if (!$this->id) {
+ MTrackACL::requireAllRights("Tickets", 'create');
+ } else {
+ MTrackACL::requireAllRights("ticket:" . $this->issue->tid, 'modify');
+ }
+ } catch (Exception $e) {
+ $this->error[] = $e->getMessage();
+ }
+
+ if (!$this->id) {
+ $comment = empty($_POST['comment']) ? '' : $_POST['comment'];
+ }
+
+ if (!strlen($comment)) {
+ $comment = $_POST['summary'];
+ }
+ try {
+ $CS = MTrackChangeset::begin("ticket:X", $comment);
+ } catch (Exception $e) {
+ $this->error[] = $e->getMessage();
+ $CS = null;
+ }
+ if (!$this->id) {
+ // compute next id number.
+ // We don't use auto-number, because we allow for importing multiple
+ // projects with their own ticket sequence.
+ // During "normal" user-driven operation, we do want plain old id numbers
+ // so we compute it here, under a transaction
+ $db = MTrackDB::get();
+
+
+
+ switch($db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
+ case 'pgsql':
+ // Some versions of postgres don't like that we have "abc123" for
+ // identifiers, so match on the bigest number nsident fields only
+ $max = "select max(cast(nsident as integer)) + 1 from tickets where nsident ~ '^\\\\d+$'";
+ break;
+
+ case 'mysql':
+ $max = "select max(cast(nsident as UNSIGNED)) + 1 from tickets";
+ break;
+
+ default:
+ $max = 'select max(cast(nsident as integer)) + 1 from tickets';
+ break;
+ }
+
+
+
+ list($this->issue->nsident) = MTrackDB::q($max)->fetchAll(PDO::FETCH_COLUMN, 0);
+ if ($this->issue->nsident === null) {
+ $this->issue->nsident = 1;
+ }
+ }
+
+ if (isset($_POST['action']) && !$this->preview) {
+ $act= explode('_', $_POST['action'] , 2);
+ //var_dump($act);exit;
+ switch ($act[0]) {
+ case 'leave':
+ break;
+ case 'reopen':
+ $this->issue->reOpen();
+ break;
+ case 'fixed':
+ $this->issue->resolution = 'fixed';
+ $this->issue->close();
+ $_POST['estimated'] = $this->issue->estimated;
+ break;
+
+
+ case 'accept':
+ // will be applied to the issue further down
+ $_POST['owner'] = MTrackAuth::whoami();
+ if ($this->issue->status == 'new') {
+ $this->issue->status = 'open';
+ }
+ break;
+
+
+ case 'resolve':
+ //$this->issue->resolution = $_POST['resolution'];
+ $this->issue->resolution = $act[1];
+ $this->issue->close();
+ $_POST['estimated'] = $this->issue->estimated;
+ break;
+
+ case 'change':
+ $this->issue->status = $act[1];
+ break;
+ }
+ }
+
+ $fields = array(
+ 'summary',
+ 'description',
+ 'classification',
+ 'priority',
+ 'severity',
+ 'changelog',
+ 'owner',
+ 'cc',
+ );
+
+ $this->issue->applyPOSTData($_POST);
+
+
+
+ foreach ($fields as $fieldname) {
+ if (isset($_POST[$fieldname]) && strlen($_POST[$fieldname])) {
+ $this->issue->$fieldname = $_POST[$fieldname];
+ } else {
+ $this->issue->$fieldname = null;
+ }
+ }
+
+ $kw = $this->issue->getKeywords();
+ $kill = array_values($kw);
+ foreach (preg_split('/[ \t,]+/', $_POST['keywords']) as $w) {
+ if (!strlen($w)) {
+ continue;
+ }
+ $x = array_search($w, $kw);
+ if ($x === false) {
+ $k = MTrackKeyword::loadByWord($w);
+ if ($k === null) {
+ $k = new MTrackKeyword;
+ $k->keyword = $w;
+ $k->save($CS);
+ }
+ $this->issue->assocKeyword($k);
+ } else {
+ $w = array_search($w, $kill);
+ if ($w !== false) {
+ unset($kill[$w]);
+ }
+ }
+ }
+ foreach ($kill as $w) {
+ $this->issue->dissocKeyword($w);
+ }
+
+ $ms = $this->issue->getMilestones();
+ $kill = $ms;
+ if (isset($_POST['milestone']) && is_array($_POST['milestone'])) {
+ foreach ($_POST['milestone'] as $mid) {
+ $this->issue->assocMilestone($mid);
+ unset($kill[$mid]);
+ }
+ }
+ foreach ($kill as $mid) {
+ $this->issue->dissocMilestone($mid);
+ }
+
+ $ms = $this->issue->getComponents();
+ $kill = $ms;
+ if (isset($_POST['component']) && is_array($_POST['component'])) {
+ foreach ($_POST['component'] as $mid) {
+ $this->issue->assocComponent($mid);
+ unset($kill[$mid]);
+ }
+ }
+ foreach ($kill as $mid) {
+ $this->issue->dissocComponent($mid);
+ }
+
+ if (!empty($_POST['comment'])) {
+ $this->issue->addComment($_POST['comment']);
+ }
+
+ $this->issue->addEffort(
+ empty($_POST['spent']) ? 0 : $_POST['spent'],
+ empty($_POST['estimate']) ? 0 : $_POST['estimate']
+ );
+
+ if (!count($this->error)) {
+ try {
+ $this->issue->save($CS);
+
+ // make sure everyone is watching it!!!!
+ if($this->issue->owner && $this->issue->tid) {
+ // make sure owner is tracking it...
+ MTrackWatch::watch_object('ticket', $this->issue->tid, $this->issue->owner);
+ }
+
+ if ($this->id == 'new') {
+ MTrackWatch::watch_object('ticket', $this->issue->tid, MTrackAuth::whoami());
+ }
+
+
+ $CS->setObject("ticket:" . $this->issue->tid);
+ } catch (Exception $e) {
+ $this->error[] = $e->getMessage();
+ }
+ }
+
+ if (!count($this->error)) {
+ if (!empty($_FILES['attachments'])) {
+ require_once 'MTrack/Attachment.php';
+ foreach ($_FILES['attachments']['name'] as $fileid => $name) {
+
+ MTrackAttachment::add("ticket:{$this->issue->tid}",
+ $_FILES['attachments']['tmp_name'][$fileid],
+ $_FILES['attachments']['name'][$fileid],
+ $CS
+ );
+ }
+ }
+ }
+ if (!count($this->error) && $this->id != 'new') {
+ require_once 'MTrack/Attachment.php';
+ MTrackAttachment::process_delete("ticket:{$this->issue->tid}", $CS);
+ }
+
+ if (isset($_POST['apply']) && !count($this->error)) {
+ $CS->commit();
+ header("Location: {$this->baseURL}/Ticket/{$this->issue->nsident}");
+ exit;
+ }
+ }
+
+
+ function initEditForm($params = array())
+ {
+ require_once 'HTML/Template/Flexy/Element.php';
+ require_once 'HTML/Template/Flexy/Factory.php';
+ $this->elements = array();
+
+
+
+ foreach(array( 'classification', 'priority', 'severity', 'resolution' ) as $c) {
+ $d = DB_DataObject::factory('core_enum');
+ $d->etype = $c;
+ $d->orderBy('seqid ASC, name ASC');
+ if (!$d->count()) {
+ $d->createBaseEntries();
+
+ }
+ $this->elements[$c] = new HTML_Template_Flexy_Element('select');
+ $this->elements[$c]->setOptions($d->fetchAll('id','name'));
+
+ }
+
+ if ($this->issue->project_id) {
+ $d = DB_DataObject::factory('mtrack_project_component');
+ $d->project_id = $this->issue->project_id;
+ $d->orderBy('name');
+ $d->whereAdd('deleted != 1');
+ $this->elements['component[]'] = new HTML_Template_Flexy_Element('select');
+ $this->elements['component[]']->setOptions($d->fetchAll('id', 'name'));
+ $ar = $this->issue->components();
+ $this->elements['component[]']->setValue(array_keys($ar));
+
+ }
+ if ($this->issue->project_id) {
+ $d = DB_DataObject::factory('mtrack_milestone');
+ $d->project_id = $this->issue->project_id;
+ $d->orderBy('(case when duedate is null then 1 else 0 end), duedate, name');
+ $d->whereAdd('completed != 1');
+ $d->whereAdd('deleted != 1');
+ $this->elements['milestone[]'] = new HTML_Template_Flexy_Element('select');
+ $this->elements['milestone[]']->setOptions($d->fetchAll('id', 'name'));
+ $ar = $this->issue->milestones();
+ $this->elements['milestone[]']->setValue(array_keys($ar));
+ }
+
+ // FIX ME - need to determine who the owner is..
+ // for a new issue it's the person who created it.
+ // later on it's an assignement???
+
+ $users = array();
+
+ $this->elements['owner'] = new HTML_Template_Flexy_Element('select');
+ $this->elements['owner']->setOptions($users);
+
+
+
+ // keywords -- in toArray...
+ // milestone
+
+
+ $this->change_status = array();
+ $this->resolve_status = array();
+ if ($this->id) {
+
+ // for coder's they can only change this ticke to certian states
+
+ //print_r($groups);
+ // Nasty - I really do not like the acl code in this ...
+ require_once 'MTrack/TicketState.php';
+
+ $ST = new MTrackTicketState;
+ $ST = $ST->enumerate();
+ //print_r($ST);
+ unset($ST['closed']);
+ unset($ST[$this->issue->status]);
+
+ $this->change_status = empty($ST) ? array() : array_keys($ST);
+
+ $ac = MTrackAuth::getUserClass($this->authUser->userid);
+ //var_dump($ac);exit;
+// KLUDGE! - remove later...
+ if ($ac == 'admin') {
+
+ $this->resolve_status= array('fixed');
+ $R = new MTrackResolution;
+ $resolutions = $R->enumerate();
+ unset($resolutions['fixed']);
+
+ $this->resolve_status= array_keys($resolutions);
+ array_unshift($this->resolve_status, 'fixed');
+ // $html .= $this->mtrack_chg_status('action', 'resolve', 'Resolve as:', 'resolution', $resolutions, $this->issue );
+ }
+
+ // } else {
+ // $html .= mtrack_radio('action', 'reopen', $_POST['action']);
+ // $html .= " <label for='reopen'>Reopen ticket</label><br>\n";
+ // }
+
+ }
+ $this->elements = HTML_Template_Flexy_Factory::fromArray($this->issue->toArray(), $this->elements);
+ if (!empty($_POST)) {
+ $this->elements = HTML_Template_Flexy_Factory::fromArray($_POST, $this->elements);
+ }
+
+
+
+
+ }
+
+
+ function eq($a,$b) {
+ return $a == $b;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+
+/**
+ * funky tree nav... - only available to the newest of browsers..
+ *
+ */
+try {
+ window.onpopstate = function(ev) {
+
+ if (!ev.state || typeof(ev.state.url) == 'undefined') {
+ return;
+ }
+ MTrack.ajaxLoad(ev.state.url, false);
+};
+} catch (e) {}
+
+
+MTrack.missingHashRequest = false;
+MTrack.missingHashes = function(el,url) {
+ //console.log('finding missing hashes?');
+ var hashes = [];
+ el.find('.browse-missing-hash').each(function() {
+ hashes.push(this.id);
+ });
+ if (!hashes.length) {
+ return;
+ }
+ if (MTrack.missingHashRequest) {
+ MTrack.missingHashRequest.abort();
+ }
+ MTrack.missingHashRequest = jQuery.ajax({
+ type : 'POST',
+ url : baseURL + url,
+ data : { hashes : hashes.join(',') },
+ success : function(data) {
+ //console.log(data.data);
+ if (data.data) {
+ for(var hash in data.data) {
+ var o = data.data[hash];
+ $('#'+ hash).html(o.changeby + ' : ' + o.changelog);
+ $('#age-'+ hash).html(o.age); // do magic replacement!!!
+ $('#rev-'+ hash).html(o.rev); // do magic replacement!!!
+ MTrack.addHandlers($('#age-'+ hash));
+ MTrack.missingHashRequest = false;
+ }
+
+ }
+ // console.log(data)
+ }
+ });
+
+};
+
+
+MTrack.ajaxLoad = function(url,slideleft) {
+
+ var target = $("#ajaxbody");
+ var content = $("#content");
+ target.css('position', 'relative');
+
+ $('.mask').show(); // must show first?
+ $('.mask-loading').show();
+ // content has a border...
+ var t = content.offset().top - $(document).scrollTop() - 13;
+ var l = content.offset().left - $(document).scrollLeft() - 13;
+ var w = content.width() + 26;
+ var h = content.height() + 26;
+
+ $('.mask').offset( { top: t, left: l});
+ $('.mask').width( w );
+ $('.mask').height( h );
+
+ $('.mask-loading').offset( {
+ top: t + 16,
+ left: l + ( w / 2) - 16
+ });
+
+ if (MTrack.missingHashRequest) {
+ MTrack.missingHashRequest.abort();
+ MTrack.missingHashRequest = false;
+ }
+ $('#loader').hide();
+ jQuery.ajax( {
+ url : baseURL + url,
+ data : { ajax_body : 1 },
+ success : function(data) {
+
+ $('.mask').hide();
+ $('#loader').html(data);
+ $('#loader').show();
+ $('#loader').width( w );
+ if (slideleft) {
+ $('#loader').offset({ top: t , left: l+w });
+ } else {
+ $('#loader').offset({ top: t , left: l-w });
+ }
+
+ target.animate( { left: slideleft ? -1 * w : w }, 500, function() {
+ target.css( { left: 0, top: 0}); // reset it at end of animation - as it ends up with the new content.
+ });
+
+ $('#loader').animate( { left: l+13 }, 500, function () {
+ target.html( $('#loader').html());
+ $('#loader').hide();
+ $('#loader').html('');
+ target.show();// make sure!!
+
+ MTrack.missingHashes(target, url);
+ MTrack.addHandlers(target);
+ });
+ }
+ //console.log('loaded dif');
+
+
+ });
+
+ return false;
+
+}
+
+
+MTrack.register('a.browse-link', 'click', function(event)
+{
+ event.preventDefault();
+
+ var href= this.getAttribute('href').substring(baseURL.length);
+ try {
+ window.history.pushState( { url: href }, "Browse : " + href , this.href );
+ } catch (e) {}
+ var slideleft = $(this).is('.browse-link-up') ? 0 : 1;
+ MTrack.ajaxLoad(href,slideleft);
+});
--- /dev/null
+//<script type="text/javascript">
+/*
+
+ticket code..
+
+basically works like this.
+
+The edit page has a number of sections
+'viewblock'
+.mtrack-view-editbar << edit bar for view.
+*/
+MTrack.register('.mtrack-ticket-edit', 'each', function(el)
+{
+
+});
+
+MTrack.register('.mtrack-ticket-events', 'each', function(el) {
+ // load events.
+})
+
+MTrack.register('mtrack-ticket-desc', 'each', function(el) {
+
+
+});
+var formChanged = 0;
+var id;
+var issue_tid_null;
+var viewblock;
+var view_off;
+var view_pos;
+var editblock;
+var edit_off;
+var edit_pos;
+var commentblock;
+
+// from ticket.php
+
+
+function show_comment_form()
+{
+
+
+ // hide stuff first.
+ //viewblock.css('position', view_pos);
+ //viewblock.css('top', view_off.top);
+ $("#comment-submit-buttons").hide();
+ $(".mtrack-make-comment").hide();
+ viewblock.hide();
+
+ // show stuff next.
+
+ $("#update-issue-desc").show()
+ $("#comment-container").show();
+ // what's this about...
+ $("#edit-comment-parent").append($("#comment-area"));
+ $("#comment-area").show();
+
+
+
+
+ editblock.show();
+ //edit_off = editblock.offset();
+ //edit_pos = editblock.css('position');
+ $("#description").focus();
+
+
+ //compute_floats();
+}
+function show_edit_form()
+{
+ //view_off = viewblock.offset();
+ //view_pos = viewblock.css('position');
+ //
+ //if (view_off) {
+ // viewblock.css('position', view_pos);
+ // viewblock.css('top', view_off.top);
+ //}
+
+ $("#issue-desc").hide();
+ $("#comment-submit-buttons").hide();
+ $(".mtrack-make-comment").hide();
+ $("#comment-container").hide();
+ viewblock.hide();
+
+ $("#update-issue-desc").show()
+ $("#edit-issue-desc").show();
+ //$("#edit-comment-parent").append($("#comment-area"));
+
+ //$("#comment-area").show();
+
+ $("#description").focus();
+
+ editblock.show();
+ //edit_off = editblock.offset();
+ //edit_pos = editblock.css('position');
+
+
+
+ //compute_floats();
+}
+function cancel_form_changes()
+{
+ if (formChanged) {
+ document.location.href = document.location.href;
+ return false;
+ }
+
+ //editblock.css('position', edit_pos);
+ //editblock.css('top', edit_off.top);
+ editblock.hide();
+ viewblock.show();
+
+ $("#tktedit").each(function(){
+ // reset form
+ this.reset();
+ });
+ // notify asm select of change
+ $("select[multiple]").change();
+ $("#edit-issue-desc").hide();
+ $("#update-issue-desc").hide()
+ $("#original-comment-parent").append($("#comment-area"));
+ $("#comment-submit-buttons").show();
+
+ if (id != 'new') {
+ $("#comment-area").hide();
+ $(".mtrack-make-comment").show();
+ }
+ $("#issue-desc").show();
+ formChanged = false;
+ //compute_floats();
+
+ return false;
+}
+/*
+
+function compute_floats() // disabled at present..
+{
+ return;
+ if ($(viewblock).is(':visible')) {
+ view_off = viewblock.offset();
+ if ($(this).scrollTop() > view_off.top) {
+ viewblock.css('position', 'fixed');
+ viewblock.css('top', '0px');
+ viewblock.addClass('button-float-floating');
+ } else {
+ viewblock.css('position', view_pos);
+ viewblock.css('top', view_off.top);
+ viewblock.removeClass('button-float-floating');
+ }
+ }
+ if ($(editblock).is(':visible')) {
+ edit_off = editblock.offset();
+ if ($(this).scrollTop() > edit_off.top) {
+ editblock.css('position', 'fixed');
+ editblock.css('top', '0px');
+ editblock.addClass('button-float-floating');
+ } else if ($(this).scrollTop() < edit_off.top + editblock.height() - $(this).height()) {
+ editblock.css('position', 'fixed');
+ editblock.css('top', $(this).height() - editblock.outerHeight());
+ editblock.addClass('button-float-floating');
+ } else {
+ editblock.css('position', edit_pos);
+ editblock.css('top', edit_off.top);
+ editblock.removeClass('button-float-floating');
+ }
+ }
+}
+*/
+
+var mtrack_ticked_loaded = false;
+$(document).ready(function()
+{
+
+ if (mtrack_ticked_loaded) {
+ return;
+ }
+ mtrack_ticked_loaded = true;
+
+ viewblock = $('#tkt-view-button-block');
+ editblock = $('#tkt-edit-button-block');
+
+ if (typeof(tid) == 'undefined') {
+ return; // not a ticket page..
+ }
+
+
+
+ if (!tid){
+ show_edit_form();
+ $('#tkt-edit-button-block .mtrack-edit-cancel').hide();
+ }
+ // $(window).scroll(function () {
+ // compute_floats();
+ // });
+
+
+ if (tid) {
+ // load the events..
+ $('#events-list').load( baseURL + '/Events/' + tid, function() {
+ $('abbr.timeinterval').timeago();
+ $('.ticketchangeinfo').hide();
+
+ $('.ticketevent-expand').click(function() {
+ var n = this.id.split('-').pop();
+ var target = "[id='ticketchangeinfo-" + n + "']";
+ var loadit = $(target).is(":hidden") && !$(target)[0].getAttribute('loaded');
+
+
+ $(target).toggle(100);
+
+
+ if (loadit && n.match(/\./)) {
+
+ $(target)[0].innerHTML = ' Loading...';
+
+ $(target)[0].setAttribute('loaded', 'true');
+ $(target).load( baseURL + '/Changeset/' + this.title + '?nowrap=1');
+ }
+
+
+ })
+
+ }) ;
+
+ // load the watchers..
+ new mtrack.watch({
+ objname : 'ticket',
+ objid : tid,
+ domid : 'watch-list'
+ });
+
+ }
+
+ $(".mtrack-edit-desc").click(
+ function() {
+ show_edit_form();
+ return false;
+ }
+ );
+
+
+ $("input[type=radio]").click(
+ function() {
+ if (this.value == 'fixed') {
+ $("#changelog-container").show();
+ } else {
+ $("#changelog-container").hide();
+ }
+ }
+ );
+
+ $(":input").change(function() {
+ formChanged = true;
+ });
+
+
+ $("textarea").keyup(function() {
+ // This is here because IE doesn't seem to reliably trigger the
+ // change event with textareas
+ formChanged = true;
+ });
+
+ // dialogs..
+
+ $("#confirmCancelDialog").dialog({
+ autoOpen: false,
+ bgiframe: true,
+ resizable: false,
+ modal: true,
+ buttons: {
+ 'Discard': function() {
+ $(this).dialog('close');
+ cancel_form_changes();
+ },
+ 'Keep': function() {
+ $(this).dialog('close');
+ }
+ }
+ });
+
+ $("#noCommentDialog").dialog({
+ autoOpen: false,
+ bgiframe: true,
+ resizable: false,
+ modal: true,
+ buttons: {
+ 'OK': function() {
+ $(this).dialog('close');
+ $("#comment").focus();
+ }
+ }
+ });
+ $("#noSummaryDialog").dialog({
+ autoOpen: false,
+ bgiframe: true,
+ resizable: false,
+ modal: true,
+ buttons: {
+ 'OK': function() {
+ $(this).dialog('close');
+ $("#summary").focus();
+ }
+ }
+ });
+
+
+ $(".mtrack-edit-cancel").click(
+ function() {
+ if (formChanged) {
+ $("#confirmCancelDialog").dialog('open');
+ return false;
+ } else {
+ return cancel_form_changes();
+ }
+ }
+ );
+
+
+ $(".mtrack-make-comment").click(
+ function() {
+ show_comment_form();
+ $("#comment").focus();
+ return false;
+ }
+ );
+
+ $(".mtrack-button-submit").click(function(){
+
+
+ if ($("#summary").val() == '') {
+
+ $("#summary").addClass('error');
+ $("#noSummaryDialog").dialog('open');
+ return false;
+
+ } else {
+
+ if (formChanged == false && $("#comment").val() == '') {
+ $("#comment").addClass('error');
+ $("#noCommentDialog").dialog('open');
+ return false;
+ }
+
+ }
+
+ });
+
+ $("#comment").keydown(function(){
+ $("#comment").removeClass('error');
+ });
+
+ $("#summary").keydown(function(){
+ $("#summary").removeClass('error');
+ });
+
+ if (issue_tid_null) {
+ $("#summary").focus();
+
+ }
+ $('#button-show-overflow').click(function() {
+ $('#show-overflow').hide('blind');
+ $('#ticketcommentsoverflow').show('clip');
+ return false;
+ });
+});
--- /dev/null
+<?php
+
+// this will be generate eventually by the 'setup.php' script..
+
+
+ini_set('include_path',
+ dirname(__FILE__) . PATH_SEPARATOR .
+ dirname(__FILE__) . '/pear');
+
+require_once 'HTML/FlexyFramework.php';
+
+
+define('DB_DATAOBJECT_NO_OVERLOAD',true);
+
+ini_set('display_errors', true);
+
+new HTML_FlexyFramework(array(
+
+ 'project' => 'MTrackWeb',
+ 'enable' => 'MTrack,Core,Admin', // enable permissions from Pman
+ 'database' => 'mysql://root:@localhost/mtrack',
+ 'DB_DataObject' => array(
+
+
+ 'class_location' => implode(PATH_SEPARATOR, array(
+ dirname(__FILE__). '/Pman/MTrack/DataObjects',
+ dirname(__FILE__). '/Pman/Core/DataObjects',
+
+ )),
+ 'class_prefix' => 'Pman_MTrack_DataObjects_:Pman_Core_DataObjects_:',
+
+ 'ini_mtrack' => implode(PATH_SEPARATOR, array(
+ dirname(__FILE__). '/PmanMTrack/DataObjects/pman.ini',
+ dirname(__FILE__). '/Pman/Core/DataObjects/pman.ini',
+
+ )),
+ ),
+ 'MTrackWeb' => array(
+ 'working_dir' => '/var/lib/mtrack', // this needs to be writable by web user..
+ )
+
+));
+
+
+
+
+
+