action.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <?php
  2. global $_,$conf;
  3. switch($_['action']){
  4. /** ISSUEREPORT **/
  5. //Récuperation d'une liste de issuereport
  6. case 'issue_issuereport_search':
  7. Action::write(function(&$response){
  8. global $myUser,$_;
  9. User::check_access('issue','read');
  10. require_once(__DIR__.SLASH.'IssueReport.class.php');
  11. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  12. require_once(__DIR__.SLASH.'IssueReportTag.class.php');
  13. $allTags = IssueReportTag::tags();
  14. $query = 'SELECT DISTINCT {{table}}.id, {{table}}.*,(SELECT ie.content FROM '.IssueEvent::tableName().' ie WHERE ie.type="'.IssueEvent::TYPE_COMMENT.'" AND ie.issue={{table}}.id ORDER BY id ASC LIMIT 1) as comment,(SELECT COUNT(ie2.id) FROM '.IssueEvent::tableName().' ie2 WHERE ie2.type="'.IssueEvent::TYPE_COMMENT.'" AND ie2.issue={{table}}.id) as comments FROM {{table}} LEFT JOIN '.IssueReportTag::tableName().' t ON t.report={{table}}.id WHERE 1';
  15. $data = array();
  16. //Recherche simple
  17. if(!empty($_['filters']['keyword'])){
  18. $query .= ' AND (SELECT ie.content FROM '.IssueEvent::tableName().' ie WHERE ie.type="'.IssueEvent::TYPE_COMMENT.'" AND ie.issue={{table}}.id ORDER BY id ASC LIMIT 1) LIKE ?';
  19. $data[] = '%'.$_['filters']['keyword'].'%';
  20. }
  21. //Uniquement les miens
  22. if(!empty($_['onlyMine']) && $_['onlyMine']=='true'){
  23. $query .= ' AND {{table}}.creator LIKE ?';
  24. $data[] = $myUser->login;
  25. }
  26. $tags = array_filter(explode(',',$_['tags']));
  27. if(count($tags)!=0){
  28. $query .= ' AND t.tag IN ("'.implode('","',$tags).'")';
  29. }
  30. //Recherche avancée
  31. if(isset($_['filters']['advanced'])) filter_secure_query($_['filters']['advanced'],array('{{table}}.id','from','comment','{{table}}.creator','{{table}}.created','assign','browser','ip','state'),$query,$data);
  32. $query .= ' ORDER BY {{table}}.id desc ';
  33. //Pagination
  34. $response['pagination'] = IssueReport::paginate(20,(!empty($_['page'])?$_['page']:0),$query,$data);
  35. foreach(IssueReport::staticQuery($query,$data) as $row){
  36. //$row = $issueReport->toArray(true);
  37. $row['state'] = IssueReport::states($row['state']);
  38. $row['relativefrom'] = str_replace(ROOT_URL,'',$row['from']);
  39. $row['date'] = date('d-m-Y',$row['created']);
  40. $row['hour'] = date('H:i',$row['created']);
  41. $row['osIcon'] = 'fas fa-question-circle';
  42. $row['browserIcon'] = 'fas fa-question-circle';
  43. $row['comments'] = $row['comments']==1 ? false : $row['comments']-1;
  44. $row['excerpt'] = isset($row['comment']) ? truncate(strip_tags($row['comment']),150) : 'Aucun commentaire';
  45. $row['belongUser'] = $myUser->login == $row['creator'] || $myUser->can('issue','configure');
  46. if(strlen($row['os'])>=3 && substr(strtolower($row['os']),0,3) == 'win') $row['osIcon'] = 'fab fa-windows text-primary';
  47. if(strlen($row['os'])>=5 && substr(strtolower($row['os']),0,5) == 'linux') $row['osIcon'] = 'fab fa-linux text-danger';
  48. if(strlen($row['os'])>=3 && substr(strtolower($row['os']),0,3) == 'mac') $row['osIcon'] = 'fab fa-apple text-secondary';
  49. switch($row['browser']){
  50. case 'firefox': $row['browserIcon'] = 'fab fa-firefox text-warning'; break;
  51. case 'ie': $row['browserIcon'] = 'fab fa-internet-explorer text-danger'; break;
  52. case 'edge': $row['browserIcon'] = 'fab fa-edge text-primary'; break;
  53. case 'chrome': $row['browserIcon'] = 'fab fa-chrome text-success'; break;
  54. }
  55. $row['tags'] = array();
  56. foreach(IssueReportTag::loadAll(array('report'=>$row['id'])) as $tag){
  57. if(!isset($allTags[$tag->tag])) continue;
  58. $row['tags'][] = $allTags[$tag->tag];
  59. }
  60. $response['rows'][] = $row;
  61. }
  62. });
  63. break;
  64. case 'issue_autocomplete':
  65. Action::write(function(&$response){
  66. global $myUser,$_;
  67. if(!$myUser->connected()) throw new Exception("Vous devez être connecté",401);
  68. require_once(__DIR__.SLASH.'IssueReport.class.php');
  69. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  70. $query = IssueReport::staticQuery('SELECT ir.id as id,ie.content as content FROM {{table}} ir LEFT JOIN '.IssueEvent::tableName().' ie ON ie.id=( SELECT id FROM '.IssueEvent::tableName().' ie2 WHERE ir.id=ie2.issue AND ie2.type="comment" ORDER BY id LIMIT 1) WHERE ie.content LIKE ?',array('%'.$_['keyword'].'%'));
  71. $response['rows'] = array();
  72. foreach ($query->fetchAll() as $row) {
  73. $response['rows'][] = array(
  74. 'id' => $row['id'],
  75. 'name' => $row['id'].' - '.strip_tags($row['content']),
  76. 'slug' => $row['id']
  77. );
  78. }
  79. });
  80. break;
  81. case 'issue_add_document':
  82. Action::write(function(&$response){
  83. global $myUser,$_;
  84. if(!$myUser->connected()) throw new Exception("Vous devez être connecté",401);
  85. require_once(__DIR__.SLASH.'IssueReport.class.php');
  86. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  87. $report = IssueReport::provide();
  88. $report->save();
  89. foreach ($_['files'] as $file) {
  90. $name = (get_OS() === 'WIN') ? utf8_decode($file['name']) : $file['name'];
  91. $row = File::move(File::temp().$file['path'],'issue'.SLASH.'screens'.SLASH.$report->id.SLASH.$name);
  92. $row['url'] = 'action.php?action=issue_download_document&event='.$event->id.'&path='.base64_encode($file['name']);
  93. $row['oldPath'] = $file['path'];
  94. $response['files'][] = $row;
  95. }
  96. $response['id'] = $report->id;
  97. });
  98. break;
  99. //Téléchargement des documents
  100. case 'issue_download_document':
  101. global $myUser,$_;
  102. if(!$myUser->connected()) throw new Exception("Vous devez être connecté",401);
  103. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  104. $event = IssueEvent::getById($_['event']);
  105. $path = str_replace(array('..','/','\\'),'',base64_decode($_['path']));
  106. $path = $event->dir().SLASH.$path;
  107. File::downloadFile($path);
  108. break;
  109. case 'issue_issuereport_meta_save':
  110. Action::write(function(&$response){
  111. global $myUser,$_,$conf;
  112. require_once(__DIR__.SLASH.'IssueReport.class.php');
  113. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  114. require_once(__DIR__.SLASH.'IssueReportTag.class.php');
  115. if(!array_key_exists($_['state'], IssueReport::states())) throw new Exception("L'état du ticket est invalide");
  116. $item = IssueReport::provide();
  117. $oldItem = clone $item;
  118. if($item->creator != $myUser->login && !$myUser->can('issue','configure')) throw new Exception("Permissions insuffisantes", 403);
  119. //Maj des tags
  120. if(empty($_['tags'])) throw new Exception("Le ticket doit avoir au moins un tag");
  121. $existingTags = IssueReportTag::loadAll(array('report'=>$item->id));
  122. $tags = explode(',',$_['tags']);
  123. $tagsAction = false;
  124. $similarTags = array();
  125. foreach ($existingTags as $existingTag) {
  126. if(in_array($existingTag->tag, $tags)){
  127. $similarTags[] = $existingTag->tag;
  128. continue;
  129. }
  130. IssueReportTag::deleteById($existingTag->id);
  131. $tagsAction = array('action'=>'deleted','tag'=>IssueReportTag::tags($existingTag->tag));
  132. }
  133. foreach ($tags as $tag) {
  134. if(in_array($tag, $similarTags)) continue;
  135. $newTag = new IssueReportTag();
  136. $newTag->tag = $tag;
  137. $newTag->report = $item->id;
  138. $newTag->save();
  139. $tagsAction = array('action'=>'added','tag'=>IssueReportTag::tags($tag));
  140. }
  141. if($tagsAction != false){
  142. $event = new IssueEvent();
  143. $event->type = IssueEvent::TYPE_TAG;
  144. $event->issue = $item->id;
  145. $event->content = json_encode($tagsAction);
  146. $event->save();
  147. }
  148. if(!$myUser->can('issue','configure')) throw new Exception("Permissions insuffisantes", 403);
  149. $item->state = $_['state'];
  150. if(!empty($_['assign'])){
  151. $item->assign = stripslashes($_['assign']);
  152. $item->addFollower($item->assign);
  153. }
  154. $item->addFollower($myUser->login);
  155. $item->save();
  156. //Si l'assignation a changée on envoi une notification
  157. if($oldItem->assign != $item->assign){
  158. $event = new IssueEvent();
  159. $event->type = IssueEvent::TYPE_ASSIGNATION;
  160. $event->issue = $item->id;
  161. $event->content = json_encode(array(
  162. 'assigned' => $item->assign
  163. ));
  164. $event->save();
  165. if($item->assign != $myUser->login){
  166. Plugin::callHook("emit_notification", array(array(
  167. 'label' => '['.PROGRAM_NAME.' - '.PROGRAM_UID.'] Le Ticket #'.$item->id.' vous a été assigné',
  168. 'html' => "Le ticket <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> vous a été assigné par ".(!empty($myUser->fullName())?$myUser->fullName():$myUser->login)." le ".date('d-m-Y à H:i').".
  169. <br>Bonne chance :).",
  170. 'type' => "issue",
  171. 'meta' => array('link' => ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$item->id),
  172. 'recipients' => array($item->assign)
  173. )));
  174. Plugin::callHook("emit_notification", array(array(
  175. 'label' => '['.PROGRAM_NAME.' - '.PROGRAM_UID.'] Le Ticket #'.$item->id.' a été assigné à '.User::byLogin($item->assign)->fullName(),
  176. 'html' => "Le ticket <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> a été assigné à ".User::byLogin($item->assign)->fullName()." par ".(!empty($myUser->fullName())?$myUser->fullName():$myUser->login)." le ".date('d-m-Y à H:i').".",
  177. 'type' => "issue",
  178. 'meta' => array('link' => ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$item->id),
  179. 'recipients' => array_diff( $item->followers(), array($myUser->login), array($item->assign) )
  180. )));
  181. }
  182. }
  183. //Si l'etat a changé on envois une notification
  184. if($oldItem->state != $item->state){
  185. $infos = array(
  186. 'meta' => array('link' => ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$item->id),
  187. 'recipients' => array_diff( $item->followers(), array($myUser->login) ) ,
  188. 'type' => "notice"
  189. );
  190. switch ($item->state) {
  191. case 'closed':
  192. $infos['label'] = "Le Ticket #".$item->id." est résolu";
  193. $infos['html'] = "Le ticket d'erreur <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> a été marqué comme résolu par ".(!empty($myUser->fullName())?$myUser->fullName():$myUser->login).' le '.date('d-m-Y à H:i').'.
  194. <br>Il est possible que la résolution de cette erreur ne soit mise en production que dans quelques jours.';
  195. break;
  196. case 'open':
  197. $infos['label'] = "Le Ticket #".$item->id." a été ré-ouvert";
  198. $infos['html'] = "Le ticket d'erreur <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> a été ré-ouvert par ".(!empty($myUser->fullName())?$myUser->fullName():$myUser->login).' le '.date('d-m-Y à H:i').'.
  199. <br>Il est possible que la résolution de cette erreur ne soit mise en production que dans quelques jours.';
  200. break;
  201. case 'canceled':
  202. $infos['label'] = "Le Ticket #".$item->id." a été annulé";
  203. $infos['html'] = "Le ticket d'erreur <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> a été annulé ".(!empty($myUser->fullName())?$myUser->fullName():$myUser->login).' le '.date('d-m-Y à H:i').'.
  204. <br>Il est possible que ce ticket soit un doublon ou que son contenu soit innaproprié.';
  205. break;
  206. default:
  207. # code...
  208. break;
  209. }
  210. $event = new IssueEvent();
  211. $event->type = IssueEvent::TYPE_STATE;
  212. $event->issue = $item->id;
  213. $event->content = json_encode(array(
  214. 'old' => $oldItem->state,
  215. 'new' => $item->state
  216. ));
  217. $event->save();
  218. Plugin::callHook("emit_notification", array($infos));
  219. }
  220. });
  221. break;
  222. //Ajout ou modification d'élément issuereport
  223. case 'issue_issuereport_save':
  224. Action::write(function(&$response){
  225. global $myUser,$_,$conf;
  226. require_once(__DIR__.SLASH.'IssueReport.class.php');
  227. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  228. require_once(__DIR__.SLASH.'IssueReportTag.class.php');
  229. if(!$myUser->connected()) throw new Exception("Vous devez être connecté pour laisser un rapport d'erreur",401);
  230. $item = IssueReport::provide();
  231. if(!$myUser->can('issue','edit') && is_numeric($item->id) && $item->id!=0) throw new Exception("Permissions insuffisantes",403);
  232. $tags = array_filter(explode(',',$_['tags']));
  233. if(count($tags)==0) throw new Exception("Merci de sélectionner au moins une catégorie");
  234. if(!isset($_['issue-comment']) || empty($_['issue-comment'])) throw new Exception("Merci de commenter votre ticket");
  235. $item->browser = $_['browser'];
  236. $item->browserVersion = $_['browserVersion'];
  237. $item->online = $_['online'];
  238. $item->os = $_['os'];
  239. $item->state = 'open';
  240. $item->from = $_['from'];
  241. $item->width = $_['width'];
  242. $item->height = $_['height'];
  243. $item->history = $_['history'];
  244. $item->ip = ip();
  245. $item->addFollower($myUser->login);
  246. $item->save();
  247. $firstEvent = new IssueEvent();
  248. $firstEvent->created = $item->created;
  249. $firstEvent->updated = $item->updated;
  250. $firstEvent->creator = $item->creator;
  251. $firstEvent->updater = $item->updater;
  252. $firstEvent->type = IssueEvent::TYPE_COMMENT;
  253. $firstEvent->content = preg_replace('/<img.*?(src="data).*?>/', '', wysiwyg_filter(html_entity_decode($_['issue-comment'])));
  254. $firstEvent->issue = $item->id;
  255. $firstEvent->save();
  256. if (preg_match_all('/<img.*?>/', wysiwyg_filter(html_entity_decode($_['issue-comment'])), $matches)) {
  257. foreach ($matches[0] as $index => $imgTag) {
  258. if(preg_match('/src="data:image.*base64,.*"/', $imgTag, $imgSrc)) {
  259. list($type, $data) = explode(';', $imgSrc[0]);
  260. $ext = explode('/', $type)[1];
  261. $dataEncode = explode(',', $data);
  262. $stream = base64_decode($dataEncode[1]);
  263. $attachmentFolder = $firstEvent->dir();
  264. if(!file_exists($attachmentFolder)) mkdir($attachmentFolder,0755,true);
  265. $screenPath = $firstEvent->dir().SLASH.'image'.$index.'.'.$ext;
  266. file_put_contents($screenPath, $stream);
  267. }
  268. }
  269. }
  270. $response['item'] = $item->toArray();
  271. IssueReportTag::delete(array('report'=>$item->id));
  272. foreach ($tags as $tag) {
  273. $reportTag = new IssueReportTag();
  274. $reportTag->tag = $tag;
  275. $reportTag->report = $item->id;
  276. $reportTag->save();
  277. }
  278. if(!empty($_['screenshot'])){
  279. $screenshot = str_replace('data:image/png;base64,', '', $_['screenshot']);
  280. $screenshot = str_replace(' ', '+', $screenshot);
  281. $stream = base64_decode($screenshot);
  282. $attachmentFolder = $firstEvent->dir();
  283. if(!file_exists($attachmentFolder)) mkdir($attachmentFolder,0755,true);
  284. $screenPath = $attachmentFolder.SLASH.'screenshot.jpg';
  285. file_put_contents($screenPath, $stream);
  286. }
  287. //Ajout des fichiers joints
  288. if(!empty($_['document_temporary'])){
  289. $files = json_decode($_['document_temporary'],true);
  290. $attachmentFolderRelative = $firstEvent->dir(true);
  291. $attachmentFolder = $firstEvent->dir();
  292. if(!file_exists($attachmentFolder)) mkdir($attachmentFolder,0755,true);
  293. foreach($files as $file){
  294. $from = (get_OS() === 'WIN') ? File::temp().utf8_decode($file['path']) : File::temp().$file['path'];
  295. $to = (get_OS() === 'WIN') ? utf8_decode($file['name']) : $file['name'];
  296. File::move($from, $attachmentFolderRelative.SLASH.$to);
  297. }
  298. }
  299. $data = $item->toArray();
  300. $data['comment'] = '<strong>'.$item->creator.': </strong>'.$firstEvent->content;
  301. $data['url'] = ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$data['id'];
  302. $data['title'] = 'Le Ticket <a href="'.$data['url'].'">#'.$item->id.'</a> a été ouvert par '.$item->creator;
  303. if(!empty($conf->get('issue_report_mails'))){
  304. $path = __DIR__.SLASH.'mail.template.php';
  305. if(!file_exists($path)) return;
  306. $stream = file_get_contents($path);
  307. $recipients = array();
  308. foreach (explode(',', $conf->get('issue_report_mails')) as $recipient) {
  309. if(is_numeric($recipient)){
  310. foreach(UserFirmRank::loadAll(array('rank'=>$recipient)) as $ufr)
  311. $recipients[] = $ufr->user;
  312. continue;
  313. }
  314. $recipients[] = $recipient;
  315. }
  316. // Émission d'une notification pour les devs
  317. Plugin::callHook("emit_notification", array(array(
  318. 'label' => 'Ticket #'.$item->id.' ouvert',
  319. 'html' => template($stream,$data),
  320. 'type' => "issue",
  321. 'meta' => array('link' => $data['url']),
  322. 'recipients' => $recipients
  323. )));
  324. }
  325. // Émission d'une notification pour l'auteur du ticket
  326. Plugin::callHook("emit_notification", array(array(
  327. 'label' => "Votre Ticket #".$item->id." a été envoyé",
  328. 'html' => "Le ticket <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>#".$item->id."</a> a été créé et sera pris en compte par nos techniciens, vous pouvez consulter
  329. son avancement en <a href='".ROOT_URL."/index.php?module=issue&page=sheet.report&id=".$item->id."'>cliquant ici</a>",
  330. 'type' => "notice",
  331. 'meta' => array('link' => $data['url']),
  332. 'recipients' => array($item->creator)
  333. )));
  334. Log::put('Déclaration d\'un rapport d\'erreur #'.$item->toText(), 'Issue');
  335. });
  336. break;
  337. case 'issue_screenshot_download':
  338. global $myUser,$_;
  339. if(!$myUser->connected()) throw new Exception("Connexion requise",401);
  340. try {
  341. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  342. $event = IssueEvent::getById($_['id']);
  343. File::downloadFile($event->dir().SLASH.'screenshot.jpg');
  344. } catch(Exception $e) {
  345. File::downloadFile(__ROOT__.'img/default-image.png');
  346. }
  347. break;
  348. //Suppression d'élement issuereport
  349. case 'issue_issuereport_delete':
  350. Action::write(function(&$response){
  351. global $myUser,$_;
  352. require_once(__DIR__.SLASH.'IssueReport.class.php');
  353. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  354. $issue = IssueReport::provide();
  355. if($issue->creator != $myUser->login && !$myUser->can('issue','configure')) throw new Exception("Permissions insuffisantes", 403);
  356. $issue->remove();
  357. });
  358. break;
  359. //Etre notifié du sujet
  360. case 'issue_follow_toggle':
  361. Action::write(function(&$response){
  362. global $myUser,$_;
  363. require_once(__DIR__.SLASH.'IssueReport.class.php');
  364. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  365. User::check_access('issue','read');
  366. $issue = IssueReport::provide();
  367. $followers = $issue->followers();
  368. $key = array_search($myUser->login, $followers);
  369. if($_['state']){
  370. if($key===false) $followers[] = $myUser->login;
  371. }else{
  372. if($issue->creator == $myUser->login) throw new Exception("Vous ne pouvez pas arrêter de suivre un ticket que vous avez créé.");
  373. if($key!==false) unset($followers[$key]);
  374. }
  375. $issue->followers($followers);
  376. $issue->save();
  377. });
  378. break;
  379. //Sauvegarde des configurations de issue
  380. case 'issue_setting_save':
  381. Action::write(function(&$response){
  382. global $myUser,$_,$conf;
  383. User::check_access('issue','configure');
  384. foreach(Configuration::setting('issue') as $key=>$value){
  385. if(!is_array($value)) continue;
  386. $allowed[] = $key;
  387. }
  388. foreach ($_['fields'] as $key => $value)
  389. if(in_array($key, $allowed)) $conf->put($key,$value);
  390. });
  391. break;
  392. /** EVENT **/
  393. //Récuperation d'une liste de issueevent
  394. case 'issue_issue_event_search':
  395. Action::write(function(&$response){
  396. global $myUser,$_;
  397. User::check_access('issue','read');
  398. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  399. require_once(__DIR__.SLASH.'IssueReport.class.php');
  400. $events = IssueEvent::loadAll(array('issue'=>$_['issue']),array('id ASC'));
  401. foreach($events as $i=>$event){
  402. $row = $event->toArray();
  403. $creator = User::byLogin($event->creator);
  404. $row['creator'] = $creator->toArray();
  405. $row['avatar'] = $creator->getAvatar();
  406. $row['fullName'] = $creator->fullName();
  407. $row['createdRelative'] = relative_time($event->created,null,10,true);
  408. switch($event->type){
  409. case 'comment':
  410. $row['classes'] = $i == 0 ? 'issue-first-comment' : '';
  411. $row['files'] = array();
  412. foreach (glob($event->dir().SLASH.'*') as $key => $value){
  413. $file = array();
  414. $file['extension'] = getExt($value);
  415. $file['label'] = mt_basename($value);
  416. $file['labelExcerpt'] = truncate($file['label'],35);
  417. $file['type'] = in_array($file['extension'], array('jpg','png','jpeg','bmp','gif','svg')) ? 'image' : 'file';
  418. $file['url'] = 'action.php?action=issue_download_document&event='.$event->id.'&path='.base64_encode(basename($value));
  419. $file['icon'] = getExtIcon($file['extension']);
  420. $row['files'][] = $file;
  421. }
  422. if($creator->login == $myUser->login || $myUser->can('issue','configure')) $row['classes'] .=' editable';
  423. $row['comment'] = empty($event->content) ? 'Pas de commentaire': $event->content;
  424. $row['hasfiles'] = count($row['files']) > 0 ? true : false;
  425. break;
  426. case IssueEvent::TYPE_STATE:
  427. $infos = json_decode($row['content'],true);
  428. $row['oldstate'] = IssueReport::states($infos['old']);
  429. $row['state'] = IssueReport::states($infos['new']);
  430. break;
  431. case IssueEvent::TYPE_TAG:
  432. $infos = json_decode($row['content'],true);
  433. $row['action'] = $infos['action'] == 'added' ? 'ajouté' :'supprimé';
  434. $row['tag'] = $infos['tag'];
  435. break;
  436. case IssueEvent::TYPE_ASSIGNATION:
  437. $infos = json_decode($row['content'],true);
  438. $assigned = User::byLogin($infos['assigned']);
  439. $row['assigned'] = array(
  440. 'fullName' => $assigned->fullName(),
  441. 'avatar' => $assigned->getAvatar()
  442. );
  443. break;
  444. }
  445. $response['rows'][] = $row;
  446. }
  447. });
  448. break;
  449. //Ajout ou modification d'élément issueevent
  450. case 'issue_issue_event_save':
  451. Action::write(function(&$response){
  452. global $myUser,$_;
  453. require_once(__DIR__.SLASH.'IssueReport.class.php');
  454. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  455. $item = IssueEvent::provide('id',1);
  456. $isNew = empty($item->id);
  457. if($myUser->login != $item->creator && !$myUser->can('issue','edit')) throw new Exception("Permissions insuffisantes", 403);
  458. if(!isset($_['content']) || empty($_['content'])) throw new Exception("Vous devez spécifier un commentaire");
  459. $item->type = IssueEvent::TYPE_COMMENT;
  460. $item->content = wysiwyg_filter(html_entity_decode($_['content']));
  461. $item->issue = $_['issue'];
  462. $mentionned = array();
  463. if($item->id == 0){
  464. preg_match_all('|data-mention-value="([^"]*)"|isU', $item->content, $matches,PREG_SET_ORDER);
  465. foreach ($matches as $match) {
  466. $mentionned[] = $match[1];
  467. }
  468. }
  469. $item->save();
  470. $issue = IssueReport::getById($item->issue);
  471. $issue->addFollower($myUser->login);
  472. $issue->save();
  473. $path = __DIR__.SLASH.'mail.template.php';
  474. if(!file_exists($path)) return;
  475. $stream = file_get_contents($path);
  476. $data = $item->toArray();
  477. $data['comment'] = '<strong>'.$item->creator.': </strong>'.$item->content;
  478. $data['url'] = ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$data['issue'];
  479. $data['title'] = 'Le Ticket <a href="'.$data['url'].'">#'.$item->issue.'</a> a été commenté par '.$item->creator;
  480. $recipients = array_diff($issue->followers(), array($myUser->login));
  481. // Émission d'une notification pour les acteurs du rapport
  482. Plugin::callHook("emit_notification", array(array(
  483. 'label' => 'Ticket #'.$item->issue.': '.($isNew?'Nouveau commentaire':'Édition d\'un commentaire') ,
  484. 'html' => template($stream,$data),
  485. 'type' => "issue",
  486. 'meta' => array('link' => $data['url']),
  487. 'recipients' => $recipients
  488. )));
  489. // Émission d'une notification pour les acteurs spécifiquement mentionnés
  490. if(count($mentionned)){
  491. $data['comment'] = 'Vous avez été mentionné dans le commentaire suivant : <br>'.$data['comment'];
  492. $data['url'] = ROOT_URL.'/index.php?module=issue&page=sheet.report&id='.$data['issue'].'#issue-comment-'.$item->id;
  493. $data['title'] = 'Vous avez été mentionné par '.$item->creator.' dans le ticket <a href="'.$data['url'].'">#'.$item->issue.'</a>';
  494. Plugin::callHook("emit_notification", array(array(
  495. 'label' => 'Vous avez été mentionné par '.$item->creator.' dans le ticket #'.$item->issue.':' ,
  496. 'html' => template($stream,$data),
  497. 'type' => "issue",
  498. 'meta' => array('link' => $data['url']),
  499. 'recipients' => $mentionned
  500. )));
  501. }
  502. });
  503. break;
  504. //Récuperation ou edition d'élément issueevent
  505. case 'issue_issue_event_edit':
  506. Action::write(function(&$response){
  507. global $myUser,$_;
  508. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  509. require_once(__DIR__.SLASH.'IssueReport.class.php');
  510. $event = IssueEvent::getById($_['id'],1);
  511. if($myUser->login != $event->creator && !$myUser->can('issue','edit')) throw new Exception("Permissions insuffisantes", 403);
  512. $response = $event->toArray(true);
  513. });
  514. break;
  515. //Suppression d'élement issueevent
  516. case 'issue_issue_event_delete':
  517. Action::write(function(&$response){
  518. global $myUser,$_;
  519. require_once(__DIR__.SLASH.'IssueReport.class.php');
  520. require_once(__DIR__.SLASH.'IssueEvent.class.php');
  521. $event = IssueEvent::getById($_['id'],1);
  522. if($event->type != 'comment') throw new Exception("Vous ne pouvez pas supprimer cet événement");
  523. $issue = $event->join('issue');
  524. if($event->creator != $myUser->login && !$myUser->can('issue','configure')) throw new Exception("Permissions insuffisantes", 403);
  525. $event->remove();
  526. if(IssueEvent::rowCount(array('issue'=>$issue->id)) == 0){
  527. if($issue->creator != $myUser->login && !$myUser->can('issue','configure')) throw new Exception("Permissions insuffisantes", 403);
  528. $issue->remove(false);
  529. $response['redirect'] = 'index.php?module=issue&page=list.issue';
  530. }
  531. });
  532. break;
  533. }
  534. ?>