__( 'Scan of {SITE_URL} complete. {ISSUES_COUNT} issues found.', 'wpdef' ), 'subject_issue_not_found' => __( 'Scan of {SITE_URL} complete. {ISSUES_COUNT} issues found.', 'wpdef' ), 'subject_error' => __( 'Couldn’t scan {SITE_URL} for vulnerabilities.', 'wpdef' ), 'content_issue_found' => __( 'Hi {USER_NAME}, {ISSUES_COUNT} vulnerabilities were identified for {SITE_URL} during a Malware Scan. See details for each issue below. {ISSUES_LIST}', 'wpdef' ), 'content_issue_not_found' => __( 'Hi {USER_NAME}, No vulnerabilities have been found for {SITE_URL}.', 'wpdef' ), 'content_error' => __( 'Hi {USER_NAME}, We couldn\'t scan {SITE_URL} for vulnerabilities. Please visit your site and run a manual scan.', 'wpdef' ), ); } public function before_load() { $default_values = $this->get_default_values(); $default = array( 'slug' => 'malware-notification', 'title' => __( 'Malware Scanning - Notification', 'wpdef' ), 'status' => self::STATUS_DISABLED, 'description' => __( 'Get email notifications when Defender has finished manual malware scans.', 'wpdef' ), // @since 3.0.0 Fix 'Guest'-line. 'in_house_recipients' => is_user_logged_in() ? array( $this->get_default_user() ) : array(), 'out_house_recipients' => array(), 'type' => 'notification', 'dry_run' => false, 'configs' => array( 'always_send' => false, 'error_send' => false, 'template' => array( 'found' => array( 'subject' => $default_values['subject_issue_found'], 'body' => $default_values['content_issue_found'], ), 'not_found' => array( 'subject' => $default_values['subject_issue_not_found'], 'body' => $default_values['content_issue_not_found'], ), 'error' => array( 'subject' => $default_values['subject_error'], 'body' => $default_values['content_error'], ), ), ), ); $this->import( $default ); } /** * @param Scan $scan * @param object $object * @param string $subject * @param string $content * @param array $issues */ protected function email_engine( Scan $scan, $object, $subject, $content, $issues ) { foreach ( $object->in_house_recipients as $user ) { if ( self::USER_SUBSCRIBED !== $user['status'] ) { continue; } $this->send_to_user( $user['email'], $user['name'], $subject, $content, $issues ); } foreach ( $object->out_house_recipients as $user ) { if ( self::USER_SUBSCRIBED !== $user['status'] ) { continue; } $this->send_to_user( $user['email'], $user['name'], $subject, $content, $issues ); } } /** * @param Scan $scan * * @return void */ public function send( Scan $scan ) { $object = $this; if ( $scan->is_automation ) { $object = wd_di()->get( Malware_Report::class ); // If Malware Reporting is unchecked, then return. if ( self::STATUS_ACTIVE !== $object->status ) { return; } // @since 2.7.0 Remove 'dry_run'-condition. } $issues = $scan->get_issues( null, Scan_Item::STATUS_ACTIVE ); /** * When the Defender is unable to scan files for some reason and the 'error_send' option is checked. * Possible reasons: * - no connection to Scan API, * - no checksums received from wp.org, * - scan process stuck. */ if ( isset( $object->configs['error_send'] ) && ! empty( $object->configs['error_send'] && in_array( $scan->status, array( Scan::STATUS_ERROR, Scan::STATUS_IDLE ), true ) ) ) { $subject = $object->configs['template']['error']['subject']; $content = nl2br( $object->configs['template']['error']['body'] ); return $this->email_engine( $scan, $object, $subject, $content, $issues ); } // Sometimes value may be as empty string for disabled 'always_send'. if ( empty( $object->configs['always_send'] ) && 0 === count( $issues ) ) { return; } // For another case: $templates = count( $issues ) ? $object->configs['template']['found'] : $object->configs['template']['not_found']; $subject = $templates['subject']; $content = nl2br( $templates['body'] ); return $this->email_engine( $scan, $object, $subject, $content, $issues ); } /** * @param $email * @param $name * @param $subject * @param $content * @param $issues * * @throws \DI\DependencyException * @throws \DI\NotFoundException */ private function send_to_user( $email, $name, $subject, $content, $issues ) { $controller_scan = wd_di()->get( \WP_Defender\Controller\Scan::class ); $replacers = array( 'issues_count' => is_array($issues) || $issues instanceof \Countable ? count( $issues ) : 0, 'issues_list' => $controller_scan->render_partial( 'email/scan-issue-list', array( 'issues' => $issues, 'email' => $email, ), false ), 'site_url' => network_site_url(), 'user_name' => $name, ); // Replace data. $replacers = apply_filters( 'wd_notification_email_params', $replacers ); $filter_name = (is_array($issues) || $issues instanceof \Countable ? count( $issues ) : 0) > 0 ? 'wd_notification_email_subject_issue' : 'wd_notification_email_subject'; $subject = apply_filters( $filter_name, $subject ); $content = apply_filters( 'wd_notification_email_content_before', $content ); foreach ( $replacers as $key => $replacer ) { $content = str_replace( "{{$key}}", $replacer, $content ); $content = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $content ); $subject = str_replace( "{{$key}}", $replacer, $subject ); $subject = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $subject ); } $content = apply_filters( 'wd_notification_email_content_after', $content ); $template = $controller_scan->render_partial( 'email/index', array( 'title' => __( 'Malware Scanning', 'wpdef' ), 'content_body' => $content, ), false ); $headers = defender_noreply_html_header( defender_noreply_email( 'wd_scan_noreply_email' ) ); $ret = wp_mail( $email, $subject, $template, $headers ); if ( $ret ) { $this->save_log( $email ); } } /** * Define labels for settings key. * * @param string|null $key * * @return string|array|null */ public function labels( $key = null ) { $labels = array( 'notification' => __( 'Malware Scanning - Notification', 'wpdef' ), 'always_send_notification' => __( 'Send notifications when no issues are detected', 'wpdef' ), 'error_send' => __( "Send notifications when Defender couldn't scan the files", 'wpdef' ), 'email_subject_issue_found' => __( 'Email subject when an issue is found', 'wpdef' ), 'email_subject_issue_not_found' => __( 'Email subject when no issues are found', 'wpdef' ), 'email_subject_error' => __( 'Email subject when failed to scan', 'wpdef' ), 'email_content_issue_found' => __( 'Email body when an issue is found', 'wpdef' ), 'email_content_issue_not_found' => __( 'Email body when no issues are found', 'wpdef' ), 'email_content_error' => __( 'When failed to scan', 'wpdef' ), 'notification_subscribers' => __( 'Recipients', 'wpdef' ), ); if ( ! is_null( $key ) ) { return $labels[ $key ] ?? null; } return $labels; } /** * Try to send email if: * Malware Scanning - Notification is enabled, * Malware Scanning - Reporting is enabled or Scan model has is_automation = true. * * @return bool */ public function check_options() { return self::STATUS_ACTIVE === $this->status || self::STATUS_ACTIVE === wd_di()->get( Malware_Report::class )->status; } /** * Additional converting rules. * * @param array $configs * * @return array * @since 3.1.0 */ public function type_casting( $configs ) { $configs['always_send'] = (bool) $configs['always_send']; $configs['error_send'] = (bool) $configs['error_send']; return $configs; } }