PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/controller/class-audit-logging.php +537 -71 6.1.0 → 6.3.0 View file →
@@ -6,10 +6,24 @@
6 6 */
7 7
8 8 namespace WP_Defender\Controller;
9 9
10 +use DateTime;
11 +use Exception;
12 +use Generator;
10 13 use WP_Defender\Event;
14 +use WP_Defender\Traits\User;
15 +use Calotes\Component\Request;
11 16 use Calotes\Component\Response;
17 +use Calotes\Helper\Array_Cache;
18 +use WP_Defender\Traits\Formats;
19 +use WP_Defender\Component\Audit;
20 +use WP_Defender\Model\Audit_Log;
21 +use WP_Defender\Behavior\WPMUDEV;
22 +use WP_Defender\Component\Network_Cron_Manager;
23 +use WP_Defender\Model\Notification\Audit_Report;
24 +use WP_Defender\Component\Export\Csv;
25 +use WP_Defender\Component\Config\Config_Hub_Helper;
12 26 use WP_Defender\Model\Setting\Audit_Logging as Model_Audit_Logging;
13 27
14 28 /**
15 29 * Handle Audit Logging module.
@@ -15,8 +29,13 @@
15 29 * Handle Audit Logging module.
16 30 */
17 31 class Audit_Logging extends Event {
18 32
33 + public const DEFAULT_PER_PAGE = 10;
34 +
35 + use User;
36 + use Formats;
37 +
19 38 /**
20 39 * The slug identifier for this controller.
21 40 *
22 41 * @var string
@@ -30,10 +49,24 @@
30 49 */
31 50 public $model;
32 51
33 52 /**
34 - * Initializes the model, registers the menu page and routes.
53 + * Service for handling logic.
54 + *
55 + * @var Audit|null
35 56 */
57 + public ?Audit $service;
58 +
59 + /**
60 + * Indicates whether the current installation is a pro version.
61 + *
62 + * @var bool
63 + */
64 + private $is_pro;
65 +
66 + /**
67 + * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
68 + */
36 69 public function __construct() {
37 70 $this->register_page(
38 71 esc_html__( 'Audit Log', 'defender-security' ),
39 72 $this->slug,
@@ -40,13 +73,325 @@
40 73 array( $this, 'main_view' ),
41 74 $this->parent_slug
42 75 );
43 76 add_action( 'defender_enqueue_assets', array( $this, 'enqueue_assets' ) );
44 - $this->model = wd_di()->get( Model_Audit_Logging::class );
77 + $this->model = wd_di()->get( Model_Audit_Logging::class );
78 + $this->service = new Audit();
45 79 $this->register_routes();
80 + $this->is_pro = wd_di()->get( WPMUDEV::class )->is_pro();
81 + if ( $this->is_active() ) {
82 + $this->service->enqueue_event_listener();
83 + add_action( 'shutdown', array( $this, 'cache_audit_logs' ) );
84 +
85 + /**
86 + * Network Cron Manager
87 + *
88 + * @var Network_Cron_Manager $network_cron_manager
89 + */
90 + $network_cron_manager = wd_di()->get( Network_Cron_Manager::class );
91 + $network_cron_manager->register_callback(
92 + 'audit_clean_up_logs',
93 + array( $this->service, 'audit_clean_up_logs' ),
94 + HOUR_IN_SECONDS
95 + );
96 + }
46 97 }
47 98
48 99 /**
100 + * Is there permission to Pro feature?
101 + *
102 + * @return bool
103 + */
104 + private function is_active(): bool {
105 + return $this->model->is_active();
106 + }
107 +
108 + /**
109 + * Parses a raw request params array, resolves filters, and fetches audit logs.
110 + *
111 + * Accepted keys (all optional except date range):
112 + * date_from — date string or empty (defaults to -7 days midnight).
113 + * date_to — date string or empty (defaults to today 23:59:59).
114 + * events — array of event type strings (also accepts key 'event_type' for CSV route).
115 + * username — login name (also accepts key 'term' for CSV route).
116 + * ip_address — IP filter string.
117 + * paged — page number; pass false for unbounded export.
118 + *
119 + * Returns the resolved user_id via the $user_id out-param so callers can use it for COUNT queries.
120 + *
121 + * @param array $params Raw request data.
122 + * @param mixed $paged Page number or false for all results.
123 + * @param int|string $user_id Populated with the resolved user ID (out-param).
124 + * @param int $per_page Number of logs per page.
125 + *
126 + * @return array|\WP_Error
127 + */
128 + private function fetch_logs( array $params, $paged, &$user_id = '', $per_page = self::DEFAULT_PER_PAGE ) {
129 + $date_from_str = $params['date_from'] ?? '';
130 + $date_to_str = $params['date_to'] ?? '';
131 + $date_from = $date_from_str ? $this->date_string_to_timestamp( $date_from_str ) : strtotime( '-7 days midnight' );
132 + $date_to = $date_to_str ? $this->date_string_to_timestamp( $date_to_str, true ) : strtotime( 'today 23:59:59' );
133 + $events = $params['events'] ?? ( isset( $params['event_type'] ) && is_array( $params['event_type'] ) ? $params['event_type'] : array() );
134 + $username = $params['username'] ?? ( $params['term'] ?? '' );
135 + $ip_address = $params['ip_address'] ?? '';
136 + $user_id = '';
137 +
138 + if ( '' !== $username ) {
139 + $user = get_user_by( 'login', $username );
140 + if ( ! is_object( $user ) ) {
141 + // Try matching by display_name or email before giving up.
142 + $users = get_users(
143 + array(
144 + 'search' => '*' . $username . '*',
145 + 'search_columns' => array( 'display_name' ),
146 + 'number' => 1,
147 + )
148 + );
149 + if ( array() === $users ) {
150 + return array();
151 + }
152 + $user_id = $users[0]->ID;
153 + } else {
154 + $user_id = $user->ID;
155 + }
156 + }
157 +
158 + return $this->service->fetch( $date_from, $date_to, $events, $user_id, $ip_address, $paged, $per_page );
159 + }
160 +
161 + /**
162 + * Prefetches users from a result set then formats each log into an array.
163 + *
164 + * @param array $result Array of Audit_Log objects.
165 + *
166 + * @return array
167 + */
168 + private function format_log_rows( array $result ): array {
169 + $user_ids = array_filter(
170 + array_unique( array_column( $result, 'user_id' ) ),
171 + static fn ( $user_id ): bool => (int) $user_id > 0
172 + );
173 + if ( array() !== $user_ids ) {
174 + get_users( array( 'include' => $user_ids ) );
175 + }
176 +
177 + $logs = array();
178 + foreach ( $result as $item ) {
179 + $item_user_id = isset( $item->user_id ) ? $item->user_id : 0;
180 + $item_user_id = ! is_int( $item_user_id ) ? (int) $item_user_id : $item_user_id;
181 + $logs[] = array_merge(
182 + $item->export(),
183 + array(
184 + 'user' => $this->get_user_display( $item->user_id ),
185 + 'user_url' => $item_user_id > 0 ? get_edit_user_link( $item_user_id ) : '',
186 + 'log_date' => $this->get_date( $item->timestamp ),
187 + 'format_date' => $this->format_date_time( $item->timestamp ),
188 + )
189 + );
190 + }
191 +
192 + return $logs;
193 + }
194 +
195 + /**
196 + * Exports audit logs as a CSV file.
197 + *
198 + * @return void
199 + * @throws Exception If there is an error during export.
200 + * @defender_route
201 + */
202 + public function export_as_csv(): void {
203 + $params = defender_get_data_from_request( null, 'g' );
204 + $user_id = '';
205 + $result = $this->fetch_logs( $params, 1, $user_id, 1000 );
206 +
207 + if ( is_wp_error( $result ) ) {
208 + wp_send_json_error(
209 + array(
210 + 'message' => $result->get_error_message(),
211 + )
212 + );
213 + }
214 +
215 + $headers = array(
216 + esc_html__( 'Summary', 'defender-security' ),
217 + esc_html__( 'Date / Time', 'defender-security' ),
218 + esc_html__( 'Context', 'defender-security' ),
219 + esc_html__( 'Type', 'defender-security' ),
220 + esc_html__( 'IP address', 'defender-security' ),
221 + esc_html__( 'User', 'defender-security' ),
222 + );
223 + try {
224 + Csv::to_browser( 'audit-logs', $this->get_export_rows( $params, $result ), $headers );
225 + } catch ( \RuntimeException $e ) {
226 + $this->log( $e->getMessage(), 'audit-logging' );
227 + }
228 + }
229 +
230 + /**
231 + * Generates audit log export rows in database-backed pages.
232 + *
233 + * @param array $params Request filters.
234 + * @param array $logs First page of audit logs.
235 + *
236 + * @return Generator
237 + * @throws Exception If there is an error while fetching logs.
238 + */
239 + private function get_export_rows( array $params, array $logs ): Generator {
240 + $page = 1;
241 + $per_page = 1000;
242 + $user_id = '';
243 +
244 + do {
245 + if ( 1 < $page ) {
246 + $logs = $this->fetch_logs( $params, $page, $user_id, $per_page );
247 + if ( is_wp_error( $logs ) ) {
248 + throw new Exception( esc_html( $logs->get_error_message() ) );
249 + }
250 + }
251 +
252 + foreach ( $this->format_log_rows( $logs ) as $row ) {
253 + yield array(
254 + $row['msg'],
255 + is_array( $row['timestamp'] )
256 + ? $this->format_date_time( $row['timestamp'][0] )
257 + : $this->format_date_time( $row['timestamp'] ),
258 + $row['context'],
259 + $row['action_type'],
260 + $row['ip'],
261 + $row['user'],
262 + );
263 + }
264 +
265 + $has_more = count( $logs ) === $per_page;
266 + ++$page;
267 + } while ( $has_more );
268 + }
269 +
270 + /**
271 + * We'll pass all the event logs into the db handler, so it writes down to db.
272 + * Do it in shutdown runtime, so no delay time.
273 + *
274 + * @return void
275 + */
276 + public function cache_audit_logs(): void {
277 + $audit = new Audit();
278 + $audit->log_audit_events();
279 + }
280 +
281 + /**
282 + * Pull the logs from db cached:
283 + * - date_from: the start of the date we will run the query, as mysql time format,
284 + * - date_to: similar to the above,
285 + * others will refer to Audit.
286 + *
287 + * @param Request $request The request object containing filter parameters.
288 + *
289 + * @return Response
290 + * @throws Exception If there is an error during log retrieval.
291 + * @defender_route
292 + */
293 + public function pull_logs( Request $request ): Response {
294 + $data = $request->get_data(
295 + array(
296 + 'date_from' => array(
297 + 'type' => 'string',
298 + 'sanitize' => 'sanitize_text_field',
299 + ),
300 + 'date_to' => array(
301 + 'type' => 'string',
302 + 'sanitize' => 'sanitize_text_field',
303 + ),
304 + 'username' => array(
305 + 'type' => 'string',
306 + 'sanitize' => 'sanitize_text_field',
307 + ),
308 + 'events' => array(
309 + 'type' => 'array',
310 + 'sanitize' => 'sanitize_text_field',
311 + ),
312 + 'ip_address' => array(
313 + 'type' => 'string',
314 + 'sanitize' => 'sanitize_text_field',
315 + ),
316 + 'paged' => array(
317 + 'type' => 'int',
318 + 'sanitize' => 'sanitize_text_field',
319 + ),
320 + 'per_page' => array(
321 + 'type' => 'int',
322 + 'sanitize' => 'sanitize_text_field',
323 + ),
324 + )
325 + );
326 + if ( ! isset( $data['date_from'] ) || ! isset( $data['date_to'] ) ) {
327 + return new Response( false, array( 'message' => esc_html__( 'Invalid data.', 'defender-security' ) ) );
328 + }
329 + // Validate date strings before passing to fetch_logs.
330 + $date_from_check = $this->date_string_to_timestamp( $data['date_from'] );
331 + $date_to_check = $this->date_string_to_timestamp( $data['date_to'], true );
332 + if ( $date_from_check <= 0 || $date_to_check <= 0 ) {
333 + return new Response( false, array( 'message' => esc_html__( 'Invalid data.', 'defender-security' ) ) );
334 + }
335 +
336 + $paged = $data['paged'] ?? 1;
337 + $per_page = min( 100, max( 1, (int) ( $data['per_page'] ?? self::DEFAULT_PER_PAGE ) ) );
338 + $result = $this->fetch_logs( $data, $paged, $user_id, $per_page );
339 +
340 + if ( is_wp_error( $result ) ) {
341 + return new Response( false, array( 'message' => $result->get_error_message() ) );
342 + }
343 + $logs = is_array( $result ) && array() !== $result ? $this->format_log_rows( $result ) : array();
344 +
345 + // @since 3.0.0 If no logs then $count = 0.
346 + // Skip the COUNT query when the result is a partial page — the fetch already told us the total is count($logs).
347 + $events = $data['events'] ?? array();
348 + $ip_address = $data['ip_address'] ?? '';
349 + if ( array() === $logs ) {
350 + $count = 0;
351 + } elseif ( count( $logs ) < $per_page ) {
352 + $count = ( $paged - 1 ) * $per_page + count( $logs );
353 + } else {
354 + $count = Audit_Log::count( $date_from_check, $date_to_check, $events, $user_id, $ip_address );
355 + }
356 +
357 + // Get the count for the submitted data.
358 + return new Response(
359 + true,
360 + array(
361 + 'logs' => $logs,
362 + 'total_items' => $count,
363 + 'total_pages' => ceil( $count / $per_page ),
364 + 'per_page' => $per_page,
365 + )
366 + );
367 + }
368 +
369 + /**
370 + * Generates a human-readable frequency text for audit reports.
371 + *
372 + * @param Audit_Report $audit_report The audit report object.
373 + *
374 + * @return string Returns the formatted frequency description.
375 + */
376 + public function get_frequency_text( Audit_Report $audit_report ): string {
377 + $text = '';
378 + switch ( $audit_report->frequency ) {
379 + case 'daily':
380 + $text = ucfirst( $audit_report->day ) . 's at ' . $audit_report->time;
381 + break;
382 + case 'weekly':
383 + case 'monthly':
384 + $text = ucfirst( $audit_report->frequency ) . ' on ' . ucfirst( $audit_report->day ) . 's at ' . $audit_report->time;
385 + break;
386 + default:
387 + break;
388 + }
389 +
390 + return $text;
391 + }
392 +
393 + /**
49 394 * Enqueues scripts and styles for this page.
50 395 */
51 396 public function enqueue_assets(): void {
52 397 if ( ! $this->is_page_active() ) {
@@ -60,8 +405,9 @@
60 405 array( 'def-vue', 'def-manifest', 'def-core-ui', 'defender', 'wp-i18n' ),
61 406 DEFENDER_VERSION,
62 407 true
63 408 );
409 + wp_set_script_translations( $handle, 'wpdef' );
64 410
65 411 wp_localize_script(
66 412 $handle,
67 413 'defenderUIData',
@@ -72,9 +418,9 @@
72 418 );
73 419
74 420 wp_enqueue_style(
75 421 $handle,
76 - WP_DEFENDER_BASE_URL . 'assets/css/showcase.css',
422 + WP_DEFENDER_BASE_URL . 'assets/css/core-ui.css',
77 423 array(),
78 424 DEFENDER_VERSION
79 425 );
80 426
@@ -90,140 +436,260 @@
90 436 $this->render( 'main' );
91 437 }
92 438
93 439 /**
94 - * Dummy — returns empty logs response.
440 + * Provides a summary of audit logs.
95 441 *
96 - * @return Response
442 + * @return void
443 + * @throws Exception If there is an error during summary generation.
97 444 * @defender_route
98 445 */
99 - public function pull_logs(): Response {
100 - return new Response( false, array() );
446 + public function summary(): void {
447 + $response = $this->is_active() ? $this->summary_data() : array();
448 + wp_send_json_success( $response );
101 449 }
102 450
103 451 /**
104 - * Dummy — no-op CSV export.
452 + * Returns an array with summary data for audit logging.
105 453 *
106 - * @defender_route
454 + * @param bool $for_hub Default 'false' because it's displayed on site summary sections.
455 + *
456 + * @return array
457 + * @throws Exception Emits Exception in case of an error.
107 458 */
108 - public function export_as_csv(): void {
459 + public function summary_data( bool $for_hub = false ): array {
460 + $timezone = wp_timezone();
461 +
462 + // Monthly count.
463 + $date_from = ( new DateTime( '-30 days', $timezone ) )
464 + ->setTime( 0, 0, 0 )
465 + ->getTimestamp();
466 + $date_to = ( new DateTime( 'now', $timezone ) )
467 + ->setTime( 23, 59, 59 )
468 + ->getTimestamp();
469 + $month_count = Audit_Log::count( $date_from, $date_to );
470 +
471 + // Weekly count sets in the returned array.
472 + // Daily count. Sync data to the Hub without timezone.
473 + $date_from = $for_hub ? new DateTime( 'now' ) : new DateTime( 'now', wp_timezone() );
474 + $date_from = $date_from->modify( '-24 hours' )->setTime( 0, 0, 0 )->getTimestamp();
475 + $day_count = Audit_Log::count( $date_from, $date_to );
476 +
477 + // Get the last item.
478 + $last = Audit_Log::get_last();
479 + if ( is_object( $last ) ) {
480 + $last = $for_hub
481 + ? $this->persistent_hub_datetime_format( $last->timestamp )
482 + : $this->format_date_time( $last->timestamp );
483 + } else {
484 + $last = 'n/a';
485 + }
486 +
487 + return array(
488 + 'monthCount' => $month_count,
489 + 'weekCount' => Audit::get_audit_events_per_week(),
490 + 'dayCount' => $day_count,
491 + 'lastEvent' => $last,
492 + 'report' => wd_di()->get( Audit_Report::class )->to_string(),
493 + );
109 494 }
110 495
111 496 /**
112 - * Dummy — returns empty summary.
497 + * Save settings.
113 498 *
499 + * @param Request $request The request object containing new settings data.
500 + *
501 + * @return Response
114 502 * @defender_route
115 503 */
116 - public function summary(): void {
117 - wp_send_json_success( array() );
504 + public function save_settings( Request $request ): Response {
505 + $data = $request->get_data_by_model( $this->model );
506 +
507 + $this->model->import( $data );
508 + if ( $this->model->validate() ) {
509 + $this->model->save();
510 + }
511 +
512 + Config_Hub_Helper::set_clear_active_flag();
513 +
514 + return new Response(
515 + true,
516 + array_merge(
517 + $this->data_frontend(),
518 + array(
519 + 'message' => esc_html__( 'Your settings have been updated.', 'defender-security' ),
520 + 'auto_close' => true,
521 + 'model' => $this->model->export(),
522 + )
523 + )
524 + );
118 525 }
119 526
120 527 /**
121 - * Dummy — returns empty summary data.
528 + * Provides the audit settings data used outside the Audit Logging page.
122 529 *
123 - * @param bool $for_hub Default false.
124 - *
125 530 * @return array
126 531 */
127 - public function summary_data( bool $for_hub = false ): array {
128 - return array();
532 + public function settings_data_frontend(): array {
533 + return array_merge(
534 + array(
535 + 'model' => $this->model->export(),
536 + ),
537 + $this->dump_routes_and_nonces()
538 + );
129 539 }
130 540
131 541 /**
132 - * Dummy — save settings no-op.
542 + * Converts the current state of the object to an array.
133 543 *
134 - * @return Response
135 - * @defender_route
544 + * @return array Returns an associative array of object properties.
136 545 */
137 - public function save_settings(): Response {
138 - return new Response( true, array() );
546 + public function to_array(): array {
547 + return array_merge(
548 + array(
549 + 'enabled' => $this->is_active(),
550 + 'report' => true,
551 + ),
552 + $this->dump_routes_and_nonces()
553 + );
139 554 }
140 555
141 556 /**
142 - * All the variables that we will show on frontend.
557 + * Removes settings for all submodules.
558 + */
559 + public function remove_settings(): void {
560 + ( new Model_Audit_Logging() )->delete();
561 + }
562 +
563 + /**
564 + * Delete all the data & the cache.
565 + */
566 + public function remove_data(): void {
567 + Audit_Log::truncate();
568 + // Remove cached data.
569 + Array_Cache::remove( 'sockets', 'audit' );
570 + Array_Cache::remove( 'logs', 'audit' );
571 + Array_Cache::remove( 'menu_updated', 'audit' );
572 + Array_Cache::remove( 'post_updated', 'audit' );
573 + delete_site_option( 'wd_audit_fetch_checkpoint' );
574 + }
575 +
576 + /**
577 + * Provides data for the frontend.
143 578 *
144 - * @return array
579 + * @return array An array of data for the frontend.
580 + * @throws Exception If there is an error.
145 581 */
146 582 public function data_frontend(): array {
583 + $logs = array();
584 + $count = 0;
585 + $per_page = self::DEFAULT_PER_PAGE;
586 + $total_page = 1;
587 + if ( $this->is_active() ) {
588 + $timezone = wp_timezone();
589 + $date_from = ( new DateTime( '-7 days', $timezone ) )->setTime( 0, 0, 0 );
590 + $date_to = ( new DateTime( 'now', $timezone ) )->setTime( 23, 59, 59 );
591 + $ip_address = sanitize_text_field( defender_get_data_from_request( 'ip', 'g' ) );
592 + $result = $this->service->fetch(
593 + $date_from->getTimestamp(),
594 + $date_to->getTimestamp(),
595 + array(),
596 + '',
597 + $ip_address,
598 + 1,
599 + $per_page
600 + );
601 + if ( ! is_wp_error( $result ) ) {
602 + $logs = $this->format_log_rows( $result );
603 + // Skip COUNT query when the first page is a partial result — it's cheaper than a second DB round-trip.
604 + if ( count( $logs ) < $per_page ) {
605 + $count = count( $logs );
606 + } else {
607 + $count = Audit_Log::count( $date_from->getTimestamp(), $date_to->getTimestamp(), array(), '', $ip_address );
608 + }
609 + $total_page = ceil( $count / $per_page );
610 + }
611 + }
612 +
147 613 return array(
148 614 'auditLogging' => array_merge(
149 615 array(
150 616 'model' => $this->model->export(),
151 - 'logs' => array(),
152 - 'events_type' => array(),
617 + 'logs' => $logs,
618 + 'events_type' => Audit_Log::allowed_events(),
153 619 'summary' => array(
154 - 'count_7_days' => 0,
155 - 'report' => '',
620 + 'count_7_days' => $count,
621 + 'report' => wd_di()->get( Audit_Report::class )->to_string(),
156 622 ),
157 623 'paging' => array(
158 624 'paged' => 1,
159 - 'total_pages' => 1,
160 - 'count' => 0,
625 + 'total_pages' => $total_page,
626 + 'count' => $count,
161 627 ),
162 628 ),
163 629 $this->dump_routes_and_nonces()
164 630 ),
165 - 'antibot' => wd_di()->get( \WP_Defender\Controller\Antibot_Global_Firewall::class )->data_frontend(),
631 + 'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(),
166 632 );
167 633 }
168 634
169 635 /**
170 - * Converts the current state of the object to an array.
636 + * Imports data into the model.
171 637 *
172 - * @return array
173 - */
174 - public function to_array(): array {
175 - return array_merge(
176 - array(
177 - 'enabled' => false,
178 - 'report' => false,
179 - ),
180 - $this->dump_routes_and_nonces()
181 - );
182 - }
183 -
184 - /**
185 - * Dummy — import data no-op.
638 + * @param array $data Data to be imported into the model.
186 639 *
187 - * @param array $data Data to import.
640 + * @throws Exception If table is not defined.
188 641 */
189 642 public function import_data( array $data ) {
643 + $model = $this->model;
644 + if ( array() === $data ) {
645 + $model->enabled = false;
646 + $model->storage_days = '6 months';
647 + $model->save();
648 + } else {
649 + $model->import( $data );
650 + if ( $model->validate() ) {
651 + $model->save();
652 + }
653 + }
190 654 }
191 655
192 656 /**
193 - * Remove all settings.
194 - */
195 - public function remove_settings(): void {
196 - }
197 -
198 - /**
199 - * Dummy — remove data no-op.
200 - */
201 - public function remove_data(): void {
202 - }
203 -
204 - /**
205 - * Export strings.
657 + * Exports strings.
206 658 *
207 - * @return array
659 + * @return array An array of strings.
208 660 */
209 661 public function export_strings(): array {
210 - return array(
211 - sprintf(
662 + $strings = $this->is_active()
663 + ? array( esc_html__( 'Active', 'defender-security' ) )
664 + : array( esc_html__( 'Inactive', 'defender-security' ) );
665 +
666 + $strings[] = sprintf(
212 667 /* translators: %s: Html for Pro-tag. */
213 - esc_html__( 'Inactive %s', 'defender-security' ),
668 + esc_html__( 'Email report inactive %s', 'defender-security' ),
214 669 '<span class="sui-tag sui-tag-pro">Pro</span>'
215 - ),
216 - );
670 + );
671 +
672 + return $strings;
217 673 }
218 674
219 675 /**
220 - * Config strings — always returns inactive.
676 + * Generates configuration strings based on the provided configuration.
221 677 *
222 - * @param array $config Configuration data.
678 + * @param array $config Configuration data.
223 679 *
224 - * @return array
680 + * @return array Returns an array of configuration strings.
225 681 */
226 682 public function config_strings( array $config ): array {
227 - return $this->export_strings();
683 + $strings = $config['enabled']
684 + ? array( esc_html__( 'Active', 'defender-security' ) )
685 + : array( esc_html__( 'Inactive', 'defender-security' ) );
686 +
687 + $strings[] = sprintf(
688 + /* translators: %s: Html for Pro-tag. */
689 + esc_html__( 'Email report inactive %s', 'defender-security' ),
690 + '<span class="sui-tag sui-tag-pro">Pro</span>'
691 + );
692 +
693 + return $strings;
228 694 }
229 695 }