1: <?php
2:
3: /**
4: * Github modules
5: * @package modules
6: * @subpackage github
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10:
11: /**
12: * Used to cache "read" github items
13: * @subpackage github/lib
14: */
15: class Hm_Github_Uid_Cache {
16: use Hm_Uid_Cache;
17: }
18:
19: /**
20: * @subpackage github/handler
21: */
22: class Hm_Handler_process_github_limit_setting extends Hm_Handler_Module {
23: public function process() {
24: process_site_setting('github_limit', $this, 'max_source_setting_callback', DEFAULT_GITHUB_PER_SOURCE);
25: }
26: }
27:
28: /**
29: * @subpackage github/handler
30: */
31: class Hm_Handler_process_github_since_setting extends Hm_Handler_Module {
32: public function process() {
33: process_site_setting('github_since', $this, 'since_setting_callback',DEFAULT_GITHUB_SINCE);
34: }
35: }
36:
37: /**
38: * @subpackage github/handler
39: */
40: class Hm_Handler_process_unread_github_included extends Hm_Handler_Module {
41: public function process() {
42: function unread_github_setting_callback($val) { return $val; }
43: process_site_setting('unread_exclude_github', $this, 'unread_github_setting_callback', DEFAULT_UNREAD_EXCLUDE_GITHUB, true);
44: }
45: }
46:
47: /**
48: * @subpackage github/handler
49: */
50: class Hm_Handler_github_status extends Hm_Handler_Module {
51: public function process() {
52: list($success, $form) = $this->process_form(array('github_repo'));
53: if ($success) {
54: $details = $this->user_config->get('github_connect_details', array());
55: if (!empty($details)) {
56: $repos = $this->user_config->get('github_repos', array());
57: if (in_array($form['github_repo'], $repos, true)) {
58: $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page=1', $form['github_repo']);
59: $start = microtime(true);
60: $api = new Hm_API_Curl();
61: $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
62: if (!empty($data)) {
63: $this->out('github_status', 'success');
64: $this->out('github_connect_time', (microtime(true) - $start));
65: $this->out('github_repo', $form['github_repo']);
66: return;
67: }
68: }
69: }
70: $this->out('github_status', 'failed');
71: $this->out('github_repo', $form['github_repo']);
72: }
73: }
74: }
75:
76: /**
77: * @subpackage github/handler
78: */
79: class Hm_Handler_load_github_repos extends Hm_Handler_Module {
80: public function process() {
81: $this->out('github_repos', $this->user_config->get('github_repos', array()));
82: }
83: }
84:
85: /**
86: * @subpackage github/handler
87: */
88: class Hm_Handler_github_message_action extends Hm_Handler_Module {
89: public function process() {
90:
91: list($success, $form) = $this->process_form(array('action_type', 'message_ids'));
92: if ($success) {
93: $id_list = explode(',', $form['message_ids']);
94: Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
95: foreach ($id_list as $msg_id) {
96: if (preg_match("/^github_(\d)+_(\d)+$/", $msg_id)) {
97: $parts = explode('_', $msg_id, 3);
98: $guid = $parts[2];
99: switch($form['action_type']) {
100: case 'unread':
101: Hm_Github_Uid_Cache::unread($guid);
102: break;
103: case 'read':
104: case 'delete':
105: Hm_Github_Uid_Cache::read($guid);
106: break;
107: }
108: }
109: }
110: $this->cache->set('github_read_uids', Hm_Github_Uid_Cache::dump(), 0, true);
111: }
112: }
113: }
114:
115: /**
116: * @subpackage github/handler
117: */
118: class Hm_Handler_github_folders_data extends Hm_Handler_Module {
119: public function process() {
120: $this->out('github_connect_details', $this->user_config->get('github_connect_details', array()));
121: $this->out('github_repos', $this->user_config->get('github_repos', array()));
122: }
123: }
124:
125: /**
126: * @subpackage github/handler
127: */
128: class Hm_Handler_github_event_detail extends Hm_Handler_Module {
129: public function process() {
130: list($success, $form) = $this->process_form(array('list_path', 'github_uid'));
131: if ($success) {
132: Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
133: $details = $this->user_config->get('github_connect_details', array());
134: $repos = $this->user_config->get('github_repos', array());
135: $limit = $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE);
136: $repo = mb_substr($form['list_path'], 7);
137: if (in_array($repo, $repos, true)) {
138: $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page='.$limit, $repo);
139: $api = new Hm_API_Curl();
140: $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
141: $event = array();
142: $uid = explode('_', $form['github_uid'])[2];
143: if (is_array($data)) {
144: foreach ($data as $item) {
145: if ($item['id'] == $uid) {
146: $event = $item;
147: break;
148: }
149: }
150: }
151: Hm_Github_Uid_Cache::read($item['id']);
152: $this->cache->set('github_read_uids', Hm_Github_Uid_Cache::dump(), 0, true);
153: $this->out('github_event_detail', $event);
154: }
155: }
156: }
157: }
158:
159: /**
160: * @subpackage github/handler
161: */
162: class Hm_Handler_process_github_authorization extends Hm_Handler_Module {
163: public function process() {
164: if (array_key_exists('state', $this->request->get) && $this->request->get['state'] == 'github_authorization') {
165: if (array_key_exists('code', $this->request->get)) {
166: $details = github_connect_details($this->config);
167: $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri']);
168: $result = $oauth2->request_token($details['token_url'], $this->request->get['code'], array('Accept: application/json'));
169: if (count($result) > 0 && array_key_exists('access_token', $result)) {
170: $this->load_repositories($result['access_token']);
171: Hm_Msgs::add('Github connection established', 'info');
172: $this->user_config->set('github_connect_details', $result);
173: $user_data = $this->user_config->dump();
174: $this->session->set('user_data', $user_data);
175: $this->session->record_unsaved('Github connection');
176: $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
177: $this->session->close_early();
178: }
179: else {
180: Hm_Msgs::add('An Error Occurred', 'danger');
181: }
182: }
183: elseif (array_key_exists('error', $this->request->get)) {
184: Hm_Msgs::add(''.ucwords(str_replace('_', ' ', $this->request->get['error'])), 'danger');
185: }
186: else {
187: Hm_Msgs::add('An Error Occurred', 'danger');
188: }
189: $this->save_hm_msgs();
190: Hm_Dispatch::page_redirect('?page=servers');
191: }
192: }
193:
194: public function load_repositories($token) {
195: $repos = $this->user_config->get('github_repos', array());
196: $url = sprintf('https://api.github.com/user/repos');
197: $api = new Hm_API_Curl();
198: $data = $api->command($url, array('Authorization: token ' . $token));
199: foreach ($data as $value) {
200: if (!in_array($value['full_name'], $repos)) {
201: $repos[] = $value['full_name'];
202: }
203: }
204: $this->user_config->set('github_repos', $repos);
205: }
206: }
207:
208: /**
209: * @subpackage github/handler
210: */
211: class Hm_Handler_setup_github_connect extends Hm_Handler_Module {
212: public function process() {
213: $details = github_connect_details($this->config);
214: if (!empty($details)) {
215: $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri']);
216: $this->out('github_auth_url', $oauth2->request_authorization_url($details['auth_url'], 'repo', 'github_authorization'));
217: $this->out('github_connect_details', $this->user_config->get('github_connect_details', array()));
218: $this->out('github_repos', $this->user_config->get('github_repos', array()));
219: }
220: }
221: }
222:
223: /**
224: * @subpackage github/handler
225: */
226: class Hm_Handler_github_process_remove_repo extends Hm_Handler_Module {
227: public function process() {
228: list($success, $form) = $this->process_form(array('github_repo', 'github_remove_repo'));
229: if ($success) {
230: $details = $this->user_config->get('github_connect_details');
231: if ($details) {
232: $repos = $this->user_config->get('github_repos', array());
233: if (in_array($form['github_repo'], $repos, true)) {
234: foreach ($repos as $index => $repo) {
235: if ($repo === $form['github_repo']) {
236: unset($repos[$index]);
237: break;
238: }
239: }
240: $this->user_config->set('github_repos', $repos);
241: $user_data = $this->user_config->dump();
242: $this->session->set('user_data', $user_data);
243: $this->session->record_unsaved('Github repository removed');
244: $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
245: Hm_Msgs::add('Removed repository');
246: }
247: }
248: }
249: }
250: }
251:
252: /**
253: * @subpackage github/handler
254: */
255: class Hm_Handler_github_process_add_repo extends Hm_Handler_Module {
256: public function process() {
257: list($success, $form) = $this->process_form(array('new_github_repo', 'new_github_repo_owner', 'github_add_repo'));
258: if ($success) {
259: $details = $this->user_config->get('github_connect_details');
260: if ($details) {
261: $url = sprintf('https://api.github.com/repos/%s/%s/events?page=1&per_page=1', urlencode($form['new_github_repo_owner']), urlencode($form['new_github_repo']));
262: $api = new Hm_API_Curl();
263: $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
264: if (!empty($data)) {
265: $repos = $this->user_config->get('github_repos', array());
266: $new_repo = urlencode($form['new_github_repo_owner']).'/'.urlencode($form['new_github_repo']);
267: if (!in_array($new_repo, $repos, true)) {
268: $repos[] = $new_repo;
269: $this->user_config->set('github_repos', $repos);
270: $user_data = $this->user_config->dump();
271: $this->session->set('user_data', $user_data);
272: $this->session->record_unsaved('Github repository added');
273: $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
274: Hm_Msgs::add('Added repository');
275: }
276: else {
277: Hm_Msgs::add('Repository is already added', 'warning');
278: }
279: }
280: else {
281: Hm_Msgs::add('Could not find that repository/owner combination at github.com', 'warning');
282: }
283: }
284: }
285: }
286: }
287:
288: /**
289: * @subpackage github/handler
290: */
291: class Hm_Handler_github_disconnect extends Hm_Handler_Module {
292: public function process() {
293: if (array_key_exists('github_disconnect', $this->request->post)) {
294: $this->user_config->set('github_connect_details', array());
295: $user_data = $this->user_config->dump();
296: $this->session->set('user_data', $user_data);
297: $this->out('reload_folders', true, false);
298: $this->session->record_unsaved('Github connection deleted');
299: Hm_Msgs::add('Github connection deleted');
300: }
301: }
302: }
303:
304: /**
305: * @subpackage github/handler
306: */
307: class Hm_Handler_github_list_data extends Hm_Handler_Module {
308: public function process() {
309: list($success, $form) = $this->process_form(array('github_repo'));
310: if ($success) {
311: $login_time = $this->session->get('login_time', false);
312: if ($login_time) {
313: $this->out('login_time', $login_time);
314: }
315: if (array_key_exists('list_path', $this->request->get)) {
316: if (in_array($this->request->get['list_path'], array('combined_inbox', 'unread'), true)) {
317: $this->out('list_parent', $this->request->get['list_path']);
318: }
319: }
320: Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
321: $details = $this->user_config->get('github_connect_details');
322: $repos = $this->user_config->get('github_repos');
323: if (in_array($form['github_repo'], $repos, true) && $details) {
324: $limit = $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE);
325: $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page='.$limit, $form['github_repo']);
326: $api = new Hm_API_Curl();
327: $this->out('github_data', $api->command($url, array('Authorization: token ' . $details['access_token'])));
328: $this->out('github_data_source', $form['github_repo']);
329: $this->out('github_data_source_id', array_search($form['github_repo'], $repos, true));
330: }
331: if (array_key_exists('list_path', $this->request->get)) {
332: if ($this->request->get['list_path'] == 'unread') {
333: $this->out('github_list_since', process_since_argument($this->user_config->get('unread_since_setting', DEFAULT_UNREAD_SINCE)));
334: }
335: if ($this->request->get['list_path'] == 'combined_inbox') {
336: $this->out('github_list_since', process_since_argument($this->user_config->get('all_since_setting', DEFAULT_ALL_SINCE)));
337: }
338: if ($this->request->get['list_path'] == 'github_all') {
339: $this->out('github_list_since', process_since_argument($this->user_config->get('github_since_setting', DEFAULT_GITHUB_SINCE)));
340: }
341: }
342: if (array_key_exists('github_unread', $this->request->post) && $this->request->post['github_unread']) {
343: $this->out('github_unread', true);
344: }
345: }
346: }
347: }
348:
349: /**
350: * @subpackage github/handler
351: */
352: class Hm_Handler_github_list_type extends Hm_Handler_Module {
353: public function process() {
354: $repos = $this->user_config->get('github_repos', array());
355: $excluded = $this->user_config->get('unread_exclude_github_setting', DEFAULT_UNREAD_EXCLUDE_GITHUB);
356: $github_list = false;
357: $parent = '';
358: if (array_key_exists('list_parent', $this->request->get)) {
359: $parent = $this->request->get['list_parent'];
360: }
361: if (array_key_exists('list_path', $this->request->get)) {
362: $path = $this->request->get['list_path'];
363: if ($this->page == 'message_list' && preg_match("/^github_(.+)$/", $path)) {
364: $github_list = true;
365: if ($path == 'github_all') {
366: $this->out('list_path', 'github_all', false);
367: $this->out('list_parent', $parent, false);
368: $this->out('mailbox_list_title', array('Github', 'All'));
369: $this->out('message_list_since', $this->user_config->get('github_since_setting', DEFAULT_GITHUB_SINCE));
370: $this->out('per_source_limit', $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE));
371: foreach ($repos as $repo) {
372: $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
373: }
374: }
375: else {
376: $repo = mb_substr($path, 7);
377: $this->out('list_parent', $parent, false);
378: if ($parent == 'github_all') {
379: $this->out('mailbox_list_title', array(urldecode($repo)));
380: }
381: else {
382: $this->out('mailbox_list_title', array('Github', urldecode($repo)));
383: $this->out('list_path', $path, false);
384: }
385: $this->out('custom_list_controls', ' ');
386: $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
387: }
388: }
389: elseif ($this->page == 'message_list' && ($path == 'combined_inbox' || $path == 'unread')) {
390: $github_list = true;
391: if (!$excluded || $path == 'combined_inbox') {
392: foreach ($repos as $repo) {
393: $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
394: }
395: }
396: }
397: elseif ($this->page == 'message' && preg_match("/^github_(.+)$/", $path)) {
398: if (!$parent) {
399: $repo = mb_substr($path, 7);
400: $this->out('mailbox_list_title', array('Github', urldecode($repo)));
401: }
402: }
403: }
404: if (!$github_list) {
405: foreach ($repos as $repo) {
406: if (!$excluded) {
407: $this->append('data_sources', array('group' => 'background', 'type' => 'github', 'name' => 'Github', 'id' => $repo));
408: }
409: }
410: }
411: }
412: }
413:
414: /**
415: * @subpackage github/output
416: */
417: class Hm_Output_github_folders extends Hm_Output_Module {
418: protected function output() {
419: $details = $this->get('github_connect_details', array());
420: if (!empty($details)) {
421: $res = '<li class="menu_github_all"><a class="unread_link" href="?page=message_list&list_path=github_all">';
422: if (!$this->get('hide_folder_icons')) {
423: $res .= '<i class="bi bi-code-slash account_icon"></i> ';
424: }
425: $res .= $this->trans('All').'</a></li>';
426: foreach ($this->get('github_repos', array()) as $repo) {
427: $res .= '<li class="menu_github_'.$this->html_safe($repo).'"><a class="unread_link" href="?page=message_list&list_path=github_'.$this->html_safe($repo).'">';
428: if (!$this->get('hide_folder_icons')) {
429: $res .= '<i class="bi bi-code-slash account_icon"></i> ';
430: }
431: $res .= $this->html_safe(explode('/', urldecode($repo))[1]).'</a></li>';
432: }
433: $this->append('folder_sources', array('github_folders', $res));
434: }
435: }
436: }
437:
438: /**
439: * @subpackage github/output
440: */
441: class Hm_Output_filter_github_data extends Hm_Output_Module {
442: protected function output() {
443: $res = array();
444: $login_time = false;
445: $unread_only = false;
446: $show_icons = $this->get('msg_list_icons');
447: if ($this->get('login_time')) {
448: $login_time = $this->get('login_time');
449: }
450: if ($this->get('list_path', '') == 'unread' || $this->get('github_unread', false)) {
451: $unread_only = true;
452: }
453: $repo_id = $this->get('github_data_source_id');
454: $repo = $this->get('github_data_source', 'Github');
455: $repo_parts = explode('/', $repo);
456: $repo_name = $repo_parts[1];
457: $cutoff = $this->get('github_list_since', '');
458: if ($cutoff) {
459: $cutoff = strtotime($cutoff);
460: }
461: else {
462: $cutoff = 0;
463: }
464: $list_parent = false;
465: if ($this->get('list_parent', '')) {
466: $list_parent = $this->get('list_parent');
467: }
468: elseif ($this->get('list_path') == 'github_all') {
469: $list_parent = $this->get('list_path');
470: }
471: foreach ($this->get('github_data', array()) as $event) {
472: if (!is_array($event) || !array_key_exists('id', $event)) {
473: continue;
474: }
475: $row_class = 'github';
476: $id = 'github_'.$repo_id.'_'.$event['id'];
477: $subject = build_github_subject($event, $this);
478: $url = '?page=message&uid='.$this->html_safe($id).'&list_path=github_'.$this->html_safe($repo);
479: if ($list_parent) {
480: $url .= '&list_parent='.$this->html_safe($list_parent);
481: }
482: $from = $event['actor']['login'];
483: $ts = strtotime($event['created_at']);
484: if ($ts < $cutoff) {
485: continue;
486: }
487: if (Hm_Github_Uid_Cache::is_read($event['id'])) {
488: $flags = array();
489: }
490: elseif (Hm_Github_Uid_Cache::is_unread($event['id'])) {
491: $flags = array('unseen');
492: $row_class .= ' unseen';
493: }
494: elseif ($ts && $login_time && $ts <= $login_time) {
495: $flags = array();
496: }
497: else {
498: $row_class .= ' unseen';
499: $flags = array('unseen');
500: }
501: if ($unread_only && !in_array('unseen', $flags)) {
502: continue;
503: }
504: $date = date('r', $ts);
505: $style = $this->get('news_list_style') ? 'news' : 'email';
506: if ($this->get('is_mobile')) {
507: $style = 'news';
508: }
509: $row_class .= ' '.$repo_name;
510: $icon = 'code';
511: if (!$show_icons) {
512: $icon = '';
513: }
514: if ($style == 'news') {
515: $res[$id] = message_list_row(array(
516: array('checkbox_callback', $id),
517: array('icon_callback', $flags),
518: array('subject_callback', $subject, $url, $flags, $icon),
519: array('safe_output_callback', 'source', $repo_name),
520: array('safe_output_callback', 'from', $from),
521: array('date_callback', translate_time_str(human_readable_interval($date), $this), $ts),
522: ),
523: $id,
524: $style,
525: $this,
526: $row_class
527: );
528: }
529: else {
530: $res[$id] = message_list_row(array(
531: array('checkbox_callback', $id),
532: array('safe_output_callback', 'source', $repo_name, $icon),
533: array('safe_output_callback', 'from', $from),
534: array('subject_callback', $subject, $url, $flags),
535: array('date_callback', translate_time_str(human_readable_interval($date), $this), $ts),
536: array('icon_callback', $flags)
537: ),
538: $id,
539: $style,
540: $this,
541: $row_class
542: );
543: }
544: }
545: $this->out('formatted_message_list', $res);
546: $this->out('github_server_id', $repo_id);
547: }
548: }
549:
550: /**
551: * @subpackage github/output
552: */
553: class Hm_Output_github_add_repo extends Hm_Output_Module {
554: protected function output() {
555: $res = '';
556: $details = $this->get('github_connect_details', array());
557: if (!empty($details)) {
558: $res = '<div class="configured_server col-12 col-lg-4 mb-4 mt-3">
559: <div class="subtitle">'.$this->trans('Add a Repository').'</div>'.
560: '<form method="POST">'.
561: '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
562: '<label class="screen_reader">'.$this->trans('Name').'</label>'.
563: '<input type="text" value="" class="form-control mb-4" placeholder="'.$this->trans('Name').'" name="new_github_repo" />'.
564: '<label class="screen_reader">'.$this->trans('Owner').'</label>'.
565: '<input type="text" value="" class="form-control mb-4" placeholder="'.$this->trans('Owner').'" name="new_github_repo_owner" />'.
566: '<input type="submit" class="btn btn-primary" name="github_add_repo" value="Add" />'.
567: '</form></div>';
568: $res .= '<div class="configured_server mt-2"><h5 class="server_title">'.$this->trans('Repositories').'</h5></div>'.
569: '<table class="table">';
570: foreach ($this->get('github_repos', array()) as $repo) {
571: $res .= '<tr>'.
572: '<td class="col-sm-2 col-lg-1 py-2 align-middle">'.
573: '<form class="remove_repo" method="POST">'.
574: '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
575: '<input type="hidden" name="github_repo" value="'.$this->html_safe($repo).'" />'.
576: '<input type="submit" name="github_remove_repo" value="'.$this->trans('Remove').'" class="github_remove_repo btn btn-outline-danger btn-sm" />'.
577: '</form>'.
578: '</td>'.
579: '<td>'.$this->html_safe($repo).'</td>'.
580: '<tr>';
581: }
582: $res .= '</table></div></div><div class="end_float"></div></div>';
583: }
584: return $res;
585: }
586: }
587:
588: /**
589: * @subpackage github/output
590: */
591: class Hm_Output_filter_github_event_detail extends Hm_Output_Module {
592: protected function output() {
593: $details = $this->get('github_event_detail', array());
594: if (!empty($details)) {
595: if (array_key_exists('payload', $details)) {
596: $body = github_parse_payload($details, $this);
597: $headers = github_parse_headers($details, $this);
598: }
599: $this->out('github_msg_text', $headers.$body);
600: }
601: }
602: }
603:
604: /**
605: * @subpackage github/output
606: */
607: class Hm_Output_filter_github_status extends Hm_Output_Module {
608: protected function output() {
609: if ($this->get('github_status') == 'success') {
610: $this->out('github_status_display', '<span class="online">'.$this->trans('Connected').'</span> in '.round($this->get('github_connect_time'), 3));
611: }
612: else {
613: $this->out('github_status_display', '<span class="down">'.$this->trans('Down').'</span>');
614: }
615: $this->out('github_status_repo', $this->get('github_repo', ''));
616: }
617: };
618:
619: /**
620: * @subpackage github/output
621: */
622: class Hm_Output_display_github_status extends Hm_Output_Module {
623: protected function output() {
624: $res = '';
625: $repos = $this->get('github_repos', array());
626: if (!empty($repos)) {
627: foreach ($repos as $repo) {
628: $res .= '<tr><td class="github_repo" data-id="'.$this->html_safe($repo).'">'.$this->trans('Github repo').'</td><td>'.$this->html_safe($repo).'</td><td class="github_'.$this->html_safe($repo).'"></td></tr>';
629: }
630: }
631: return $res;
632: }
633: }
634:
635: /**
636: * @subpackage github/output
637: */
638: class Hm_Output_github_connect_section extends Hm_Output_Module {
639: protected function output() {
640: $details = $this->get('github_connect_details', array());
641: $res = '<div class="github_connect"><div data-target=".github_connect_section" class="server_section border-bottom cursor-pointer px-1 py-3 pe-auto">
642: <a href="#" class="pe-auto">
643: <i class="bi bi-github me-3"></i>
644: <b>'.$this->trans('Github Connect').'</b>
645: </a>
646: </div>';
647:
648: $res .= '<div class="github_connect_section"><div class="add_server">';
649:
650: if (empty($details)) {
651: $res .= 'Connect to Github<br /><br />';
652: $res .= '<a class="btn btn-secondary" href="'.$this->get('github_auth_url', '').'" data-external="true">'.$this->trans('Enable').'</a></div></div><div class="end_float"</div>';
653: }
654: else {
655: $res .='<h6>'. $this->trans('Already connected').'</h6>';
656: $res .= '<form id="github_disconnect_form" method="POST">';
657: $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
658: $res .= '<input type="submit" name="github_disconnect" class="github_disconnect btn btn-secondary" value="'.$this->trans('Disconnect').'" />';
659: $res .= '</form>';
660: }
661: return $res.'</div>';
662: }
663: }
664:
665: /**
666: * @subpackage github/output
667: */
668: class Hm_Output_unread_github_included_setting extends Hm_Output_Module {
669: protected function output() {
670: $settings = $this->get('user_settings');
671: if (array_key_exists('unread_exclude_github', $settings) && $settings['unread_exclude_github']) {
672: $checked = ' checked="checked"';
673: }
674: else {
675: $checked = '';
676: }
677: return '<tr class="unread_setting"><td><label for="unread_exclude_github">'.$this->trans('Exclude unread Github notices').'</label></td>'.
678: '<td><input class="form-check-input" type="checkbox" '.$checked.' value="1" id="unread_exclude_github" name="unread_exclude_github" /></td></tr>';
679: }
680: }
681:
682: /**
683: * @subpackage github/output
684: */
685: class Hm_Output_start_github_settings extends Hm_Output_Module {
686: protected function output() {
687: return '<tr><td colspan="2" data-target=".github_all_setting" class="settings_subtitle cursor-pointer border-bottom p-2">'.
688: '<i class="bi bi-github fs-5 me-2"></i>'.$this->trans('Github Settings').'</td></tr>';
689: }
690: }
691:
692: /**
693: * @subpackage github/output
694: */
695: class Hm_Output_github_since_setting extends Hm_Output_Module {
696: protected function output() {
697: $since = DEFAULT_GITHUB_SINCE;
698: $settings = $this->get('user_settings');
699: if (array_key_exists('github_since', $settings) && $settings['github_since']) {
700: $since = $settings['github_since'];
701: }
702: return '<tr class="github_all_setting"><td><label for="github_since">'.$this->trans('Show Github notices received since').'</label></td>'.
703: '<td>'.message_since_dropdown($since, 'github_since', $this, DEFAULT_GITHUB_SINCE).'</td></tr>';
704: }
705: }
706:
707: /**
708: * @subpackage github/output
709: */
710: class Hm_Output_github_limit_setting extends Hm_Output_Module {
711: protected function output() {
712: $limit = DEFAULT_GITHUB_PER_SOURCE;
713: $settings = $this->get('user_settings');
714: if (array_key_exists('github_limit', $settings)) {
715: $limit = $settings['github_limit'];
716: }
717: return '<tr class="github_all_setting"><td><label for="github_limit">'.$this->trans('Max Github notices per repository').'</label></td>'.
718: '<td class="d-flex"><input type="text" id="github_limit" name="github_limit" size="2" value="'.$this->html_safe($limit).'" data-default-value="'.DEFAULT_GITHUB_PER_SOURCE.'"/></td></tr>';
719: }
720: }
721:
722: /**
723: * @subpackage github/functions
724: */
725: if (!hm_exists('github_connect_details')) {
726: function github_connect_details($config) {
727: $confs = $config->dump();
728: return $confs['github'];
729: }}
730:
731: /**
732: * @subpackage github/functions
733: */
734: if (!hm_exists('build_github_subject')) {
735: function build_github_subject($event, $output_mod) {
736: $ref = '';
737: if (array_key_exists('payload', $event) && array_key_exists('ref', $event['payload'])) {
738: $ref = sprintf(' / %s', preg_replace("/^refs\/heads\//", '', $event['payload']['ref']));
739: }
740: $pre = '['.$output_mod->html_safe(trim(str_replace('Event', '', trim(preg_replace("/([A-Z])/", " $1", $event['type']))))).']';
741: $post = '';
742: $max = 100;
743: switch (mb_strtolower($event['type'])) {
744: case 'issuecommentevent':
745: $post = $event['payload']['issue']['title'];
746: break;
747: case 'issuesevent':
748: $post = $event['payload']['issue']['title'].' - '.$event['payload']['action'];
749: break;
750: case 'pushevent':
751: if (count($event['payload']['commits']) > 1) {
752: $post .= sprintf($output_mod->trans('%d commits: '), count($event['payload']['commits']));
753: }
754: else {
755: $post .= sprintf($output_mod->trans('%d commit: '), count($event['payload']['commits']));
756: }
757: $post .= mb_substr($event['payload']['commits'][0]['message'], 0, $max);
758: break;
759: case 'watchevent':
760: if ($event['payload']['action'] == 'started') {
761: $post .= sprintf($output_mod->trans('%s started watching this repo'), $event['actor']['login']);
762: }
763: else {
764: $post .= sprintf($output_mod->trans('%s stopped watching this repo'), $event['actor']['login']);
765: }
766: break;
767: case 'forkevent':
768: $post = sprintf($output_mod->trans("%s forked %s"), $event['actor']['login'], $event['repo']['name']);
769: break;
770: case 'createevent':
771: $post = sprintf($output_mod->trans("%s repository created"), $event['repo']['name']);
772: break;
773: case 'pullrequestevent':
774: $post = mb_substr($event['payload']['pull_request']['body'], 0, $max);
775: break;
776: case 'commitcommentevent':
777: $post = mb_substr($event['payload']['comment']['body'], 0, $max);
778: break;
779: case 'releaseevent':
780: $post = mb_substr($event['payload']['release']['name'], 0, $max);
781: default:
782: break;
783: }
784: if ($ref) {
785: $post .= $output_mod->html_safe($ref);
786: }
787: return $pre.' '.$post;
788: }}
789:
790: /**
791: * @subpackage github/functions
792: */
793: if (!hm_exists('github_parse_headers')) {
794: function github_parse_headers($data, $output_mod) {
795: $res = '<div class="msg_headers d-flex flex-column border-bottom border-2 border-secondary-subtle pb-3 mb-3">';
796: if (array_key_exists('type', $data)) {
797: $type = build_github_subject($data, $output_mod);
798: }
799: else {
800: $type = '[Unknown Type]';
801: }
802: if (array_key_exists('created_at', $data)) {
803: $date = sprintf("%s (%s)", date('r', strtotime($data['created_at'])), human_readable_interval($data['created_at']));
804: }
805: else {
806: $date = '[No date]';
807: }
808: $repo_link = '';
809: $from_link = '';
810:
811: if (array_key_exists('actor', $data) && array_key_exists('login', $data['actor'])) {
812: $from = $data['actor']['login'];
813: $from_link = sprintf(' - <a href="https://github.com/%s" target="_blank" rel="noopener">https://github.com/%s</a>', $output_mod->html_safe($from), $output_mod->html_safe($from));
814: }
815: else {
816: $from = '[No From]';
817: }
818: if (array_key_exists('repo', $data) && array_key_exists('name', $data['repo'])) {
819: $name = $data['repo']['name'];
820: $repo_link = sprintf(' - <a href="https://github.com/%s" target="_blank" rel="noopener">https://github.com/%s</a>', $output_mod->html_safe($name), $output_mod->html_safe($name));
821: }
822: else {
823: $name = '[No Repo]';
824: }
825: $res .= '<div class="header_subject d-flex justify-content-center"><h4 class="text-center mb-0 fw-bold">'.$output_mod->html_safe($type).'</h4></div>';
826: $res .= '<div class="header_date d-flex align-items-center py-1"><span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Date').':</span><span class="text-break">'.$output_mod->html_safe($date).'</span></div>';
827: $res .= '<div class="header_from d-flex align-items-center py-1"><span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Author').':</span><span class="text-break">'.$output_mod->html_safe($from).$from_link.'</span></div>';
828: $res .= '<div class="d-flex align-items-center py-1"><span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Repository').':</span><span class="text-break">'.$output_mod->html_safe($name).$repo_link.'</span></div>';
829: $res .= '</div>';
830: return $res;
831: }}
832:
833: /**
834: * @subpackage github/functions
835: */
836: if (!hm_exists('github_parse_payload')) {
837: function github_parse_payload($data, $output_mod) {
838: $type = false;
839: if (array_key_exists('type', $data)) {
840: $type = $data['type'];
841: }
842: /* Event types: CommitCommentEvent CreateEvent DeleteEvent DeploymentEvent DeploymentStatusEvent DownloadEvent FollowEvent
843: * ForkEvent ForkApplyEvent GistEvent GollumEvent IssueCommentEvent IssuesEvent MemberEvent MembershipEvent PageBuildEvent
844: * PublicEvent PullRequestEvent PullRequestReviewCommentEvent PushEvent ReleaseEvent RepositoryEvent StatusEvent TeamAddEvent
845: * WatchEvent */
846:
847: $data = $data['payload'];
848: $content = payload_search($data);
849: $res = '<div class="msg_text_inner">';
850: foreach ($content as $vals) {
851: $res .= '<div class="github_para">';
852: if (array_key_exists('name', $vals)) {
853: $res .= $output_mod->html_safe($vals['name']);
854: $res .= '</div><div class="github_para">';
855: }
856: if (array_key_exists('body', $vals)) {
857: $res .= $output_mod->html_safe(wordwrap($vals['body'], 100));
858: }
859: if (array_key_exists('message', $vals)) {
860: $res .= $output_mod->html_safe(wordwrap($vals['message'], 100));
861: }
862: if (array_key_exists('html_url', $vals)) {
863: $res .= sprintf('<div class="github_link"><a href="%s" data-external="true">%s</a></div>',
864: $output_mod->html_safe($vals['html_url']), $output_mod->html_safe($vals['html_url']));
865: }
866: if (array_key_exists('url', $vals) && array_key_exists('sha', $vals)) {
867: $url = str_replace(array('commits', 'https://api.github.com/repos'), array('commit', 'https://github.com'), $vals['url']);
868: $res .= sprintf('<div class="github_link"><a href="%s" target="_blank" rel="noopener">%s</a></div>',
869: $output_mod->html_safe($url), $output_mod->html_safe($vals['sha']));
870: }
871: $res .= '</div>';
872: }
873: $res .= '</div>';
874: return $res;
875: }}
876:
877: /**
878: * @subpackage github/functions
879: */
880: if (!hm_exists('payload_search')) {
881: function payload_search($data) {
882: $res = array();
883: $data_flds = array('url', 'sha', 'body', 'description', 'message', 'name');
884: foreach($data as $vals) {
885: if (is_array($vals)) {
886: $item = array();
887: foreach ($data_flds as $fld) {
888: if (array_key_exists($fld, $vals)) {
889: $item[$fld] = $vals[$fld];
890: }
891: }
892: if (!empty($item)) {
893: if (array_key_exists('html_url', $data)) {
894: $item['html_url'] = $vals['html_url'];
895: }
896: }
897: else {
898: $res = array_merge($res, payload_search($vals));
899: }
900: if (count($item) > 0) {
901: $res[] = $item;
902: }
903: }
904: }
905: return $res;
906: }}
907: