PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.4
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.4
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / code-blue / rest.php

rest.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.4, at includes/code-blue/rest.php

264 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Code Blue: REST routes.
4 *
5 * Three endpoints under `desktop-mode/v1/code-blue`:
6 *
7 * GET /sources
8 * Log sources this install offers, plus the environment card
9 * (debug constants, PHP/WP versions, environment type).
10 *
11 * GET /entries?source=<id>
12 * Parsed entries from one source's trailing window:
13 * `{ source, entries, truncated, scanned_bytes,
14 * dropped_entries, generated_at }`.
15 *
16 * DELETE /entries?source=<id>
17 * Truncates the source's file to zero bytes.
18 *
19 * Every route sits behind `openstation_code_blue_user_can_use()`
20 * (manage_options by default) — log content leaks server paths and
21 * SQL, so this is deliberately an administrators-only surface.
22 *
23 * @package OpenStation
24 */
25
26 defined( 'ABSPATH' ) || exit;
27
28 /**
29 * Capability check shared across every endpoint.
30 *
31 * @return bool
32 */
33 function openstation_code_blue_rest_permission() {
34 return openstation_code_blue_user_can_use();
35 }
36
37 /**
38 * Register the routes.
39 */
40 function openstation_code_blue_register_routes() {
41 register_rest_route(
42 'desktop-mode/v1',
43 '/code-blue/sources',
44 array(
45 'methods' => WP_REST_Server::READABLE,
46 'callback' => 'openstation_code_blue_rest_sources',
47 'permission_callback' => 'openstation_code_blue_rest_permission',
48 )
49 );
50 register_rest_route(
51 'desktop-mode/v1',
52 '/code-blue/entries',
53 array(
54 array(
55 'methods' => WP_REST_Server::READABLE,
56 'callback' => 'openstation_code_blue_rest_entries',
57 'permission_callback' => 'openstation_code_blue_rest_permission',
58 'args' => array(
59 'source' => array(
60 'description' => 'Log source id from GET /sources.',
61 'type' => 'string',
62 'required' => true,
63 ),
64 ),
65 ),
66 array(
67 'methods' => WP_REST_Server::DELETABLE,
68 'callback' => 'openstation_code_blue_rest_clear',
69 'permission_callback' => 'openstation_code_blue_rest_permission',
70 'args' => array(
71 'source' => array(
72 'description' => 'Log source id from GET /sources.',
73 'type' => 'string',
74 'required' => true,
75 ),
76 ),
77 ),
78 )
79 );
80 }
81 add_action( 'rest_api_init', 'openstation_code_blue_register_routes' );
82
83 /**
84 * The environment card: the switches and versions an error hunter
85 * checks first, as `{ key, label, value, on }` rows. `on` is null
86 * for rows that are informational rather than toggles.
87 *
88 * @return array[]
89 */
90 function openstation_code_blue_environment() {
91 $rows = array();
92 foreach ( array( 'WP_DEBUG', 'WP_DEBUG_LOG', 'WP_DEBUG_DISPLAY', 'SCRIPT_DEBUG', 'SAVEQUERIES' ) as $constant ) {
93 $on = defined( $constant ) && constant( $constant );
94 $rows[] = array(
95 'key' => strtolower( $constant ),
96 'label' => $constant,
97 'value' => $on ? 'on' : 'off',
98 'on' => $on,
99 );
100 }
101
102 $rows = array_merge(
103 $rows,
104 array(
105 array(
106 'key' => 'environment',
107 'label' => __( 'Environment', 'desktop-mode' ),
108 'value' => wp_get_environment_type(),
109 'on' => null,
110 ),
111 array(
112 'key' => 'php',
113 'label' => 'PHP',
114 'value' => PHP_VERSION,
115 'on' => null,
116 ),
117 array(
118 'key' => 'wordpress',
119 'label' => 'WordPress',
120 'value' => get_bloginfo( 'version' ),
121 'on' => null,
122 ),
123 )
124 );
125
126 /**
127 * Filter the environment rows shown in the Code Blue window.
128 *
129 * @param array[] $rows Each: `key`, `label`, `value` (string),
130 * `on` (bool|null — null renders neutral).
131 */
132 return (array) apply_filters( 'openstation_code_blue_environment', $rows );
133 }
134
135 /**
136 * GET /sources
137 *
138 * @return WP_REST_Response
139 */
140 function openstation_code_blue_rest_sources() {
141 return rest_ensure_response(
142 array(
143 'sources' => openstation_code_blue_log_sources(),
144 'environment' => openstation_code_blue_environment(),
145 )
146 );
147 }
148
149 /**
150 * Resolve the `source` param to a readable descriptor or an error.
151 *
152 * @param WP_REST_Request $request Request.
153 * @return array|WP_Error
154 */
155 function openstation_code_blue_rest_resolve_source( WP_REST_Request $request ) {
156 $id = sanitize_key( (string) $request->get_param( 'source' ) );
157 $source = openstation_code_blue_get_source( $id );
158 if ( null === $source ) {
159 return new WP_Error(
160 'openstation_code_blue_unknown_source',
161 __( 'Unknown log source.', 'desktop-mode' ),
162 array( 'status' => 404 )
163 );
164 }
165 return $source;
166 }
167
168 /**
169 * GET /entries?source=<id>
170 *
171 * @param WP_REST_Request $request Request.
172 * @return WP_REST_Response|WP_Error
173 */
174 function openstation_code_blue_rest_entries( WP_REST_Request $request ) {
175 $source = openstation_code_blue_rest_resolve_source( $request );
176 if ( is_wp_error( $source ) ) {
177 return $source;
178 }
179
180 // A registered source whose file doesn't exist yet (debug log
181 // before the first error is written) is an EMPTY log, not an
182 // error condition.
183 if ( ! $source['exists'] ) {
184 return rest_ensure_response(
185 array(
186 'source' => $source,
187 'entries' => array(),
188 'truncated' => false,
189 'scanned_bytes' => 0,
190 'dropped_entries' => 0,
191 'generated_at' => time(),
192 )
193 );
194 }
195 if ( ! $source['readable'] ) {
196 return new WP_Error(
197 'openstation_code_blue_unreadable',
198 __( 'The log file exists but PHP cannot read it.', 'desktop-mode' ),
199 array( 'status' => 500 )
200 );
201 }
202
203 $read = openstation_code_blue_read_source( $source );
204
205 return rest_ensure_response(
206 array(
207 'source' => $source,
208 'entries' => $read['entries'],
209 'truncated' => $read['truncated'],
210 'scanned_bytes' => $read['scanned_bytes'],
211 'dropped_entries' => $read['dropped_entries'],
212 'generated_at' => time(),
213 )
214 );
215 }
216
217 /**
218 * DELETE /entries?source=<id> — truncate the log file.
219 *
220 * @param WP_REST_Request $request Request.
221 * @return WP_REST_Response|WP_Error
222 */
223 function openstation_code_blue_rest_clear( WP_REST_Request $request ) {
224 $source = openstation_code_blue_rest_resolve_source( $request );
225 if ( is_wp_error( $source ) ) {
226 return $source;
227 }
228
229 // Clearing a log whose file doesn't exist is a no-op success.
230 if ( ! $source['exists'] ) {
231 return rest_ensure_response( array( 'cleared' => true ) );
232 }
233 if ( ! $source['writable'] ) {
234 return new WP_Error(
235 'openstation_code_blue_unwritable',
236 __( 'The log file is not writable, so it cannot be cleared.', 'desktop-mode' ),
237 array( 'status' => 500 )
238 );
239 }
240
241 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Truncating a server-side log the descriptor already validated; WP_Filesystem adds nothing here.
242 $written = file_put_contents( $source['path'], '' );
243 if ( false === $written ) {
244 return new WP_Error(
245 'openstation_code_blue_clear_failed',
246 __( 'Clearing the log file failed.', 'desktop-mode' ),
247 array( 'status' => 500 )
248 );
249 }
250
251 /**
252 * Fires after the Code Blue window truncates a log file.
253 *
254 * @param string $id Source id.
255 * @param string $path Absolute file path that was truncated.
256 */
257 do_action( 'openstation_code_blue_log_cleared', $source['id'], $source['path'] );
258
259 // Just the flag — the client refreshes its picker from
260 // GET /sources after a clear, so a descriptor here would be
261 // dead weight built by a second discovery pass.
262 return rest_ensure_response( array( 'cleared' => true ) );
263 }
264