3 * Table Definition for mtrack_ticket
5 class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
7 class Pman_MTrack_DataObjects_Mtrack_ticket extends DB_DataObject
10 /* the code below is auto generated do not remove the above tag */
12 public $__table = 'mtrack_ticket'; // table name
13 public $id; // int(11) not_null primary_key auto_increment
14 public $summary; // blob(-1) blob
15 public $description; // blob(-1) blob
16 public $changelog; // blob(-1) blob
17 public $created; // int(11) not_null
18 public $updated; // int(11) not_null
19 public $owner_id; // int(11) not_null
20 public $developer_id; // int(11) not_null
22 public $priority_id; // int(11) not_null
24 public $severity_id; // int(11) not_null
25 public $classification_id; // int(11) not_null
26 public $resolution_id; // int(11) not_null
27 public $cc; // blob(65535) blob
28 public $status; // int(11) not_null
29 public $estimated; // real(22)
30 public $spent; // real(22)
31 public $x_fieldname; // blob(65535) blob
35 public $act_now; // a field to flag as ready to go.
36 // we only allow 3 of these to
37 // be active at the same time..
39 /* the code above is auto generated do not remove the tag below */
41 //checkPerm('S'/'E'/'A', $authuser) - can
42 function checkPerm($perm, $au)
44 $pr = $this->project();
45 // var_dump($pr ? $pr->name : false); var_dump($perm);
47 if (($perm == 'S') && (!$pr || ($pr->code == '*PUBLIC'))) {
53 // admins with all project rights.
54 if ($au->hasPerm('Core.Projects_All', 'S')) {
58 $pd = DB_DataObject::factory('ProjectDirectory');
59 $pd->user_id = $au->id;
60 $pd->project_id = $this->project_id;
61 $pd->whereAdd("role != ''");
72 if (!$this->project_id) {
75 $p = DB_DataObject::factory('core_project');
76 $p->get($this->project_id);
85 $p = DB_DataObject::factory('core_enum');
86 $p->get($this->status);
93 if (!$this->resolution_id) {
96 $p = DB_DataObject::factory('core_enum');
97 $p->get($this->resolution_id);
102 function classification()
104 if (!$this->classification_id) {
107 $p = DB_DataObject::factory('core_enum');
108 $p->get($this->classification_id);
115 if (!$this->developer_id) {
118 $p = DB_DataObject::factory('Person');
119 $p->get($this->developer_id);
124 function applyFilters($q, $au, $roo)
126 if (isset($q['query']['ticket_change'])) {
127 return $this->ticketChangeOptions($roo,$q, $au);
130 $tn = $this->tableName();
132 // then we only show global projects..
133 // DB_DAtaObject::DebugLevel(1);
134 $this->whereAdd(" join_project_id_id.code = '*PUBLIC' ");
135 } else if ($au->hasPerm('Core.Projects_All', 'S')) {
136 // // if we are admin?? should we show all..
139 //DB_DataObject::debugLevel(1);
140 // restrict to any projects that the user is involved in..
141 $pd = DB_DataObject::factory('ProjectDirectory');
142 $pd->person_id = $au->id;
143 $pd->whereAdd("role != ''");
146 $this->whereAdd(" join_project_id_id.code = '*PUBLIC' ");
148 // they can do what they like on all projects.
153 $ar = $pd->fetchAll('project_id');
154 $this->whereAddIn("join_project_id_id.id", $ar, 'int');
168 onid = mtrack_ticket.id
170 ontable = 'mtrack_ticket'
176 if ($au && isset($q['_options_for'])) {
177 $this->optionsForProject($roo, $q['_options_for']);
178 $this->jerr("somethings missing");
184 $jira_ref = "(SELECT jira_key
187 WHERE on_table='mtrack_ticket' AND on_id = {$tn}.id LIMIT 1)";
190 if (empty($q['_distinct'])) {
191 $tn = $this->tableName();
192 $this->selectAdd("$jira_ref as jira_ref ");
197 if (!empty($q['query']['search'])) {
199 $sq = $this->escape($q['query']['search']);
201 if (is_numeric($q['query']['search'])) {
202 $cond = "OR {$tn}.id = {$q['query']['search']}";
205 {$tn}.summary like '%{$sq}%'
207 {$tn}.description like '%{$sq}%'
209 $jira_ref like '%{$sq}%'
213 if (is_numeric($q['query']['search'])) {
214 // allow quick lookup by number..
221 if (!empty($q['_developer'])) {
222 switch ($q['_developer']) {
224 $this->whereAdd("mtrack_ticket.developer_id = {$au->id} ");
232 if (!empty($q['query']['viewtype'])) {
234 switch ($q['query']['viewtype']) {
236 // hide "on hold" and closed..
237 $this->whereAdd("join_status_id.name NOT IN( 'CLOSED') OR {$tn}.status = 0");
238 $this->whereAdd("{$tn}.milestone_id = 0 OR join_milestone_id_id.on_hold = 0");
242 $this->whereAdd("join_status_id.name = 'ON_HOLD' ");
243 $this->whereAdd("{$tn}.milestone_id = 0 OR join_milestone_id_id.on_hold = 0");
247 $this->whereAdd("mtrack_ticket.developer_id = {$au->id} ");
248 $this->whereAdd("{$tn}.milestone_id = 0 OR join_milestone_id_id.on_hold = 0");
252 $this->whereAdd("join_status_id.name NOT IN('closed', 'CLOSED') OR {$tn}.status = 0 ");
253 $this->whereAdd("mtrack_ticket.developer_id = {$au->id} ");
259 $this->whereAdd("join_status_id.name ='closed'");
263 case 'active-or-recent':
265 (join_status_id.name NOT IN('closed', 'CLOSED') OR {$tn}.status = 0)
267 (join_status_id.name IN('closed', 'CLOSED') AND join_updated.changedate > NOW() - 1 MONTH )
269 $this->whereAdd("{$tn}.milestone_id = 0 OR join_milestone_id_id.on_hold = 0");
273 $this->whereAdd("join_status_id.name IN ('REVIEW_BY_ADMIN', 'REVIEW_BY_CLIENT )");
277 $this->whereAdd("join_status_id.name IN ('IN_PROGRESS' )");
281 $this->whereAdd("1=1");
285 $this->whereAdd("1=0");
293 if (!empty($q['_orderby_self_alloc '])) {
294 $auid = $au ? (int) $au->id : 0;
296 IF(developer_id = $auid , 1, 0) as is_self_alloc
298 $this->orderBy('is_self_alloc DESC');
303 if (!empty($q['query']['include_id'])) {
304 $this->whereAdd("{$tn}.id = " . ((int) $q['query']['include_id'] ), 'OR');
308 /// only show unposted..
313 function ticketChangeOptions($roo, $q, $au)
315 $et = DB_DataObject::Factory('core_enum');
317 $et->whereAddIn('etype', array(
326 // the end result of this:
331 ticketstate_[me] ????
333 ticketstate_[... everything else except close & new ...]
334 .. eg. open, reopened..
336 // these actually close the ticket...
337 // so the do not appear if the ticket is closed...
344 if (empty($q['query']['ticket_change'])) {
345 $roo->jerr("no ticket!");
348 // find out what state the ticket is in.
349 $t = DB_DataObject::Factory('mtrack_ticket');
351 if (strpos($q['query']['ticket_change'], ',') > 0) {
353 $t->status_name = 'xx';
357 $t->get($q['query']['ticket_change']);
360 if (!$et->checkPerm('S', $au)) {
361 $roo->jerr("permssion denied to query state of ticket");
366 //$this->debugLevel(1);
371 IF( id = {$t->status} , 0, 10000) +
372 IF( etype='ticketstate'
373 AND name='assigned' , 10, 0) +
374 IF( etype='ticketstate' AND
375 name !='closed' AND name != 'new'
377 name !='CLOSED' AND name != 'NEW', 100, 0) +
378 IF( etype='ticketstate' , 0 , 1000) +
387 switch($t->status_name) {
391 // do not show closed..
393 ( CONCAT(etype , '.', name) != 'ticketstate.closed')
395 ( CONCAT(etype , '.', name) != 'ticketstate.reopened')
397 ( CONCAT(etype , '.', name) != 'ticketstate.CLOSED')
404 ( CONCAT(etype , '.', name) = 'ticketstate.reopened')
406 core_enum.id = {$t->status}
413 ( CONCAT(etype , '.', name) != 'ticketstate.closed')
415 ( CONCAT(etype , '.', name) != 'ticketstate.new')
417 ( CONCAT(etype , '.', name) != 'ticketstate.NEW')
419 ( CONCAT(etype , '.', name) != 'ticketstate.open')
428 ( CONCAT(etype , '.', name) != 'ticketstate.closed')
430 ( CONCAT(etype , '.', name) != 'ticketstate.new')
432 ( CONCAT(etype , '.', name) != 'ticketstate.reopened')
439 CASE WHEN etype= 'ticketstate' THEN
440 LOWER(REPLACE(name, '_', ' '))
442 CONCAT('closed - ', LOWER(REPLACE(name, '_', ' ')))
444 CASE WHEN etype= 'ticketstate' THEN
447 CONCAT('Closed - ', display_name)
448 END AS display_name_mixed
453 $et->orderBy("ts_order ASC");
455 $roo->jdata( $et->fetchAll(false,false,'toArray'));
462 *-- return false to let system do default sort code.
465 function applySort($au, $sortcol, $direction)
468 $direction = $direction == 'DESC' ? 'DESC' : 'ASC';
470 $tn = $this->tableName();
475 $this->orderBy("$sortcol $direction");
481 when join_status_id.display_name = 'IN_PROGRESS' then 1
482 when join_status_id.display_name = 'REVIEW_BY_ADMIN' then -1
485 (case when join_milestone_id_id.duedate is null then 1 else 0 end) ASC,
486 join_milestone_id_id.duedate $direction ,
487 {$tn}.seqid $direction ,
488 join_milestone_id_id.name $direction
495 (case when join_milestone_id_id.duedate is null then 1 else 0 end) ASC,
496 join_milestone_id_id.duedate ASC ,
497 join_severity_id_id.seqid ASC,
498 join_priority_id_id.seqid ASC,
499 join_classification_id_id.seqid ASC,
500 join_updated_id.changedate DESC
506 when join_status_id.display_name = 'IN_PROGRESS' then 1
507 when join_status_id.display_name = 'REVIEW_BY_ADMIN' then -1
510 join_priority_id_id.seqid $direction, {$tn}.seqid ASC
514 // this should favour in_progress, and disfavour resolved
518 when join_status_id.display_name = 'IN_PROGRESS' then 1
519 when join_status_id.display_name = 'REVIEW_BY_ADMIN' then -1
522 join_priority_id_id.seqid ASC,
535 (case when join_milestone_id_id.duedate is null then 1 else 0 end),
536 join_milestone_id_id.duedate ASC ,
537 join_severity_id_id.seqid ASC,
538 join_priority_id_id.seqid ASC,
539 join_classification_id_id.seqid ASC,
540 join_updated_id.changedate DESC
554 switch ($this->status) {
563 function descriptionToHtml() /* depreciated */
565 // return MTrack_Wiki::format_to_html($this->description);
566 return htmlspecialchars($this->description);
571 function toEmailCalcAddress($changes, $person)
573 $ff = HTML_FlexyFramework::get();
576 $headers['To'] = '"'. addslashes($person->name). '" <' . $person->email .'>';
577 $headers['From'] = $ff->MTrackWeb['email_address']; // could be the user who made the change...
579 //if (count($from) > 1) {
581 // array_shift($from);
582 // foreach ($from as $email) {
583 // $rep[] = make_email($email[0], $email[1]);
586 $headers['Reply-To'] = $ff->MTrackWeb['email_address'];
592 $i = DB_DataObject::factory('Images');
593 return $i->gather($this);
596 function toEventName()
598 return $this->summary;
603 function toEmail($person, $lasttime)
605 $ff = HTML_FlexyFramework::get(); // used to work out url to show..
607 $CS = DB_DataObject::factory('mtrack_change');
608 $changes = $CS->changesSince($this,$lasttime);
609 $headers = $this->toEmailCalcAddress($changes, $person);
612 'MIME-Version' => '1.0',
613 'Content-Type' => 'text/plain; charset="UTF-8"',
614 'Content-Transfer-Encoding' => 'quoted-printable',
618 $mid = $this->id . '@' . php_uname('n');
620 $p = $this->project();
622 $subj = "[" . $p->code . ' - ' . $p->name . "] ";
624 $headers['X-mtrack-project-list'] = $p->code;
627 $headers['Subject'] = sprintf("%s#%s %s (%s %s)",
628 $subj, $this->id, $this->summary, $this->status_name, $this->classification_id_name);
630 $owner = $this->owner_id ? $this->owner_id_name : 'nobody';
633 sprintf("%s/Ticket/%s\n\n", $ff->MTrackWeb['url'], $this->id) .
634 sprintf("#%s: %s (%s %s)\n", $this->id, $this->summary, $this->status_name, $this->classification_id_name) .
635 sprintf("Responsible: %s (%s / %s)\n", $owner, $this->priority_id_name, $this->severity_id_name) .
637 sprintf("\nDescription:\n%s\n", wordwrap($this->description,80, "\n")) .
638 //sprintf("Milestone: %s\n", join(', ', $T->getMilestones()));
639 //sprintf("Component: %s\n", join(', ', $T->getComponents()));
642 // Display changed fields grouped by the person that last changed them
643 //$who_changed = array();
644 //foreach ($field_changers as $field => $who) {
645 // $who_changed[$who][] = $field;
648 foreach ($changes as $change) {
650 $body .= "\n{$change->changedate}: Change by {$change->person_id_name}\n";
651 $body .= str_repeat('-', 80) . "\n";
652 if (!empty($change->reason)) {
653 $body.= wordwrap($change->reason, 80, "\n") ."\n\n";
655 $ar = $change->cachedAudit();
657 foreach($ar as $audit) {
659 switch($audit->field()) {
661 continue 2; //??? ignore?
674 if ($audit->oldvalue == $audit->value) {
675 continue 2; // no change?!?!?
677 $oldvalue = $audit->oldvalue($change);
678 $value = $audit->value($change);
679 $field = $audit->field();
681 $field = preg_replace('/_id$/', '', $field);
682 $field = str_replace('/_/', ' ', $field);
683 $lb = strpos($oldvalue,"\n") > -1 || strpos($value,"\n") > -1 ? "\n" : '';
685 if (!strlen($oldvalue)) {
686 $body .= "Set {$field} to: {$lb}{$value}\n";
690 if (!strlen($value)) {
691 $body .= "Removed {$field} - was: {$lb}{$oldvalue}\n";
694 $body .= "Changed {$field} from : {$lb}{$oldvalue} {$lb}-> {$lb}{$value}\n";
700 $body .= sprintf("\n\n%s/Ticket/%s\n\n", $ff->MTrackWeb['url'], $this->id);
704 'headers' => $headers
710 function toEventString()
712 return '#'.$this->id . ' ' . $this->summary;
716 var $_mtrack_change = false;
718 function beforeUpdate($old, $req, $roo)
721 if (!empty($req['reason'])) {
722 $ch = DB_DataObject::factory('mtrack_change');
723 $ch->beginChange($this, $req['reason']);
724 $this->_mtrack_change= $ch;
726 // used by gitlive close ticket...
727 if (!empty($req['status_name']) && empty($req['status'])) {
728 if ($req['status_name'] == 'resolved') {
729 $req['status_name'] = 'REVIEW_BY_ADMIN';
730 // if it's an admin doing it.. then set it to fixed?
732 $n = DB_DataObject::Factory('core_enum');
733 $n->whereAddIn('name', array( $req['status_name'], strtoupper($req['status_name'])), 'string');
734 $n->whereAddIn('etype', array('ticketstate','resolution'),'string');
735 if (!$n->find(true)) {
736 $roo->jerr("invalid status name");
738 $this->status = $n->id;
741 if ($old->status != $this->status) {
742 // status has been changed. - check to see if it's really a resolution.
743 $n = DB_DataObject::Factory('core_enum');
744 if (!$this->status || !$n->get($this->status)) {
745 $roo->jerr("status set invalid");
747 if ($n->etype != 'ticketstate') {
748 if ($n->etype != 'resolution') {
749 $roo->jerr("status set invalid");
751 $this->resolution_id = $n->id;
752 $n = DB_DataObject::Factory('core_enum');
753 $n->etype = 'ticketstate';
754 $n->get('name', 'closed'); // no error checking!?!
755 $this->status = $n->id;
763 if (isset($req['_reorder'])) {
764 $old->updateSequence($req['_reorder']);
765 $roo->jok("UPDATED");
768 if (isset($req['_before']) || isset($req['_after'])) {
769 $req['_after'] = isset($req['_after']) ? $req['_after'] : 0;
770 $req['_before'] = isset($req['_before']) ? $req['_before'] : 0;
771 $old->updateSequence($req['_before'],$req['_after']);
774 if ($req['_before']) {
775 $before = DB_DataObject::factory('mtrack_ticket');
776 $before->get($req['_before']);
777 $this->seqid = $before->seqid;
778 } else if ($req['_after']) {
779 $after = DB_DataObject::factory('mtrack_ticket');
780 $after->get($req['_after']);
781 $this->seqid = $after->seqid+1;
793 function beforeInsert($req, $roo)
796 if (isset($req['_bulk_update'])) {
797 $this->bulkUpdate($req,$roo);
801 if (!$this->status) {
802 $n = DB_DataObject::Factory('core_enum');
803 $n->etype = 'ticketstate';
804 $n->get('name', 'IN_PROGRESS');
805 $this->status = $n->id;
810 function bulkUpdate($req, $roo)
812 if (empty($req['_bulk_update'])) {
813 $roo->jerr("invalid tickets");
816 $t = DB_DAtaObject::Factory('mtrack_ticket');
818 $t->whereAddIn($this->tableName().'.id', explode(',',$req['_bulk_update'] ), 'int');
819 $all = $t->fetchAll();
821 foreach($req as $k=>$v) {
827 foreach($all as $t) {
830 $t->beforeUpdate($old, $cg, $roo);
832 $t->onUpdate($old,$req, $roo);
835 $t = DB_DAtaObject::Factory('mtrack_ticket');
837 $t->whereAddIn($this->tableName().'.id', explode(',',$req['_bulk_update'] ), 'int');
838 $roo->jok($t->fetchAll(false,false,'toArray'));
849 function onUpdate($old,$req, $roo)
851 //print_r($old); print_r($this);
854 $ch = $this->_mtrack_change;
856 $ch = DB_DataObject::factory('mtrack_change');
857 $ch->beginChange($this, "Changed");
858 $this->_mtrack_change = $ch;
861 $n = $ch->add($this, $old);
862 if (!$n && !$this->_mtrack_change) {
863 //die("deleting change");
864 $ch->delete(); // do not record the change..
869 $this->updated = $ch->id;
874 if (isset($req['uploads'])) {
875 $this->updateUploads(json_decode($req['uploads']),$roo);
884 function onInsert($req,$roo, $even)
886 $ch = $this->_mtrack_change;
888 $ch = DB_DataObject::factory('mtrack_change');
889 $ch->beginChange($this, "Changed");
890 $this->_mtrack_change = $ch;
892 $ch->add($this, false);
893 if (isset($req['uploads'])) {
894 $this->updateUploads(json_decode($req['uploads']), $roo);
896 // in theory this should trigger a timesheet log that we have started working on something.
902 function updateUploads($uploads, $roo)
904 foreach($uploads as $up) {
906 if (empty($up->id) || $up->id < 0) {
907 if ($up->is_deleted) {
912 $i = DB_DAtaObject::factory('Images');
915 'mimetype' => $up->mimetype,
916 'title' => $up->title,
917 'filename' => $up->title,
918 'created' => date('Y-m-d H:i:s')
920 $i->createFromData($up->srcdata);
924 $i = DB_DAtaObject::factory('Images');
926 if (!$i->get($up->id)) {
927 $roo->jerr("invalid update id for image {$up->id}");
929 if ($up->is_deleted) {
931 $i->beforeDelete(array(), $roo);
943 $ch = DB_DataObject::factory('mtrack_change');
944 $ch->get($this->updated ? $this->updated : $this->created);
950 function toRooSingleArray($au, $req)
953 $ret = $this->toArray();;
954 $ret['images'] = array();
955 foreach($this->images() as $i) {
956 $ret['images'][] = $i->toRooArray(array());
962 $p = $this->factory('core_enum');
963 $p->etype='priority';
964 $p->get('name', 'normal');
966 $s = $this->factory('core_enum');
967 $s->etype='severity';
968 $s->get('name', 'normal');
970 $c = $this->factory('core_enum');
971 $c->etype='classification';
972 $c->get('name', 'enhancement');
974 $this->severity_id = $s->id;
975 $this->classification_id = $c->id;
976 $this->priority_id = $p->id;
977 $ret = $this->toArray();
978 $ret = array_merge($ret, $p->toArray('priority_id_%s'));
979 $ret = array_merge($ret, $c->toArray('classification_id_%s'));
980 $ret = array_merge($ret, $s->toArray('severity_id_%s'));
987 // http://roojs.com/admin.php/Roo/Mtrack_ticket?_post=1&id=733&_reorder=0
988 function updateSequence($tbefore, $tafter = 0)
990 // fix empty sequences?
992 $tn = $this->tableName();
993 $t = DB_DataObject::factory('mtrack_ticket');
996 //$t->project_id = $this->project_id;
999 // we still apply to closed tickets????
1000 $t->whereAddIn('join_status_id.name', array( 'CLOSED'), 'string');
1001 $t->applySort(false, 'old_style', false);
1003 $old_seqid = $this->seqid;
1006 // add a seq id to everything.
1007 $t->selectAdd("{$tn}.id as id");
1008 $ids = $t->fetchAll('id');
1010 $t_all = DB_DataObject::factory('mtrack_ticket');
1011 $t_all->selectAdd('max(seqid) as seqid');
1014 $start = empty($t_all->seqid) ? 1 : $t_all->seqid;
1016 foreach($ids as $i => $id) {
1017 $tt->query("UPDATE {$tn} SET seqid = " . ($start + $i) ." WHERE id = {$id}");
1019 $t = DB_DataObject::Factory('mtrack_ticket');
1021 $old_seqid = $t->seqid;
1031 $before = DB_DataObject::factory('mtrack_ticket');
1032 $before->get($tbefore);
1033 $before_seq_id = $before->seqid;
1037 $after = DB_DataObject::factory('mtrack_ticket');
1038 $after->get($tafter);
1039 $before_seq_id = $after->seqid+1;
1042 $t = DB_DataObject::factory('mtrack_ticket');
1045 /// move everything down.
1046 $t->query("UPDATE {$tn} SET
1049 seqid > {$before_seq_id}
1054 // puts this one in the gap..
1055 $t->query("UPDATE {$tn} SET seqid = ". ( $before_seq_id + 1) . " WHERE id = {$this->id}");
1057 // now there is a gap where this used to be..
1060 $t->query("UPDATE {$tn} SET
1063 seqid > {$old_seqid}
1068 function toRooArray($q) {
1069 if (empty($q['_future_schedule'])) {
1070 return $this->toArray();
1072 if (!$this->developer_id) {
1075 $add = $this->toCalEvent();
1076 if (isset($q['filter'])) {
1077 foreach($q['filter'] as $k=>$v) {
1081 if (!isset($add[$k])) {
1082 $roo->jerr('invalid filter ' .$k);
1084 if ($v != $add[$k]) {
1095 // only run this once..
1096 function toCalEvent()
1098 //secho '<PRE>';print_r($this);exit;
1099 static $cdata = array();
1102 if (empty($cdata[$this->developer_id])) {
1103 $start = $this->availStart(date('Y-m-d H:i:s'));
1105 $start = $this->availStart($cdata[$this->developer_id]);
1108 $end = $this->availEnd($start, $this->estimated ? $this->estimated : 1);
1109 $cdata[$this->developer_id]= $end;
1111 $ret = array_merge($this->toArray(), array(
1112 'start_dt' => $start,
1115 'title' => '#' . $this->id . ' ' .$this->developer_id_name . ' : ' . $this->project_id_name,
1116 'description' => $this->summary,
1124 function availStart($dt)
1126 //give a date time.. see if we should schedule it today?
1127 $st = strtotime($dt);
1129 if (date('H', $st) < 11) {
1130 $st = strtotime(date('Y-m-d 10:00:00',$st));
1132 if (date('H', $st) > 17) {
1133 if(date('N',$st) > 4) {
1134 // it's friday... (5) +3 , sat (6) +2 sun(7) +1
1135 $days = 8 - date('N',$st);
1136 $st = strtotime(date('Y-m-d',$st).' + ' . $days . ' DAY');
1139 $st = strtotime(date('Y-m-d',$st).' + 1 DAY');
1141 $st = strtotime(date('Y-m-d 10:00:00',$st));
1142 return date('Y-m-d H:i:s', $st);
1144 // otherwise this time is OK...
1147 function availEnd($start, $hours) {
1150 for ($i = 0; $i < ceil($hours ); $i++) {
1151 $et = $this->availStart(date('Y-m-d H:i:s', strtotime($et . ' + 1 HOUR')));
1157 function optionsForProject($roo,$pid)
1160 $m = DB_DataObject::factory('mtrack_milestone');
1161 $m->project_id = $pid;
1163 $m->selectAdd('id,name as name, name as display_name');
1164 $m->orderBy('startdate ASC');
1165 $ret['milestone'] = $m->fetchAll(false,false,'toArray');
1167 $mm = DB_DataObject::factory('core_enum');
1168 $mm->orderBy('seqid ASC');
1172 $m->etype = 'priority';
1173 $ret['priority'] = $m->fetchAll(false,false,'toArray');
1176 $m->etype = 'severity';
1177 $ret['severity'] = $m->fetchAll(false,false,'toArray');
1180 $m->etype = 'classification';
1181 $ret['classification'] = $m->fetchAll(false,false,'toArray');
1185 $m = DB_DataObject::factory('core_person');
1186 $m->company_id = $roo->company->id;
1188 $m->selectAdd("id,name as name, concat(name ,' <', email , '>') as display_name");
1190 $m->applyFilters(array(
1192 'project_id' => $pid,
1193 'role' => 'DEVELOPER'
1195 ),$roo->authUser, $roo);
1197 $m->orderBy('name ASC');
1198 $ret['developer'] = $m->fetchAll(false,false,'toArray');
1199 $ret['authuser_id'] = "".$roo->authUser->id; //string...