1: <?php
2:
3: /**
4: * WordPress modules
5: * @package modules
6: * @subpackage wordpress
7: */
8:
9: define('WPCOM_READ_URL', 'https://public-api.wordpress.com/rest/v1.1/notifications/read');
10: define('WPCOM_NOTICES_URL', 'https://public-api.wordpress.com/rest/v1/notifications/?number=20&fields=id,type,unread,subject,timestamp');
11: define('WPCOM_NOTICE_URL', 'https://public-api.wordpress.com/rest/v1/notifications/');
12:
13: if (!defined('DEBUG_MODE')) { die(); }
14:
15: /**
16: * @subpackage wordpress/handler
17: */
18: class Hm_Handler_process_wordpress_since_setting extends Hm_Handler_Module {
19: public function process() {
20: process_site_setting('wordpress_since', $this, 'since_setting_callback', DEFAULT_WORDPRESS_SINCE);
21: }
22: }
23:
24: /**
25: * @subpackage wordpress/handler
26: */
27: class Hm_Handler_process_unread_wp_included extends Hm_Handler_Module {
28: public function process() {
29: function unread_wp_setting_callback($val) { return $val; }
30: process_site_setting('unread_exclude_wordpress', $this, 'unread_wp_setting_callback', DEFAULT_UNREAD_EXCLUDE_WORDPRESS, true);
31: }
32: }
33:
34: /**
35: * @subpackage wordpress/handler
36: */
37: class Hm_Handler_wordpress_msg_action extends Hm_Handler_Module {
38: public function process() {
39:
40: list($success, $form) = $this->process_form(array('action_type', 'message_ids'));
41: if ($success) {
42: $id_list = explode(',', $form['message_ids']);
43: $wp_details = $this->user_config->get('wp_connect_details', array());
44: $wp_ids = array();
45: if ($form['action_type'] == 'read') {
46: foreach ($id_list as $msg_id) {
47: if (preg_match("/^wordpress_0_(\d)+$/", $msg_id)) {
48: $parts = explode('_', $msg_id, 3);
49: $wp_ids[] = $parts[2];
50: }
51: }
52: if (!empty($wp_ids)) {
53: $post = array();
54: foreach ($wp_ids as $id) {
55: $post['counts['.$id.']'] = 5;
56: }
57: $res = wp_fetch_content($wp_details, WPCOM_READ_URL, $post);
58: if (!is_array($res) || !array_key_exists('success', $res) || $res['success'] != 1) {
59: Hm_Msgs::add('Unable to update read status of WordPress notification', 'danger');
60: }
61: }
62: }
63: }
64: }
65: }
66:
67: /**
68: * @subpackage wordpress/handler
69: * @todo: fix for background unread
70: */
71: class Hm_Handler_wp_load_sources extends Hm_Handler_Module {
72: public function process() {
73: $wp_details = $this->user_config->get('wp_connect_details', array());
74: if (empty($wp_details)) {
75: return;
76: }
77: if (array_key_exists('list_path', $this->request->get)) {
78: $path = $this->request->get['list_path'];
79: }
80: else {
81: $path = '';
82: }
83: if ($path == 'combined_inbox' || $path == 'unread') {
84: $excluded = false;
85: if ($path == 'unread' && $this->user_config->get('unread_exclude_wordpress_setting', DEFAULT_UNREAD_EXCLUDE_WORDPRESS)) {
86: $excluded = true;
87: }
88: if (!$excluded) {
89: $this->append('data_sources', array('load_wp_notices_for_combined_list', 'type' => 'wordpress', 'name' => 'WordPress.com Notifications', 'id' => 0));
90: }
91: }
92: }
93: }
94:
95: /**
96: * @subpackage wordpress/handler
97: */
98: class Hm_Handler_wordpress_folders_data extends Hm_Handler_Module {
99: public function process() {
100: $this->out('wp_connect_details', $this->user_config->get('wp_connect_details', array()));
101: }
102: }
103:
104: /**
105: * @subpackage wordpress/handler
106: */
107: class Hm_Handler_get_wp_notice_data extends Hm_Handler_Module {
108: public function process() {
109: if (array_key_exists('wp_uid', $this->request->post)) {
110: $wp_details = $this->user_config->get('wp_connect_details', array());
111: if ($wp_details) {
112: $parts = explode('_', $this->request->post['wp_uid'], 3);
113: $wp_id = $parts[2];
114: $details = wp_get_notice_detail($wp_details, $wp_id);
115: if (is_array($details) && !empty($details)) {
116: if (preg_match("/^wordpress_0_(\d)+$/", $this->request->post['wp_uid'])) {
117: $post = array('counts['.$wp_id.']' => 5);
118: $res = wp_fetch_content($wp_details, WPCOM_READ_URL, $post);
119: if (!is_array($res) || !array_key_exists('success', $res) || $res['success'] != 1) {
120: Hm_Msgs::add('nable to update read status of WordPress notification', 'danger');
121: }
122: }
123: $this->out('wp_notice_details', $details);
124: }
125: }
126: }
127: }
128: }
129:
130: /**
131: * @subpackage wordpress/handler
132: */
133: class Hm_Handler_wordpress_list_type extends Hm_Handler_Module {
134: public function process() {
135: if (array_key_exists('list_path', $this->request->get)) {
136: $path = $this->request->get['list_path'];
137: $parent = '';
138: if (array_key_exists('list_parent', $this->request->get)) {
139: $parent = $this->request->get['list_parent'];
140: }
141: elseif (in_array($path, array('combined_inbox', 'unread'), true)) {
142: $parent = $path;
143: }
144: if ($path == 'wp_notifications') {
145: $this->out('list_path', 'wp_notifications', false);
146: $this->out('list_parent', $parent);
147: $this->out('mailbox_list_title', array('WordPress.com Notifications'));
148: $this->out('message_list_since', $this->user_config->get('wordpress_since_setting', DEFAULT_WORDPRESS_SINCE));
149: $this->out('per_source_limit', 100);
150: $this->append('data_sources', array('load_wp_notices', 'type' => 'wordpress', 'name' => 'WordPress.com Notifications', 'id' => 0));
151: }
152: else {
153: $this->out('list_path', $path, false);
154: $this->out('list_parent', $parent);
155: }
156: }
157: }
158: }
159:
160: /**
161: * @subpackage wordpress/handler
162: */
163: class Hm_Handler_wp_notification_data extends Hm_Handler_Module {
164: public function process() {
165: $res = array();
166: $wp_details = $this->user_config->get('wp_connect_details', array());
167: $details = wp_get_notifications($wp_details);
168: if (array_key_exists('notes', $details)) {
169: $res = $details['notes'];
170: }
171: $this->out('wp_notice_data', $res);
172: if (array_key_exists('list_path', $this->request->get) && $this->request->get['list_path'] == 'unread') {
173: $this->out('wp_list_since', process_since_argument($this->user_config->get('unread_since_setting', DEFAULT_UNREAD_SINCE)));
174: }
175: elseif (array_key_exists('list_path', $this->request->get) && $this->request->get['list_path'] == 'combined_inbox') {
176: $this->out('wp_list_since', process_since_argument($this->user_config->get('all_since_setting', DEFAULT_ALL_SINCE)));
177: }
178: elseif (array_key_exists('list_path', $this->request->get) && $this->request->get['list_path'] == 'wp_notifications') {
179: $this->out('wp_list_since', process_since_argument($this->user_config->get('wordpress_since_setting', DEFAULT_WORDPRESS_SINCE)));
180: }
181: }
182: }
183:
184: /**
185: * @subpackage wordpress/handler
186: */
187: class Hm_Handler_process_wordpress_authorization extends Hm_Handler_Module {
188: public function process() {
189: if (array_key_exists('state', $this->request->get) && $this->request->get['state'] == 'wp_authorization') {
190: if (array_key_exists('code', $this->request->get)) {
191: $details = wp_connect_details($this->config);
192: $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri']);
193: $result = $oauth2->request_token($details['token_url'], $this->request->get['code']);
194: if (!empty($result) && array_key_exists('access_token', $result)) {
195: Hm_Msgs::add('WordPress.com connection established');
196: $this->user_config->set('wp_connect_details', $result);
197: $user_data = $this->user_config->dump();
198: if (!empty($user_data)) {
199: $this->session->set('user_data', $user_data);
200: }
201: $this->session->record_unsaved('WordPress.com connection');
202: $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
203: $this->session->close_early();
204: }
205: else {
206: Hm_Msgs::add('An Error Occured', 'danger');
207: }
208: }
209: elseif (array_key_exists('error', $this->request->get)) {
210: Hm_Msgs::add(ucwords(str_replace('_', ' ', $this->request->get['error'])), 'danger');
211: }
212: else {
213: Hm_Msgs::add('An Error Occured', 'danger');
214: }
215: $this->save_hm_msgs();
216: Hm_Dispatch::page_redirect('?page=servers');
217: }
218: }
219: }
220:
221: /**
222: * @subpackage wordpress/handler
223: */
224: class Hm_Handler_wordpress_disconnect extends Hm_Handler_Module {
225: public function process() {
226: if (array_key_exists('wp_disconnect', $this->request->post)) {
227: $this->user_config->set('wp_connect_details', array());
228: $user_data = $this->user_config->dump();
229: if (!empty($user_data)) {
230: $this->session->set('user_data', $user_data);
231: }
232: $this->out('reload_folders', true, false);
233: $this->session->record_unsaved('WordPress connection deleted');
234: Hm_Msgs::add('WordPress connection deleted');
235: }
236: }
237: }
238:
239: /**
240: * @subpackage wordpress/handler
241: */
242: class Hm_Handler_setup_wordpress_connect extends Hm_Handler_Module {
243: public function process() {
244: $details = wp_connect_details($this->config);
245: if (!empty($details)) {
246: $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri'] ?? null);
247: $this->out('wp_auth_url', $oauth2->request_authorization_url($details['auth_url'] ?? null, 'global', 'wp_authorization'));
248: $this->out('wp_connect_details', $this->user_config->get('wp_connect_details', array()));
249: }
250: }
251: }
252:
253: /**
254: * @subpackage wordpress/output
255: */
256: class Hm_Output_wordpress_folders extends Hm_Output_Module {
257: protected function output() {
258: $details = $this->get('wp_connect_details', array());
259: if (!empty($details)) {
260: $res = '<li class="menu_wp_notifications"><a class="unread_link" href="?page=message_list&list_path=wp_notifications">';
261: if (!$this->get('hide_folder_icons')) {
262: $res .= '<i class="bi bi-caret-down-fill account_icon"></i> ';
263: }
264: $res .= $this->trans('Notifications').'</a></li>';
265: $this->append('folder_sources', array('wordPress_folders', $res));
266: }
267: return '';
268: }
269: }
270:
271: /**
272: * @subpackage wordpress/output
273: */
274: class Hm_Output_filter_wp_notice_data extends Hm_Output_Module {
275: protected function output() {
276: $data = $this->get('wp_notice_details', array());
277: if (isset($data['notes'][0]['id'])) {
278: $data = $data['notes'][0];
279: $this->out('wp_notice_text', wp_build_notice_text($data['type'], $data['body']));
280: $this->out('wp_notice_headers', wp_build_notice_headers($data, $this));
281: }
282: }
283: }
284:
285: /**
286: * @subpackage wordpress/output
287: */
288: class Hm_Output_filter_wp_notification_data extends Hm_Output_Module {
289: protected function output() {
290: $res = array();
291: $unread_only = false;
292: if ($this->get('list_path', '') == 'unread') {
293: $unread_only = true;
294: }
295: $cutoff = $this->get('wp_list_since', '');
296: if ($cutoff) {
297: $cutoff = strtotime($cutoff);
298: }
299: else {
300: $cutoff = 0;
301: }
302: $show_icons = $this->get('msg_list_icons');
303: foreach ($this->get('wp_notice_data', array()) as $vals) {
304: $row_class = 'wordpress notifications';
305: if (array_key_exists('id', $vals)) {
306: $id = 'wordpress_0_'.$vals['id'];
307: $url = '?page=message&list_path=wp_notifications&uid='.$this->html_safe($id);;
308: if ($this->get('list_parent', '')) {
309: $url .= '&list_parent='.$this->html_safe($this->get('list_parent', ''));
310: }
311: $style = 'email';
312: $style = $this->get('news_list_style') ? 'news' : 'email';
313: if ($this->get('is_mobile')) {
314: $style = 'news';
315: }
316: $subject = html_entity_decode($vals['subject']['text']);
317: if (!$subject) {
318: $subject = '[No subject]';
319: }
320: $from = ucfirst(str_replace('_', ' ', $vals['type']));
321: $ts = intval($vals['timestamp']);
322: if ($ts < $cutoff) {
323: continue;
324: }
325: $flags = array();
326: if ((int) $vals['unread'] > 0) {
327: $row_class .= ' unseen';
328: $flags[] = 'unseen';
329: }
330: if ($unread_only && !in_array('unseen', $flags, true)) {
331: continue;
332: }
333: $icon = '';
334: $date = date('r', $ts);
335: if ($show_icons) {
336: $icon = 'w';
337: }
338: if ($style == 'news') {
339: $res[$id] = message_list_row(array(
340: array('checkbox_callback', $id),
341: array('icon_callback', $flags),
342: array('subject_callback', $subject, $url, $flags, $icon),
343: array('safe_output_callback', 'source', 'WordPress'),
344: array('safe_output_callback', 'from', $from),
345: array('date_callback', human_readable_interval($date), $ts),
346: ),
347: $id,
348: $style,
349: $this,
350: $row_class
351: );
352: }
353: else {
354: $res[$id] = message_list_row(array(
355: array('checkbox_callback', $id),
356: array('safe_output_callback', 'source', 'WordPress', $icon),
357: array('safe_output_callback', 'from', $from),
358: array('subject_callback', $subject, $url, $flags),
359: array('date_callback', human_readable_interval($date), $ts),
360: array('icon_callback', $flags)
361: ),
362: $id,
363: $style,
364: $this,
365: $row_class
366: );
367: }
368: }
369: }
370: $this->out('formatted_message_list', $res);
371: }
372: }
373:
374: /**
375: * @subpackage wordpress/output
376: */
377: class Hm_Output_unread_wp_included_setting extends Hm_Output_Module {
378: protected function output() {
379: $settings = $this->get('user_settings');
380: if (array_key_exists('unread_exclude_wordpress', $settings) && $settings['unread_exclude_wordpress']) {
381: $checked = ' checked="checked"';
382: }
383: else {
384: $checked = '';
385: }
386: return '<tr class="unread_setting"><td><label for="unread_exclude_wordpress">'.$this->trans('Exclude unread WordPress notices').'</label></td>'.
387: '<td><input class="form-check-input" type="checkbox" '.$checked.' value="1" id="unread_exclude_wordpress" name="unread_exclude_wordpress" /></td></tr>';
388: }
389: }
390:
391: /**
392: * @subpackage wordpress/output
393: */
394: class Hm_Output_start_wordpress_settings extends Hm_Output_Module {
395: protected function output() {
396: return '<tr><td colspan="2" data-target=".wp_notifications_setting" class="settings_subtitle cursor-pointer border-bottom p-2">'.
397: '<i class="bi bi-wordpress fs-5 me-2"></i>'.$this->trans('WordPress.com Settings').'</td></tr>';
398: }
399: }
400:
401: /**
402: * @subpackage wordpress/output
403: */
404: class Hm_Output_wordpress_since_setting extends Hm_Output_Module {
405: protected function output() {
406: $since = DEFAULT_WORDPRESS_SINCE;
407: $settings = $this->get('user_settings');
408: if (array_key_exists('wordpress_since', $settings) && $settings['wordpress_since']) {
409: $since = $settings['wordpress_since'];
410: }
411: return '<tr class="wp_notifications_setting"><td><label for="wordpress_since">'.$this->trans('Show WordPress.com notices received since').'</label></td>'.
412: '<td>'.message_since_dropdown($since, 'wordpress_since', $this, DEFAULT_WORDPRESS_SINCE).'</td></tr>';
413: }
414: }
415:
416: /**
417: * @subpackage wordpress/output
418: */
419: class Hm_Output_wordpress_connect_section extends Hm_Output_Module {
420: protected function output() {
421: $details = $this->get('wp_connect_details', array());
422:
423: $res = '<div class="wordpress_connect"><div data-target=".wordpress_connect_section" class="server_section border-bottom cursor-pointer px-1 py-3 pe-auto">
424: <a href="#" class="pe-auto">
425: <i class="bi bi-wordpress me-3"></i>
426: <b>'.$this->trans('WordPress.com Connect').'</b>
427: </a>
428: </div>';
429:
430: $res .= '<div class="wordpress_connect_section">';
431:
432: if (empty($details)) {
433: $res .= 'Connect to WordPress.com to view notifications and posts.<br /><br />';
434: $res .= '<a class="btn btn-secondary" href="'.$this->get('wp_auth_url', '').'">'.$this->trans('Enable').'</a></div></div>';
435: }
436: else {
437: $res .= $this->trans('Already connected');
438: $res .= '<br /><form id="wp_disconnect_form" method="POST">';
439: $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
440: $res .= '<input type="submit" name="wp_disconnect" class="wp_disconnect" value="'.$this->trans('Disconnect').'" />';
441: $res .= '</form></div></div>';
442: }
443: return $res;
444: }
445: }
446:
447: /**
448: * @subpackage wordpress/functions
449: */
450: if (!hm_exists('wp_connect_details')) {
451: function wp_connect_details($config) {
452: return $config->get('wordpress', array());
453: }}
454:
455: /**
456: * @subpackage wordpress/functions
457: */
458: if (!hm_exists('wp_build_notice_headers')) {
459: function wp_build_notice_headers($data, $output_mod) {
460: $subject = $data['subject']['text'];
461: if (!$subject) {
462: $subject = '[No subject]';
463: }
464: $txt = '';
465: $txt .= '<div class="container-fluid p-0 ml-0 border-bottom border-secondary-subtle text-muted">';
466: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
467: $txt .= '<div class="col-12">';
468: $txt .= '<span class="fs-5 fw-normal text-dark">' . $output_mod->html_safe(html_entity_decode($data['subject']['text'])) . '</span>';
469: $txt .= '</div></div>';
470: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
471: $txt .= '<div class="col-md-2 d-none d-md-block"><span class="text-muted">'.$output_mod->trans('Date').'</span></div>';
472: $txt .= '<div class="col-md-10"><small class="text-muted">'.date('r', $data['timestamp']).' ('.human_readable_interval(date('r', $data['timestamp'])).')</small></div>';
473: $txt .= '</div>';
474: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
475: $txt .= '<div class="col-md-2 d-none d-md-block"><span class="text-muted">'.$output_mod->trans('Type').'</span></div>';
476: $txt .= '<div class="col-md-10">'.$output_mod->html_safe($data['type']).'</div>';
477: $txt .= '</div>';
478: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
479: $txt .= '<div class="col-md-2 d-none d-md-block"><span class="text-muted">'.$output_mod->trans('Id').'</span></div>';
480: $txt .= '<div class="col-md-10">'.$output_mod->html_safe($data['id']).'</div>';
481: $txt .= '</div>';
482: $txt .= '</div>';
483: return $txt;
484: }}
485:
486: /**
487: * @subpackage wordpress/functions
488: */
489: if (!hm_exists('wp_build_notice_text')) {
490: function wp_build_notice_text($type, $data) {
491: $res = array();
492: if ($type == 'comment') {
493: foreach ($data['items'] as $vals) {
494: $res[] = $vals['header_text'].'<br />'.$vals['html'];
495: }
496: }
497: return '<div class="msg_text_inner">'.format_msg_html(implode('<div class="hr"></div>', $res)).'</div>';
498: }}
499:
500: /**
501: * @subpackage wordpress/functions
502: */
503: if (!hm_exists('wp_get_notifications')) {
504: function wp_get_notifications($details) {
505: $result = array();
506: return wp_fetch_content($details, WPCOM_NOTICES_URL);
507: }}
508:
509: /**
510: * @subpackage wordpress/functions
511: */
512: if (!hm_exists('wp_get_notice_detail')) {
513: function wp_get_notice_detail($details, $uid) {
514: $uid = (int) $uid;
515: return wp_fetch_content($details, WPCOM_NOTICE_URL.$uid);
516: }}
517:
518: /**
519: * @subpackage wordpress/functions
520: */
521: if (!hm_exists('wp_fetch_content')) {
522: function wp_fetch_content($details, $url, $post=array()) {
523: if (!is_array($details) || empty($details) || !array_key_exists('access_token', $details)) {
524: return array();
525: }
526: $api = new Hm_API_Curl();
527: return $api->command($url, array('Authorization: Bearer ' . $details['access_token']), $post);
528: }}
529: