PluginProbe
Pushly / trunk
Pushly vtrunk
2.4.0 2.3.0 trunk 1.0 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 2.0.0 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1
pushly / src / php / Admin / ActivityLogController.php

ActivityLogController.php in Pushly trunk, at src/php/Admin/ActivityLogController.php

201 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Pushly\Admin;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 class ActivityLogController {
10
11 /**
12 * @var LogStore
13 */
14 private $store;
15
16 /**
17 * @param LogStore $store
18 */
19 public function __construct( LogStore $store ) {
20 $this->store = $store;
21 }
22
23 /**
24 * Registers the REST API init hook.
25 */
26 public function register_hooks(): void {
27 add_action( 'rest_api_init', [ $this, 'register_routes' ] );
28 }
29
30 /**
31 * Registers REST API routes under pushly/v1.
32 */
33 public function register_routes(): void {
34 register_rest_route( 'pushly/v1', '/activity-log', [
35 'methods' => 'GET',
36 'callback' => [ $this, 'get_entries' ],
37 'permission_callback' => [ $this, 'check_permissions' ],
38 'args' => [
39 'page' => [
40 'type' => 'integer',
41 'default' => 1,
42 'sanitize_callback' => 'absint',
43 ],
44 'severity' => [
45 'type' => 'string',
46 'default' => '',
47 'sanitize_callback' => 'sanitize_text_field',
48 ],
49 'search' => [
50 'type' => 'string',
51 'default' => '',
52 'sanitize_callback' => 'sanitize_text_field',
53 ],
54 ],
55 ] );
56
57 register_rest_route( 'pushly/v1', '/activity-log/export', [
58 'methods' => 'GET',
59 'callback' => [ $this, 'export_entries' ],
60 'permission_callback' => [ $this, 'check_permissions' ],
61 'args' => [
62 'severity' => [
63 'type' => 'string',
64 'default' => '',
65 'sanitize_callback' => 'sanitize_text_field',
66 ],
67 'search' => [
68 'type' => 'string',
69 'default' => '',
70 'sanitize_callback' => 'sanitize_text_field',
71 ],
72 ],
73 ] );
74
75 register_rest_route( 'pushly/v1', '/activity-log/clear', [
76 'methods' => 'DELETE',
77 'callback' => [ $this, 'clear_entries' ],
78 'permission_callback' => [ $this, 'check_permissions' ],
79 ] );
80 }
81
82 /**
83 * GET pushly/v1/activity-log
84 *
85 * Returns paginated, filterable log entries.
86 *
87 * @param \WP_REST_Request $request
88 * @return \WP_REST_Response
89 */
90 public function get_entries( \WP_REST_Request $request ): \WP_REST_Response {
91 $page = $request->get_param( 'page' );
92 $severity = $request->get_param( 'severity' );
93 $search = $request->get_param( 'search' );
94 $severities = null;
95
96 if ( ! empty( $severity ) ) {
97 $severities = array_map( 'trim', explode( ',', $severity ) );
98 $severities = array_filter( $severities );
99 }
100
101 $search = ! empty( $search ) ? $search : null;
102
103 $result = $this->store->query( $page, 50, $severities, $search );
104
105 // Decode context JSON strings into objects for the response.
106 $entries = array_map( function ( $entry ) {
107 if ( ! empty( $entry->context ) ) {
108 $decoded = json_decode( $entry->context );
109 if ( json_last_error() === JSON_ERROR_NONE ) {
110 $entry->context = $decoded;
111 }
112 }
113 return $entry;
114 }, $result['entries'] );
115
116 return new \WP_REST_Response( [
117 'entries' => $entries,
118 'total' => $result['total'],
119 'page' => $result['page'],
120 'per_page' => $result['per_page'],
121 'total_pages' => $result['total_pages'],
122 ] );
123 }
124
125 /**
126 * GET pushly/v1/activity-log/export
127 *
128 * Returns all matching log entries as a downloadable plain-text file.
129 *
130 * @param \WP_REST_Request $request
131 * @return \WP_REST_Response
132 */
133 public function export_entries( \WP_REST_Request $request ): \WP_REST_Response {
134 $severity = $request->get_param( 'severity' );
135 $search = $request->get_param( 'search' );
136 $severities = null;
137
138 if ( ! empty( $severity ) ) {
139 $severities = array_map( 'trim', explode( ',', $severity ) );
140 $severities = array_filter( $severities );
141 }
142
143 $search = ! empty( $search ) ? $search : null;
144 $entries = $this->store->query_all( $severities, $search );
145
146 $lines = [];
147 foreach ( $entries as $entry ) {
148 $context_json = '';
149 if ( ! empty( $entry->context ) ) {
150 $context_json = $entry->context;
151 }
152
153 $lines[] = sprintf(
154 '[%s UTC] [%s] [%s] %s | context: %s',
155 $entry->timestamp,
156 strtoupper( $entry->severity ),
157 $entry->event_type,
158 $entry->message,
159 $context_json
160 );
161 }
162
163 $body = implode( "\n", $lines );
164 $filename = 'pushly-debug-log-' . gmdate( 'Y-m-d' ) . '.txt';
165
166 $response = new \WP_REST_Response( $body );
167 $response->header( 'Content-Type', 'text/plain; charset=utf-8' );
168 $response->header( 'Content-Disposition', 'attachment; filename="' . $filename . '"' );
169
170 return $response;
171 }
172
173 /**
174 * DELETE pushly/v1/activity-log/clear
175 *
176 * Removes all log entries and returns the count of deleted entries.
177 *
178 * @param \WP_REST_Request $request
179 * @return \WP_REST_Response
180 */
181 public function clear_entries( \WP_REST_Request $request ): \WP_REST_Response {
182 $count = $this->store->count();
183 $this->store->truncate();
184
185 return new \WP_REST_Response( [
186 'success' => true,
187 'deleted' => $count,
188 ] );
189 }
190
191 /**
192 * Permission callback for all endpoints.
193 * Requires the manage_options capability.
194 *
195 * @return bool
196 */
197 public function check_permissions(): bool {
198 return current_user_can( 'manage_options' );
199 }
200 }
201