';
}
/**
* „Daten zurücksetzen": löscht alle erfassten Mess-/Lerndaten, sodass das
* Dashboard bei 0 startet (z. B. nach einer Test-Simulation). Inhalts-Inventar
* und Einstellungen bleiben erhalten.
*/
public function handle_reset_stats() {
if ( !current_user_can( 'manage_options' ) || !check_admin_referer( 'adsec_reset_stats' ) ) {
wp_die( esc_html__( 'Not allowed.', 'sectivio' ) );
}
ADSEC_Database::reset_stats();
wp_safe_redirect( add_query_arg( array(
'page' => 'adsec-settings',
'adsec_reset' => 1,
), admin_url( 'admin.php' ) ) );
exit;
}
/** Inhaltserkennung: Stand der KI-Verbindung von WordPress. */
private static function render_ai_section( $s ) {
?>
admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'adsec_settings_ajax' ),
'i18n' => array(
'sending' => __( 'Sending report …', 'sectivio' ),
'sendFail' => __( 'The report could not be sent. Please try again later.', 'sectivio' ),
'checking' => __( 'Checking …', 'sectivio' ),
'checkNow' => __( 'Check now', 'sectivio' ),
'scanRun' => $scan_run,
'scanWait' => __( 'Scanning pages … this can take a minute.', 'sectivio' ),
'aiRun' => $ai_run,
'aiWait' => __( 'Detecting sections … this can take a minute.', 'sectivio' ),
'keepOpen' => __( 'Please keep this page open.', 'sectivio' ),
'failed' => __( 'That did not work. Please check your connection and try again.', 'sectivio' ),
),
) );
}
/**
* Status-Widget auf der WordPress-Startseite des Backends (v0.10.0).
* Zeigt den aktuellen Stand auf einen Blick, ohne ins Plugin wechseln zu müssen.
*/
public function register_dashboard_widget() {
if ( !current_user_can( 'manage_options' ) ) {
return;
}
wp_add_dashboard_widget( 'adsec_status_widget', esc_html__( 'Sectivio — Status', 'sectivio' ), array($this, 'render_status_widget') );
}
/**
* Inhalt des Status-Widgets: in der Sammelphase der Fortschritt (% der
* benötigten Sitzungen), sobald optimiert wird die Verbesserungs-Raten.
* Eigenständig gestylt (inline), da das Plugin-CSS hier nicht geladen wird.
*/
public function render_status_widget() {
$sum = ADSEC_Dashboard::quick_summary();
$dashboard_url = admin_url( 'admin.php?page=adaptive-sections' );
echo '
';
/* translators: %s: page builder names. */
$msg = __( 'Some pages are built with %s. Sectivio does not support this page builder yet and cannot recognize the sections on these pages.', 'sectivio' );
printf( esc_html( $msg ), esc_html( implode( ', ', array_keys( $sum['unsupported'] ) ) ) );
echo '
';
} elseif ( 0 === $sum['sections'] ) {
echo '
' . esc_html__( 'Sectivio did not find any sections on your website. Sections are the large blocks of a page, such as a header area, services or references. Pages that consist of one block of text cannot be measured section by section.', 'sectivio' ) . '
';
}
}
/** AJAX: scan the next few pages (content overview shows the progress). */
public function ajax_scan_step() {
if ( !current_user_can( 'manage_options' ) || !check_ajax_referer( 'adsec_settings_ajax', false, false ) ) {
wp_send_json_error( 'forbidden', 403 );
}
if ( !empty( $_POST['start'] ) ) {
update_option( 'adsec_scan_offset', 0, false );
// a click always scans the whole website
delete_option( ADSEC_Inventory::REPORT_OPTION );
}
wp_send_json_success( ADSEC_Inventory::scan_site( 5 ) );
}
/** AJAX: detect the next batch of unclear sections. */
public function ajax_ai_step() {
if ( !current_user_can( 'manage_options' ) || !check_ajax_referer( 'adsec_settings_ajax', false, false ) ) {
wp_send_json_error( 'forbidden', 403 );
}
wp_send_json_success( ADSEC_AI::detect_batch() );
}
/** AJAX: Monatsbericht sofort an die angegebene Adresse senden. */
public function ajax_send_report() {
if ( !current_user_can( 'manage_options' ) || !check_ajax_referer( 'adsec_settings_ajax', false, false ) ) {
wp_send_json_error( 'forbidden', 403 );
}
$to = sanitize_email( wp_unslash( $_POST['email'] ?? '' ) );
if ( !$to ) {
wp_send_json_success( array(
'ok' => false,
'message' => __( 'Please enter a valid email address.', 'sectivio' ),
) );
}
$status = ADSEC_Report::send_now( $to );
// Translator comments sit on their own statements: Freemius' code formatter drops comments inside arrays.
/* translators: %s: email address */
$msg_sent = __( 'The report is on its way to %s.', 'sectivio' );
/* translators: %s: email address */
$msg_confirm = __( 'Almost done: we have sent a confirmation to %s. As soon as you click the link in that email, the report will be on its way.', 'sectivio' );
/* translators: %s: email address */
$msg_pending = __( 'The address %s has not been confirmed yet. Please click the link in the confirmation email we sent you, and check your spam folder too.', 'sectivio' );
/* translators: %s: email address */
$msg_stopped = __( 'The monthly report has been unsubscribed for %s. Please use a different address or write to us at office@sectivio.com.', 'sectivio' );
$messages = array(
'sent' => sprintf( $msg_sent, $to ),
'confirm_sent' => sprintf( $msg_confirm, $to ),
'confirm_pending' => sprintf( $msg_pending, $to ),
'stopped' => sprintf( $msg_stopped, $to ),
'error' => __( 'The report could not be sent just now. Please try again in a few minutes.', 'sectivio' ),
);
wp_send_json_success( array(
'ok' => in_array( $status, array('sent', 'confirm_sent', 'confirm_pending'), true ),
'message' => $messages[$status] ?? $messages['error'],
) );
}
/* ---------- Content-Inventar ---------- */
public function handle_scan() {
if ( !current_user_can( 'manage_options' ) || !check_admin_referer( 'adsec_scan' ) ) {
wp_die( esc_html__( 'Not allowed.', 'sectivio' ) );
}
$result = ADSEC_Inventory::scan_site();
$args = array(
'page' => 'adsec-inventory',
'adsec_scanned' => $result['scanned'],
'adsec_found' => $result['sections'],
'adsec_remaining' => $result['remaining'],
);
wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) );
exit;
}
public function handle_save_inventory() {
if ( !current_user_can( 'manage_options' ) || !check_admin_referer( 'adsec_save_inventory' ) ) {
wp_die( esc_html__( 'Not allowed.', 'sectivio' ) );
}
// Seiten-Sperren ZUERST speichern, damit das reusable-Gate in save_inventory
// eine frisch gesperrte Seite sofort berücksichtigt.
// Große Websites haben Tausende Felder; PHP verwirft alles über max_input_vars
// (oft 1.000) stillschweigend. Das Formular schickt die Angaben deshalb als ein
// JSON-Feld; ohne JavaScript gilt weiterhin das normale Formular.
$payload = ( isset( $_POST['adsec_payload'] ) ? json_decode( sanitize_textarea_field( wp_unslash( $_POST['adsec_payload'] ) ), true ) : null );
if ( is_array( $payload ) ) {
$pages = map_deep( (array) ($payload['pages'] ?? array()), 'sanitize_text_field' );
$sections = map_deep( (array) ($payload['sections'] ?? array()), 'sanitize_text_field' );
} else {
$pages = ( isset( $_POST['pages'] ) ? map_deep( wp_unslash( (array) $_POST['pages'] ), 'sanitize_text_field' ) : array() );
$sections = ( isset( $_POST['sections'] ) ? map_deep( wp_unslash( (array) $_POST['sections'] ), 'sanitize_text_field' ) : array() );
}
ADSEC_Inventory::save_page_overrides( $pages );
ADSEC_Inventory::save_inventory( $sections );
wp_safe_redirect( add_query_arg( array(
'page' => 'adsec-inventory',
'adsec_saved' => 1,
), admin_url( 'admin.php' ) ) );
exit;
}
public function handle_ai_classify() {
if ( !current_user_can( 'manage_options' ) || !check_admin_referer( 'adsec_ai_classify' ) ) {
wp_die( esc_html__( 'Not allowed.', 'sectivio' ) );
}
$result = ADSEC_AI::detect_batch();
$args = array(
'page' => 'adsec-inventory',
'adsec_ai_done' => (int) $result['done'],
'adsec_ai_remaining' => (int) $result['remaining'],
);
if ( null !== $result['error'] ) {
$args['adsec_ai_error'] = rawurlencode( mb_substr( $result['error'], 0, 160 ) );
}
wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) );
exit;
}
/**
* Sitemap über der Inhalts-Übersicht: die Navigation der Website (Hauptmenü mit
* Untermenüs) als Sprungliste. Seiten ohne Menüeintrag stehen unter „Weitere Seiten“.
*/
private static function inventory_sitemap( array $inventory ) {
$menu_id = self::main_menu_id();
$items = ( $menu_id ? (array) wp_get_nav_menu_items( $menu_id ) : array() );
$in_menu = array();
$tree = array();
foreach ( $items as $it ) {
$tree[(int) $it->menu_item_parent][] = $it;
if ( 'post_type' === $it->type ) {
$in_menu[(int) $it->object_id] = true;
}
}
$link = function ( $title, $page_id ) use($inventory) {
if ( $page_id && isset( $inventory[$page_id] ) ) {
return '' . esc_html( $title ) . '';
}
return '' . esc_html( $title ) . '';
};
$branch = function ( $parent ) use(&$branch, $tree, $link) {
if ( empty( $tree[$parent] ) ) {
return '';
}
$out = '