1: <?php
2:
3: /**
4: * Developer modules
5: * @package modules
6: * @subpackage developer
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10: define('COMMITS_URL', 'https://github.com/cypht-org/cypht/commit/');
11:
12: use Webklex\ComposerInfo\ComposerInfo;
13:
14: /**
15: * Build server information data
16: * @subpackage developer/handler
17: */
18: class Hm_Handler_process_server_info extends Hm_Handler_Module {
19: /***
20: * Collect environment info
21: */
22: public function process() {
23: $res = $this->request->server;
24: $res['phpversion'] = phpversion();
25: $res['zend_version'] = zend_version();
26: $res['sapi'] = php_sapi_name();
27: $res['handlers'] = Hm_Handler_Modules::dump();
28: $res['output'] = Hm_Output_Modules::dump();
29:
30: $branch_name = '-';
31: $commit_hash = '-';
32: $commit_url = '-';
33: $commit_date = '-';
34:
35: new ComposerInfo([
36: 'location'=>VENDOR_PATH.'composer/installed.json'
37: ]);
38: $package = ComposerInfo::getPackage("jason-munro/cypht");
39: if ($package) {
40: // Cypht is embedded
41: $branch_name = str_replace(['dev-', '-dev'], '', $package['version']);
42: $commit_hash = mb_substr($package['dist']['reference'], 0, 7);
43: $commit_url = COMMITS_URL.$commit_hash;
44: $commit_date = $package['time'];
45: } elseif (exec('git --version')) {
46: // Standalone cypht
47: $branch_name = trim(exec('git rev-parse --abbrev-ref HEAD'));
48: $commit_hash = mb_substr(trim(exec('git log --pretty="%H" -n1 HEAD')), 0, 7);
49: $commit_url = COMMITS_URL.$commit_hash;
50: $commit_date = trim(exec('git log -n1 --pretty=%ci HEAD'));
51: }
52:
53: if ($commit_hash != '-') {
54: // Get the correct commit date (merged PR date or commit date)
55: $ch = Hm_Functions::c_init();
56: if ($ch) {
57: Hm_Functions::c_setopt($ch, CURLOPT_URL, 'https://api.github.com/repos/cypht-org/cypht/commits/'.$commit_hash);
58: Hm_Functions::c_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
59: Hm_Functions::c_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
60: Hm_Functions::c_setopt($ch, CURLOPT_USERAGENT, $this->request->server["HTTP_USER_AGENT"]);
61: $curl_result = Hm_Functions::c_exec($ch);
62: if (trim($curl_result)) {
63: if (!mb_strstr($curl_result, 'No commit found for SHA')) {
64: $json_commit = json_decode($curl_result);
65: $commit_date = $json_commit->commit->author->date;
66:
67: // Now check if this commit is part of a PR and if it was merged
68: $pr_ch = Hm_Functions::c_init();
69: if ($pr_ch) {
70: Hm_Functions::c_setopt($pr_ch, CURLOPT_URL, 'https://api.github.com/repos/cypht-org/cypht/commits/'.$commit_hash.'/pulls');
71: Hm_Functions::c_setopt($pr_ch, CURLOPT_RETURNTRANSFER, 1);
72: Hm_Functions::c_setopt($pr_ch, CURLOPT_CONNECTTIMEOUT, 1);
73: Hm_Functions::c_setopt($pr_ch, CURLOPT_USERAGENT, $this->request->server["HTTP_USER_AGENT"]);
74: $pr_curl_result = Hm_Functions::c_exec($pr_ch);
75:
76: if (trim($pr_curl_result)) {
77: $json_pr = json_decode($pr_curl_result);
78: if (isset($json_pr[0]->merged_at)) {
79: $commit_date = $json_pr[0]->merged_at;
80: }
81: }
82: }
83: }
84: }
85: }
86: }
87:
88: $res['branch_name'] = $branch_name;
89: $res['commit_hash'] = $commit_hash;
90: $res['commit_url'] = $commit_url;
91:
92: if ($commit_date != '-') {
93: $commit_date = new \DateTime($commit_date);
94: $commit_date->setTimezone(new \DateTimeZone('UTC'));
95: $res['commit_date'] = $commit_date->format('M d, Y');
96: }
97:
98: $this->out('server_info', $res);
99: }
100: }
101:
102: /**
103: * Output links to developer resources
104: * @subpackage developer/output
105: */
106: class Hm_Output_dev_content extends Hm_Output_Module {
107: /**
108: * Dev resources
109: */
110: protected function output() {
111: return '<div class="dev_content p-0"><div class="content_title px-3">'.$this->trans('Developer Documentation').'</div>'.
112: '<div class="p-3">'.
113: '<p class="mb-2">There is documentation available for developers, along with several resources online. One valuable resource is the <b>Module Overview Page</b> on the Cypht website, which is designed for developers interested in creating module sets:</p>'.
114: '<a class="ms-5" href="http://cypht.org/modules.html" data-external="true">Cypht Module Overview Page</a>'.
115: '<p class="mt-4 mb-2">As of 2024, the Cypht developers\' community has significantly evolved, offering comprehensive resources for new developers to understand the structure and functionality of Cypht. A nearly complete documentation is now accessible, making it easier for developers to get started and dive deeper into the platform\'s architecture:</p>'.
116: '<a class="ms-5" href="https://www.cypht.org/developers-documentation.html" data-external="true">Cypht Developers Documentation</a>'. '<p class="mt-4 mb-2">These resources provide a solid foundation for learning and contributing to the Cypht ecosystem, empowering developers to explore and extend its capabilities effectively.</p>'.
117: '</div></div>';
118: }
119: }
120:
121: /**
122: * Start the info section on the dev page
123: * @subpackage developer/output
124: */
125: class Hm_Output_info_heading extends Hm_Output_Module {
126: /**
127: * Leaves an open div
128: */
129: protected function output() {
130: return '<div class="info_content px-0"><div class="content_title p-3">'.$this->trans('Info').'</div>';
131: }
132: }
133:
134: /**
135: * Adds a link to the dev resources page to the folder list
136: * @subpackage developer/output
137: */
138: class Hm_Output_developer_doc_link extends Hm_Output_Module {
139: /**
140: * Link to the dev page
141: */
142: protected function output() {
143: $res = '<li class="menu_dev"><a class="unread_link" href="?page=dev">';
144: if (!$this->get('hide_folder_icons')) {
145: $res .= '<i class="bi bi-bug-fill menu-icon"></i>';
146: }
147: $res .= $this->trans('Dev').'</a></li>';
148: if ($this->format == 'HTML5') {
149: return $res;
150: }
151: $this->concat('formatted_folder_list', $res);
152: }
153: }
154:
155:
156: /**
157: * Adds a link to the info page to the folder list
158: * @subpackage developer/output
159: */
160: class Hm_Output_info_page_link extends Hm_Output_Module {
161: /**
162: * Info page link
163: */
164: protected function output() {
165: $res = '<li class="menu_info"><a class="unread_link" href="?page=info">';
166: if (!$this->get('hide_folder_icons')) {
167: $res .= '<i class="bi bi-info-circle menu-icon"></i>';
168: }
169: $res .= $this->trans('Info').'</a></li>';
170: if ($this->format == 'HTML5') {
171: return $res;
172: }
173: $this->concat('formatted_folder_list', $res);
174: }
175: }
176:
177: /**
178: * Outputs server information
179: * @subpackage developer/output
180: */
181: class Hm_Output_server_information extends Hm_Output_Module {
182: /**
183: * Information about the running instance
184: */
185: protected function output() {
186: $server_info = $this->get('server_info', array());
187: if (!empty($server_info)) {
188: return '<div class="server_info p-3"><table class="info table table-borderless">'.
189: '<tr><th class="text-secondary fw-light text-nowrap">Server Name</th><td>'.$server_info['HTTP_HOST'].'</td></tr>'.
190: '<tr><th class="text-secondary fw-light text-nowrap">Server Scheme</th><td>'.$server_info['REQUEST_SCHEME'].'</td></tr>'.
191: '<tr><th class="text-secondary fw-light text-nowrap">Server Address</th><td>'.$server_info['SERVER_ADDR'].'</td></tr>'.
192: '<tr><th class="text-secondary fw-light text-nowrap">Client Address</th><td>'.$server_info['REMOTE_ADDR'].'</td></tr>'.
193: '<tr><th class="text-secondary fw-light text-nowrap">PHP version</th><td>'.$server_info['phpversion'].'</td></tr>'.
194: '<tr><th class="text-secondary fw-light text-nowrap">Zend version</th><td>'.$server_info['zend_version'].'</td></tr>'.
195: '<tr><th class="text-secondary fw-light text-nowrap">SAPI</th><td>'.$server_info['sapi'].'</td></tr>'.
196: '<tr><th class="text-secondary fw-light text-nowrap">Enabled Modules</th><td>'.str_replace(',', ', ', implode(',', $this->get('router_module_list'))).'</td></tr>'.
197: '<tr><th class="text-secondary fw-light text-nowrap">Git version</th><td>'.$server_info['branch_name'].' at revision <a href="'.$server_info['commit_url'].'" data-external="true">'.$server_info['commit_hash'].'</a> ('.$server_info['commit_date'].')</td></tr>'.
198: '</table></div>';
199: }
200: return '';
201: }
202: }
203:
204: /**
205: * Output the current configuration setup
206: * @subpackage developer/output
207: */
208: class Hm_Output_config_map extends Hm_Output_Module {
209: /**
210: * Show pages, module assignments, and input filters
211: */
212: protected function output() {
213: $res = '<div class="content_title px-3">'.$this->trans('Configuration Map').'</div><table class="config_map">';
214: $handlers = array();
215: $outputs = array();
216: $ajax = array();
217: $normal = array();
218: $server_info = $this->get('server_info', array());
219: if (!empty($server_info)) {
220: $handlers = $server_info['handlers'];
221: ksort($handlers);
222: $outputs = $server_info['output'];
223: }
224: $res .= '<tr><td colspan="3"><div class="settings_subtitle mt-3">Pages</div></td></tr>';
225: foreach ($handlers as $page => $mods) {
226: if (mb_substr($page, 0, 4) == 'ajax') {
227: continue;
228: }
229: $res .= '<tr><td colspan="3" class="config_map_page" data-target="c'.$page.'">'.$page.'</td></tr>';
230: $res .= '<tr><th class="c'.$page.'" >Handler Modules</th><th class="c'.$page.'" >'.$this->trans('Source').'</th><th class="c'.$page.'" >Docs/Code</th></tr>';
231: foreach ($mods as $name => $vals) {
232: $res .= '<tr><td class="hmod c'.$page.'">'.$name.'</td><td class="hmod_val c'.$page.'">'.$vals[0].'</td>';
233: $res .= '<td class="hmod c'.$page.'"><a href="https://cypht.org/docs/code_docs/classes/Hm_Handler_'.$name.'.html" data-external="true"><i alt="Refresh" class="bi bi-code-slash"></i></a></td></tr>';
234: }
235: if (array_key_exists($page, $outputs)) {
236: $res .= '<tr><th class="c'.$page.'" >Output Modules</th><th class="c'.$page.'" >'.$this->trans('Source').'</th><th class="c'.$page.'" >Docs/Code</th></tr>';
237: foreach($outputs[$page] as $name => $vals) {
238: $res .= '<tr><td class="omod c'.$page.'">'.$name.'</td><td class="omod_val c'.$page.'">'.$vals[0].'</td>';
239: $res .= '<td class="omod c'.$page.'"><a href="https://cypht.org/docs/code_docs/classes/Hm_Output_'.$name.'.html" data-external="true"><i alt="Refresh" class="bi bi-code-slash"></i></a></td></tr>';
240: }
241: }
242: }
243: $res .= '<tr><td colspan="3"><div class="settings_subtitle mt-3">AJAX Requests</div></td></tr>';
244: foreach ($handlers as $page => $mods) {
245: if (mb_substr($page, 0, 4) != 'ajax') {
246: continue;
247: }
248: $res .= '<tr><td colspan="3" class="config_map_page" data-target="c'.$page.'">'.$page.'</td></tr>';
249: $res .= '<tr><th class="c'.$page.'" >Handler Modules</th><th class="c'.$page.'" >'.$this->trans('Source').'</th><th class="c'.$page.'" >Docs/Code</th></tr>';
250: foreach ($mods as $name => $vals) {
251: $res .= '<tr><td class="hmod c'.$page.'">'.$name.'</td><td class="hmod_val c'.$page.'">'.$vals[0].'</td>';
252: $res .= '<td class="hmod c'.$page.'"><a href="https://cypht.org/docs/code_docs/classes/Hm_Handler_'.$name.'.html" data-external="true"><i class="bi bi-code-slash"></i></a></td></tr>';
253: }
254: if (array_key_exists($page, $outputs)) {
255: $res .= '<tr><th class="c'.$page.'" >Output Modules</th><th class="c'.$page.'" >'.$this->trans('Source').'</th><th class="c'.$page.'" >Docs/Code</th></tr>';
256: foreach($outputs[$page] as $name => $vals) {
257: $res .= '<tr><td class="omod c'.$page.'">'.$name.'</td><td class="omod_val c'.$page.'">'.$vals[0].'</td>';
258: $res .= '<td class="omod c'.$page.'"><a href="https://cypht.org/docs/code_docs/classes/Hm_Output_'.$name.'.html" data-external="true"><i class="bi bi-code-slash"></i></a></td></tr>';
259: }
260: }
261: }
262: $res .= '</table>';
263: return $res;
264: }
265: }
266:
267: /**
268: * Starts a status table used on the info page
269: * @subpackage developer/output
270: */
271: class Hm_Output_server_status_start extends Hm_Output_Module {
272: /**
273: * Modules populate this table to run a status check from the info page
274: */
275: protected function output() {
276: $settings = $this->get('user_settings', array());
277: $enable_sieve = $settings['enable_sieve_filter_setting'] ?? DEFAULT_ENABLE_SIEVE_FILTER;
278: $res = '<div class="content_title_info px-4">'.$this->trans('Status & Sieve Capabilities').'</div><div class="server_status_info p-3">';
279: if (!$this->get('is_mobile')) {
280: $res .= '<table class="table table-borderless"><thead><tr><th class="text-secondary fw-light">'.$this->trans('Type').'</th><th class="text-secondary fw-light">'.$this->trans('Name').'</th><th class="text-secondary fw-light">'.
281: $this->trans('Status').'</th>';
282: if ($enable_sieve) {
283: $res .= '<th class="text-secondary fw-light">'.$this->trans('Sieve server capabilities').'</th>';
284: }
285: $res .= '</tr></thead><tbody>';
286: }else {
287: $imap_servers = $this->get('imap_servers', array());
288: // Helper function to render label/value pairs
289: $row = function($label, $value) {
290: return '<li><div class="server_status_label">'.$label.'</div><div>'.$value.'</div></li>';
291: };
292: $last_key = end(array_keys($imap_servers));
293: foreach($imap_servers as $index => $imap_server) {
294: $res .= "<ul class='server_status_parent_list'>";
295: $res .= $row($this->trans('Type'), strtoupper($imap_server['type'] ?? 'IMAP'));
296: $res .= $row($this->trans('Name'), $imap_server['name']);
297: $res .= $row(
298: $this->trans('Status'),
299: '<div class="imap_status_'.$imap_server['id'].' imap_status" data-id="'.$imap_server['id'].'"></div>'
300: );
301: if ($enable_sieve) {
302: $res .= $row(
303: $this->trans('Sieve server capabilities'),
304: '<div class="imap_detail_'.$imap_server['id'].'"></div>'
305: );
306: }
307: $res .= "</ul>";
308: if ($index !== $last_key) {
309: $res .= '<hr class="my-4 border-top border-secondary opacity-50">';
310: }
311: }
312: }
313: return $res;
314: }
315: }
316:
317:
318: /**
319: * Close the status table used on the info page
320: * @subpackage developer/output */
321: class Hm_Output_server_status_end extends Hm_Output_Module {
322: /**
323: * Close the table opened in Hm_Output_server_status_start
324: */
325: protected function output() {
326: if (!$this->get('is_mobile')) {
327: return '</tbody></table></div></div>';
328: }
329: return '</div>';
330: }
331: }
332:
333: /**
334: * Starts a capabilities table used on the info page
335: * @subpackage developer/output
336: */
337: class Hm_Output_server_capabilities_start extends Hm_Output_Module {
338: /**
339: * Modules populate this table to run a status check from the info page
340: */
341: protected function output() {
342: $res = '<div class="content_title_info px-4">'.$this->trans('Capabilities').'</div><div class="p-3">';
343: if(!$this->get('is_mobile')) {
344: $res .= '<table class="table table-borderless"><thead><tr><th class="text-secondary fw-light">'.$this->trans('Type').'</th><th class="text-secondary fw-light">'.$this->trans('Name').'</th><th class="text-secondary fw-light">'.
345: $this->trans('Server capabilities').'</th></tr></thead><tbody>';
346: }else {
347: $imap_servers = $this->get('imap_servers', array());
348:
349: // Helper function to render label/value pairs
350: $row = function($label, $value) {
351: return '<li><div class="server_status_label">'.$label.'</div><div>'.$value.'</div></li>';
352: };
353: $last_key = end(array_keys($imap_servers));
354: foreach($imap_servers as $imap_server) {
355: $res .= "<ul class='server_status_parent_list'>";
356: $res .= $row($this->trans('Type'), strtoupper($imap_server['type'] ?? 'IMAP'));
357: $res .= $row($this->trans('Name'), $imap_server['name']);
358:
359: $res .= $row(
360: $this->trans('Server capabilities'),
361: '<div class="imap_capabilities_'.$imap_server['id'].'"></div>'
362: );
363: $res .= "</ul>";
364: if ($imap_server['id'] !== $last_key) {
365: $res .= '<hr class="my-4 border-top border-secondary opacity-50">';
366: }
367: }
368: }
369: return $res;
370: }
371: }
372:
373: /**
374: * Close the capabilities table used on the info page
375: * @subpackage developer/output
376: */
377: class Hm_Output_server_capabilities_end extends Hm_Output_Module {
378: /**
379: * Close the table opened in Hm_Output_server_capabilities_start
380: */
381: protected function output() {
382: if(!$this->get('is_mobile')) {
383: return '</tbody></table></div></div>';
384: }
385: return '</div>';
386: }
387: }
388: