parent          = $parent;
		$this->current_running = get_option( $this->OPT_EVENTS_INLINE );
		register_shutdown_function( array( $this, 'catch_errors' ) );
		add_action( 'admin_notices', array( $this, 'display_errors' ) );
		add_action( 'plugins_loaded', array( $this, 'load_checks' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
		add_action( 'wp_ajax_wpscan_check_action', array( $this, 'handle_actions' ) );
		add_action( $this->WPSCAN_SECURITY_SCHEDULE, array( $this, 'security_check_now' ), 99 );
	}
	/**
	 * Register Admin Scripts
	 *
	 * @param string $hook parent.
	 *
	 * @access public
	 * @return void
	 * @since 1.0.0
	 */
	public function admin_enqueue( $hook ) {
		if ( $hook === $this->parent->page_hook ) {
			wp_enqueue_script(
				'wpscan-security-checks',
				plugins_url( 'assets/js/security-checks.js', WPSCAN_PLUGIN_FILE ),
				array( 'jquery-ui-tooltip' )
			);
		}
	}
	/**
	 * Load checks files.
	 *
	 * @return void
	 * @since 1.0.0
	 * @access public
	 */
	public function load_checks() {
		$dir     = $this->parent->plugin_dir . 'security-checks';
		$folders = array_diff( scandir( $dir ), array( '..', '.' ) );
		foreach ( $folders as $folder ) {
			$file = "$dir/$folder/check.php";
			if ( '.' === $folder[0] ) {
				continue;
			}
			require_once $file;
			$data = get_file_data( $file, array( 'classname' => 'classname' ) );
			$data['instance'] = new $data['classname']( $folder, "$dir/$folder", $this->parent );
			$this->checks[ $folder ] = $data;
		}
	}
	/**
	 * Register a shutdown hook to catch errors
	 *
	 * @return void
	 * @since 1.0.0
	 * @access public
	 */
	public function catch_errors() {
		$error = error_get_last();
		if ( $error && $error['type'] ) {
			if ( basename( $error['file'] ) == 'check.php' ) {
				$errors = get_option( $this->OPT_FATAL_ERRORS, array() );
				array_push( $errors, $error );
				update_option( $this->OPT_FATAL_ERRORS, array_unique( $errors ) );
				$report = $this->parent->get_report();
				$report['cache'] = strtotime( current_time( 'mysql' ) );
				update_option( $this->parent->OPT_REPORT, $report );
				$this->parent->classes['account']->update_account_status();
				delete_transient( $this->parent->WPSCAN_TRANSIENT_CRON );
			}
		}
	}
	/**
	 * Display fatal errors
	 *
	 * @return void
	 * @since 1.0.0
	 * @access public
	 */
	public function display_errors() {
		$screen = get_current_screen();
		$errors = get_option( $this->OPT_FATAL_ERRORS, array() );
		if ( strstr( $screen->id, $this->parent->classes['report']->page ) ) {
			foreach ( $errors as $err ) {
				$msg = explode( 'Stack', $err['message'] )[0];
				$msg = trim( $msg );
				echo "
";
			}
		}
	}
	/**
	 * List vulnerabilities in the report.
	 *
	 * @param object $check - The check instance.
	 *
	 * @access public
	 * @return string
	 * @since 1.0.0
	 *
	 */
	public function list_check_vulnerabilities( $instance ) {
		$vulnerabilities = $instance->get_vulnerabilities();
		$count           = $instance->get_vulnerabilities_count();
		$ignored         = $this->parent->get_ignored_vulnerabilities();
		$not_checked_text = __( 'Not checked yet. Click the Run button to run a scan', 'wpscan' );
		if ( ! isset( $vulnerabilities ) ) {
			echo esc_html( $not_checked_text );
		} elseif ( empty( $vulnerabilities ) || 0 === $count ) {
			echo esc_html( $instance->success_message() );
		} else {
			$list = array();
			foreach ( $vulnerabilities as $item ) {
				if ( in_array( $item['id'], $ignored, true ) ) {
					continue;
				}
				$html  = "";
				$html .= "
";
				$html .= "" . esc_html( $item['severity'] ) ."";
				$html .= '';
				$html .= "
" . wp_kses( $item['title'], array( 'a' => array( 'href' => array() ) ) ) . '
';
				$html .= "
";
				$html .= '
', $list );
		}
	}
	/**
	 * Return vulnerabilities in the report.
	 *
	 * @param object $check - The check instance.
	 *
	 * @access public
	 * @return string
	 * @since 1.14.4
	 *
	 */
	public function get_check_vulnerabilities( $instance ) {
		$vulnerabilities = $instance->get_vulnerabilities();
		$count           = $instance->get_vulnerabilities_count();
		$ignored         = $this->parent->get_ignored_vulnerabilities();
		$not_checked_text = __( 'Not checked yet. Click the Run button to run a scan', 'wpscan' );
		if ( ! isset( $vulnerabilities ) ) {
			return esc_html( $not_checked_text );
		} elseif ( empty( $vulnerabilities ) || 0 === $count ) {
			return esc_html( $instance->success_message() );
		} else {
			$list = array();
			foreach ( $vulnerabilities as $item ) {
				if ( in_array( $item['id'], $ignored, true ) ) {
					continue;
				}
				$html   = "";
				$html  .= "
";
				$html  .= "" . esc_html( $item['severity'] ) . '';
				$html  .= '
';
				$html  .= "
" . wp_kses( $item['title'], array( 'a' => array( 'href' => array() ) ) ) . '
';
				$html  .= '
', $list );
		}
	}
	/**
	 * Display actions buttons
	 *
	 * @param object $instance - The check instance.
	 *
	 * @access public
	 * @return string
	 * @since 1.0.0
	 *
	 */
	public function list_actions( $instance ) {
		foreach ( $instance->actions as $action ) {
			$confirm         = isset( $action['confirm'] ) ? $action['confirm'] : false;
			$button_text     = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? esc_html__( 'Running', 'wpscan' ) : esc_html( $action['title'] );
			$button_disabled = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? ' disabled' : '';
			echo sprintf(
				"",
				esc_attr( $instance->id ),
				esc_attr( $confirm ),
				esc_attr( $action['id'] ),
				$button_disabled,
				$button_text
			);
		}
	}
	/**
	 * Get actions buttons
	 *
	 * @param object $instance - The check instance.
	 *
	 * @access public
	 * @return string
	 * @since 1.14.4
	 *
	 */
	public function get_list_actions( $instance ) {
		foreach ( $instance->actions as $action ) {
			$confirm         = isset( $action['confirm'] ) ? $action['confirm'] : false;
			$button_text     = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? esc_html__( 'Running', 'wpscan' ) : esc_html( $action['title'] );
			$button_disabled = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? ' disabled' : '';
			return sprintf(
				"",
				esc_attr( $instance->id ),
				esc_attr( $confirm ),
				esc_attr( $action['id'] ),
				$button_disabled,
				$button_text
			);
		}
	}
	/**
	 * Load checks files.
	 *
	 * @return void
	 * @since 1.0.0
	 * @access public
	 */
	public function handle_actions() {
		check_ajax_referer( 'wpscan' );
		if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
			wp_die();
		}
		$check  = isset( $_POST['check'] ) ? $_POST['check'] : false;
		$action = isset( $_POST['action_id'] ) ? $_POST['action_id'] : false;
		if ( $action && $check ) {
			$res = 0;
			if ( 'run' === $action ) {
				$event_type[ $check ] = $action;
				$this->add_event_inline( $event_type );
				if ( false === as_next_scheduled_action( $this->WPSCAN_SECURITY_SCHEDULE ) ) {
					as_schedule_single_action( strtotime( 'now' ), $this->WPSCAN_SECURITY_SCHEDULE );
				}
				$res = 1;
			} else {
				$action = array_filter(
					$this->checks[ $check ]['instance']->actions,
					function ( $i ) use ( $action ) {
						return $i['id'] === $action;
					}
				);
				$action = current( $action );
				if ( method_exists( $this->checks[ $check ]['instance'], $action['method'] ) ) {
					$res = call_user_func( array( $this->checks[ $check ]['instance'], $action['method'] ) );
				}
			}
			if ( $res ) {
				wp_send_json_success( $check );
			} else {
				wp_send_json_error();
			}
		}
		wp_send_json_error();
	}
	/**
	 * Run the Security checks
	 *
	 * @since 1.15
	 * @acces public
	 */
	public function security_check_now() {
		if ( ! empty( $this->current_running ) ) {
			foreach ( $this->current_running as $key => $to_check ) {
				$check  = $key;
				$action = $to_check;
				$action = array_filter(
					$this->checks[ $check ]['instance']->actions,
					function ( $i ) use ( $action ) {
						return $i['id'] === $action;
					}
				);
				$action = current( $action );
				if ( method_exists( $this->checks[ $check ]['instance'], $action['method'] ) ) {
					call_user_func( array( $this->checks[ $check ]['instance'], $action['method'] ) );
				}
				$this->remove_event_from_list( $check );
				as_schedule_single_action( strtotime( 'now' ) + 10, $this->WPSCAN_SECURITY_SCHEDULE );
				break;
			}
		} else {
			delete_option( $this->OPT_EVENTS_INLINE );
		}
	}
	/**
	 * Register event to wait inline
	 *
	 * @param $event_type
	 *
	 * @since 1.15
	 * @acces public
	 */
	public function add_event_inline( $event_type ) {
		if ( $this->current_running ) {
			update_option( $this->OPT_EVENTS_INLINE, $this->current_running + $event_type );
		} else {
			update_option( $this->OPT_EVENTS_INLINE, $event_type );
		}
	}
	/**
	 * Remove event from the waiting line
	 *
	 * @param $event
	 *
	 * @since 1.15
	 * @acces public
	 */
	public function remove_event_from_list( $event ) {
		if ( $event ) {
			unset( $this->current_running[ $event ] );
			update_option( $this->OPT_EVENTS_INLINE, $this->current_running );
		}
	}
}