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