PDO fix
[Pman.MTrack] / DataObjects / Mtrack_change.php
1 <?php
2 /**
3  * Table Definition for mtrack_change
4  */
5 class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
6
7 class Pman_MTrack_DataObjects_Mtrack_change extends DB_DataObject 
8 {
9     ###START_AUTOCODE
10     /* the code below is auto generated do not remove the above tag */
11
12     public $__table = 'mtrack_change';                   // table name
13     public $id;                              // int(11)  not_null primary_key auto_increment
14     public $person_id;                       // int(11)  
15     public $ontable;                         // string(128)  
16     public $onid;                            // int(11)  not_null
17     public $changedate;                      // datetime(19)  not_null binary
18     public $reason;                          // blob(65535)  blob
19
20     
21     /* the code above is auto generated do not remove the tag below */
22     ###END_AUTOCODE
23      ###END_AUTOCODE
24      
25     function objectCached()
26     {
27         static $cache;
28         if (empty($this->ontable) || empty($this->onid)) {
29             return false;
30         }
31         
32         if (isset($cache[$this->ontable.':'. $this->onid])) {
33             $cache[$this->ontable.':'. $this->onid];
34         }
35         $o = DB_DataObject::factory($this->ontable);
36         $o->get($this->onid);
37         $cache[$this->ontable.':'. $this->onid] = $o;
38         return $o;
39         
40      
41     }
42     
43      //checkPerm('S'/'E'/'A', $authuser) - can
44     function checkPerm($perm, $au)
45     {
46         
47         if ($au && $au->company()->comptype == 'OWNER') {
48             // owner can do anything..????
49             return true;
50         }
51         
52         if ($perm == 'E' || $perm == 'D' ) { // Edit and delete ..not allowed..
53             return false;
54         }
55         
56         if (!$au && $perm != 'S') {
57             // non-authenticated users can only list stuff..
58             return false;
59             
60         }
61         
62         //DB_DataObject::debugLevel(1);
63         $obj = $this->objectCached();
64         if (!$obj) {
65             return true;
66         }
67         if (!method_exists($obj, 'checkPerm')) {
68             return false;
69         }
70         
71         
72         return $obj->checkPerm($perm, $au);
73         
74          
75     }
76     
77     
78      
79     function applyFilters($q, $au, $roo)
80     {
81         
82         if (!empty($q['timeline'])) {
83             return $this->applyFilterTimeline($q,$au, $roo);
84         }
85         $this->setFrom($q);
86         $obj = $this->objectCached();
87         
88         // global searching on non-object...
89         // needed for timelime, but not ready yet...
90         
91         if (!$obj) {
92             $this->whereAdd('1=0');
93             return;
94            
95         }
96         if (!method_exists($obj, 'checkPerm')) {
97             $this->whereAdd('1=0');
98             return;
99         }
100         
101         if (!$au && !$obj->checkPerm('S', $au)) {
102             $this->whereAdd('1=0');
103             return;
104         }    
105         
106         
107         
108            
109     }
110     // this is the query for timelines..
111     function applyFilterTimeline($q, $au, $roo)
112     { 
113         
114         
115         $start = empty($q['on_date']) ? date('Y-m-d') : 
116             date('Y-m-d', strtotime($q['on_date']));
117
118         
119         //DB_DataObject::debugLevel(1);
120         
121         
122         $pd = DB_DataObject::factory('ProjectDirectory');
123         $pd->whereAdd("role != ''");
124         $pids = $pd->projects($au);
125         if (empty($pids)) {
126             $roo->jerr("User is not a member of any projects");
127         }
128         
129         if (!empty($q['project_id'])) {
130             if (!in_array($q['project_id'], $pids)) {
131                 $roo->jerr("Selected project not in your permitted project list.");
132             }
133             $pids = array($q['project_id']);
134         }
135         
136         $pids = implode(',', $pids);
137         
138         // add emails???
139         $this->whereAdd("
140             ( ontable='mtrack_ticket' AND
141                 onid IN (SELECT id FROM mtrack_ticket where project_id IN ( $pids ))
142             )
143             OR
144             ( ontable='mtrack_repos' AND
145                 onid IN (SELECT id FROM mtrack_repos where project_id IN ( $pids ))
146             )
147         ");
148         $this->orderBy('changedate DESC');
149  
150         if (!empty($q['viewtype']) && $q['viewtype'] == 'summary') {
151             //DB_DataObject::debugLevel(1);
152             $isSummary = true;
153             $this->whereAdd("
154                         changedate >= '$start 00:00:00'
155                         AND
156                         changedate < '$start 00:00:00' + INTERVAL 1 DAY
157             ");
158             
159             
160             $this->selectAdd();
161             //$this->joinAdd(DB_DataObject::factory('Person'), 'LEFT');
162             $this->selectAdd("
163                      DATE_FORMAT(changedate, '%Y-%m-%d')  as changeday,
164                      join_person_id_id.id   as person_id,
165                      join_person_id_id.name as person_name,
166                      Projects.id as  project_id,
167                      mtrack_repos.shortname as repo_name,
168                      mtrack_repos.id as repo_id,
169
170                      Projects.code  as project_code,
171                         
172                      COUNT(mtrack_change.id) as nchanges
173                     ");
174             $this->_join .= "
175                 LEFT JOIN Projects ON
176                    Projects.id = IF (ontable='mtrack_repos',
177                        (SELECT project_id FROM mtrack_repos where onid = mtrack_repos.id ),
178                        (SELECT project_id FROM mtrack_ticket where onid = mtrack_ticket.id )
179                      )
180                 LEFT JOIN mtrack_repos ON
181                    mtrack_repos.id = IF (ontable='mtrack_repos', onid,  0)
182                 
183                 
184                 ";
185                 
186             $this->groupBy("changeday,
187                             join_person_id_id.id,
188                             join_person_id_id.name,
189                             repo_id, repo_name,
190                             mtrack_repos.id,
191                              mtrack_repos.shortname,
192                             Projects.code");
193             $this->orderBy('project_code asc, repo_name ASC, person_name asc');
194             $ret = array();
195             $roo->jdata($this->fetchAll(false,false,'toArray'));   
196         }
197         
198          
199         $this->whereAdd("changedate >= '$start_day 00:00:00' AND
200                       changedate < '$start_day 00:00:00' + INTERVAL 1 DAY");
201          
202         //DB_DataObject::debugLevel(1);
203         
204        
205     
206     
207     }
208     // do not accept use input for this..
209     function autoJoinObject($tbl)
210     {
211          
212         
213         $d = DB_DataObject::Factory($tbl);
214         $ji = $d->autoJoin();
215         //echo '<PRE>';print_R($ji);
216         // get cols
217         foreach($ji['join_names'] as $cname=>$fname) {
218              $this->selectAdd($fname . ' as ontable_id_' . $cname );
219         }
220         
221         //$this->selectAdd($d->_query['data_select']); -- this will cause the same dataIndex...
222         $this->_join .= "
223             LEFT JOIN {$d->tableName()} ON {$this->tableName()}.onid = {$d->tableName()}.id
224             {$d->_join}
225         "; 
226         $this->selectAs($d, 'ontable_%s');
227         
228         
229         
230     }
231     
232     
233     
234     
235     function person()
236     {
237         static $cache = array();
238         if (isset($cache[$this->person_id])) {
239             return $cache[$this->person_id];
240         }
241         $p = DB_DataObject::factory('core_person');
242         $p->get($this->person_id);
243         $cache[$this->person_id] = $p;
244         return $p;
245         
246     }
247     
248     function getCommit($repo, $hash, $runhistory=true) 
249     {
250         $this->object = $repo->toIdString(); // should be repo:1
251         $this->rev = $id;
252         if ($this->find(true)) {
253             $this->fetchAudit();
254             return;
255         }
256         if (!$runhistory) {
257             throw new Exception("Can not find revision");
258         }
259         $this->refreshHistory($repo);
260         if ( $this->find(true)) {
261             $this->fetchAudit();
262             return true;
263         }; /// yes I know we say you should not run this twice...
264         return false;
265     }
266     
267     /**
268      *createFromCommit:
269      *@param {TrackCommitHookChangeEvent} ce the change event..
270      */
271     function createFromCommit($ce, $checker, $ticket = false)
272     {
273         $rev = substr(array_pop(explode(',', $ce->rev)), 0, -1);
274         
275         $tc = clone($this);
276         $tc->ontable = 'mtrack_repos';
277         $tc->onid = $checker->repo->id;
278         $tc->rev= $rev;
279         $tc->branch = $ce->branch;
280         if ($tc->count()) { // if we have already tracked this..
281             return false;
282         }
283         
284         
285         
286         
287         
288         $tcid = 0;
289         
290         if ($ticket) {
291             $tc = clone($this);    
292             $tc->ontable = $ticket->tableName();
293             $tc->onid = $ticket->id;
294             $tc->changedate = date('Y-m-d H:i:s', $ce->ctime);
295             $tc->reason = $ce->changelog;
296             $tc->person_id = $ce->changeby_id;
297             $tc->rev = $rev;
298             $tcid  = $tc->insert();
299             
300         }
301         
302         
303         $this->ontable = 'mtrack_repos';
304         $this->onid = $checker->repo->id;
305         $this->changedate = date('Y-m-d H:i:s', $ce->ctime);
306         $this->reason = $ce->changelog;
307         $this->person_id = $ce->changeby_id;
308         $this->branch = $ce->branch;
309         $this->rev = $rev;
310         $this->insert();
311         
312         // add all the chanedge files..
313         foreach($ce->fileActions as $f => $cg) { 
314             
315             $au = DB_DataObject::factory('mtrack_change_audit');  
316             $au->change_id  = $this->id;
317             $au->ticket_change_id  = $tcid;
318             $au->fieldname  = $rev;
319             $au->action     = 'commit-'.$cg;
320             $au->value      = $f; 
321             $au->insert();
322         }
323         
324         $this->onInsert(array(), HTML_FlexyFramework::get()->page);
325         
326         // finnaly trigger watchers
327      
328         
329         return true;
330         
331         //print_r($this);
332         
333         // audit should contain files changed..
334     }
335     
336     function onInsert($request,$roo) 
337     { 
338         // this will send
339         // for a commit ? - ontable = repos, onid = the repo id...
340         // for a change... ontable - will be the ticked, and onid will be the ticket id..
341         
342         $obj = $this->objectCached();
343         $target = $obj;
344         if ($obj->tableName() == 'mtrack_ticket') {
345             $obj = $obj->project();
346             $target = $obj;
347         } else {
348             $target = empty($this->branch) ? $obj : $obj->branchObject($this->branch);
349         }
350         if ($obj) {
351            
352             $core_watch = DB_DataObject::Factory('core_watch');
353             $core_watch->notify(
354                     $obj->tableName(),
355                     $obj->pid(),
356                     "medium='ENDOFDAYMAIL'",
357                         // end of day..
358                     date('Y-m-d 23:59:59'),
359                     $target->tableName(),
360                     $target->pid()
361             );
362         }    
363     }
364     
365     
366     /*
367     * scans repo history and imports it... ??? does not know about branches yet...
368     */
369     function refreshHistory($repo) 
370     {
371         $c = DB_DataObject::factory('mtrack_changes');
372         $c->object =  $repo->toIdString(); 
373         $c->orderBy('changedate DESC');
374         $c->whereAdd("rev != ''");
375         $last  = '';
376         if ($c->find(true)) {
377             $last = $c->scmid;
378         }
379         // history returns an array of scmEvents... - should actually return an array of this object...
380         $recs = $repo->history('', null, $last ? 'rev' : null, $last ? $last  :null);
381         
382         foreach($recs as $r) { 
383             // see if we have a copy of it already.
384             $c = DB_DataObject::factory('mtrack_change');
385             $c->object =  $repo->toIdString(); 
386             $c->changedate != $r->ctime;
387             $cr  = clone($c);
388             $c->whereAdd("rev != ''");
389             if (!$c->find(true)) {
390                 // not found, create a new one..
391                 $c->insert();
392                 
393             }
394             $cr->rev = $r->rev;
395             $cr->reason = $r->changelog;
396             $cr->who =  $r->changeby;
397             $cr->update();
398             foreach($cr->files as $f) {
399                 // we could do with adding 'X' lines changed on this
400                 $ca = DB_DataObject::factory('change_audit');
401                 $ca->cid = $cr->id;
402                 $ca->fieldname = $cr->object. ':file';
403                 $ca->action = $f->status . ($f->added ? ' +' . $f->added : '') . ($f->removed ? ' -' . $f->removed : '') ;
404                 $ca->value = $f->name;
405                 $ca->insert();
406                 
407             }
408             
409             
410         }
411     
412     }
413      
414
415     function beginChange($object, $reason = '', $when = null)
416     {
417          // $db->beginTransaction();
418          //$this->query('BEGIN');
419          $pg = HTML_FlexyFramework::get()->page;
420          $au  = $pg->authUser;
421
422         $this->person_id = $au->id;
423         $this->ontable      = $object->tableName();                         // string(128)  
424         $this->onid = $object->id; // should use keys( really..)
425         $this->reason = $reason;
426         //$d = date_create("@" . (empty($when)  ? time() : $when), new DateTimeZone('UTC'));
427         $this->changedate =  date('Y-m-d H:i:s'); // everythign at server time..
428                                   //$d->format('Y-m-d\TH:i:s.u\Z');
429         $this->insert();
430         $this->onInsert(array(), $pg);
431          
432     }
433
434     function commit()
435     {
436         if ($this->count == 0) {
437             //      throw new Exception("no changes were made as part of this changeset");
438         }
439         if (self::$use_txn) {
440             //$this->query('COMMIT');
441           //$db->commit();
442         }
443     }
444     
445     function addentry($fieldname, $action, $old, $value = null)
446     {
447         
448         // final sanity check..
449         $o = DB_DataObject::factory($this->ontable);
450         $info = $o->tableColumns();
451         $far = explode(':',$fieldname);
452         $col = array_pop($far);
453         if (!isset($info[$col])) {
454             return 0;
455         }
456         
457         
458         if ($info[$col] & (defined('DB_DATAOBJECT_INT') ? DB_DATAOBJECT_INT : PDO_DataObject::INT)) {
459             if (((int) $old)  == ((int) $value)) {
460                 return 0;
461             }
462         }
463         
464         
465         
466         $ca = DB_DataObject::factory('mtrack_change_audit');
467         $ca->setFrom(array(
468             'change_id'     => $this->id,
469             'fieldname'     => $fieldname,
470             'action'        => $action,
471             'oldvalue'  => $old,
472             'value'     => $value
473         ));
474         $ca->insert();
475         //print_r($ca);
476         
477         return 1;
478     }
479    
480     /**
481      * usage
482      *      add('xxx:yyy:zzz' , new , old)
483      *      add('xxx:yyy', new_do, old_do) // two dataobjects.. (will diff the two..
484      *      add($new_do, $old_do) 
485      */
486     
487     
488     function add($fieldname, $new, $old = false)
489     {
490         //print_r(array("ADD", $fieldname, $new, $old));
491         //echo "check: $fieldname ($old) => ($new)\n"; 
492         $ret = 0;
493         if (is_object($fieldname)) {
494             return $this->add($this->ontable.':'. $this->onid , $fieldname, $new );  
495         }
496         if (is_object($new)) {
497             // should check keys() - so it does not log primary key addition.
498             
499             $cols = array_keys($new->tableColumns());
500             $keys  = $new->keys();
501             $links = $new->links();
502             
503             foreach($cols as $k) {
504                 if (in_array($k, $keys)) {
505                     continue;
506                 } 
507                 $alinks = isset($links[$k])  ? explode(':', $links[$k]) : false;
508                 if ($alinks && $this->tablename() == array_shift($alinks)) {
509                     continue;
510                 }
511                 $ret += $this->add($fieldname .':'. $k, $new->{$k}, !$old ? '' :  $old->{$k});
512             }
513             
514             return $ret;
515         }
516      
517         if ($old == $new) {
518             return $ret;
519         }
520         if (!$old === false) {
521             return  $this->addentry($fieldname, 'set', $old, $new);
522              
523         }
524         if ($new === false) {
525             return    $this->addentry($fieldname, 'deleted', $old, $new);
526             
527         }
528         return   $this->addentry($fieldname, 'changed', $old, $new);
529          
530     }
531     
532     
533     
534     
535     
536     var $_audit = false;
537     function cachedAudit()
538     {
539         static $cache = array();
540         
541         if (!isset($cache[$this->id])) {
542             $cache[$this->id]= $this->audit();
543         }
544         return $cache[$this->id];
545     }
546     function cachedAuditToJSONArray()
547     {
548          
549         $ar = $this->cachedAudit();
550         
551        
552         $ret = array();
553         foreach($ar as $a) {
554              
555             $add = $a->toJSONArray($this);
556             if (!$add) {
557                 continue;
558             }
559             
560             $ret[] = $add;
561         }
562         return $ret;
563         
564     }
565      
566     function relatedCommits()
567     {
568         if (empty($this->rev))  {
569             return '';
570         }
571         // occurs on a ticket... when listing...
572         $mc = DB_DataObject::Factory('mtrack_change');
573         $mc->rev = $this->rev;
574         $mc->ontable = 'mtrack_repos';
575         if (!$mc->find(true)) {
576             return '';
577         }
578         // got the repo.. and the commit..
579         
580         $repo = $mc->objectCached();
581          
582         return  $repo->historyToSummary(
583                     $repo->impl()->history('.', 1, 'rev', $this->rev),
584                 $this->changedate
585         );
586         
587         
588         
589         
590     }
591     
592     
593     function audit()
594     {
595         $a = DB_DataObject::Factory('mtrack_change_audit');
596         $a->whereAdd(" 
597            {$a->tableName()}.change_id = {$this->id} OR
598            {$a->tableName()}.ticket_change_id = {$this->id}   
599         ");
600         //       var_dump($a->find());exit;
601         
602         return $a->fetchAll();
603     }
604     
605     function changesSince($object,$since)
606     {
607         $q = DB_DataObject::factory('mtrack_change');
608         $q->onid = $object->id;
609         $q->ontable = $object->tableName();
610         $q->whereAdd("changedate > '$since'");
611         $q->orderBy('changedate ASC');
612         $q->autoJoin();
613         // we are going to end up with a list of objects that have changed.
614         // eg. a ticket or a repo..
615         
616         return  $q->fetchAll();
617         
618         
619     }
620     
621     
622     function cachedAuditToString()
623     {
624         // move these to 
625         $body = array();
626         $ar = $this->cachedAudit();
627         foreach($ar as $audit) {
628             $add = $audit->toAuditString($this);
629             if ($add === false) {
630                 continue;
631             }
632             $body[] = $add ;
633                   
634         }
635         return implode("\n",$body);
636     }
637     
638     function toRooArray()
639     {
640         $ret = $this->toArray();
641         $ret['audit'] = $this->cachedAuditToJSONArray();
642         $ret['commit']  = $this->relatedCommits();
643          
644         return $ret;
645         
646     }
647     
648     function beforeInsert($req, $roo)
649     {
650         
651         $roo->jerr("no direct access");
652         /*
653         if (empty($roo->authUser)) {
654             $roo->jerr("Invalid user inserting");
655         }
656         $this->person_id = $roo->authUser->id;
657         
658         $obj = $this->objectCached();
659         if (!$obj || !$obj->checkPerm('E', $roo->authUser)) {
660             $this->jerr("Invalid object / permission denied");
661         }
662         
663         $this->changedate = $this->sqlValue('NOW()');
664         
665         */
666     }
667     function beforeUpdate($old,$req,$roo)
668     {
669         $roo->jerr("update not allowed by user interface.");
670         
671         
672     }
673     
674     function changedate($format)
675     {
676         return date($format, strtotime($this->changedate));
677     }
678     function postListFilter($data, $authUser, $q)
679     {
680         if (empty($q['ontable']) || empty($q['onid']) || $q['ontable'] != 'mtrack_ticket') {
681             return $data;
682         }
683         //DB_DataObject::debugLevel(1);
684         // look up in the accounting system what time was spent on the ticket..
685         $ie = DB_DataObject::factory('cash_invoice_entry');
686         $ie->ticket_id = $q['onid'];
687         $ie->orderBy('entered_dt ASC'); // doesnt matter really - we will sort it..
688         $ie->autoJoin();
689         $ie->selectAdd();
690         $ie->selectAdd("
691             cash_invoice_entry.id * -1 as id,
692             staff_id as person_id,
693             join_staff_id_id.name as person_id_name,
694             entered_dt as changedate,
695             CONCAT(qtyvalue , ' Hours worked: ', cash_invoice_entry.description) as reason,
696             '' as rev,
697             '' as audit
698         ");
699         $hours = $ie->fetchAll(false,false,'toArray');
700         
701         $ret =  array_merge($data , $hours);
702         
703         usort($ret, function($a,$b) {
704             $aa = $a['changedate'];
705             $bb =  $b['changedate'];
706             if ($aa == $bb) {
707                 return 0;
708             }
709             return strtotime($aa) < strtotime($bb) ? -1 : 1;
710         });
711         
712         return $ret;
713         
714
715         
716         
717     }
718
719
720     
721 }