0 ) { $text = __( 'Sectivio is still turned off. Your content has been recorded; all that is missing is the start.', 'sectivio' ); $hints[] = array( 'id' => 'enable', 'prio' => 1, 'where' => array('dashboard', 'settings', 'report'), 'text' => $text, 'action' => 'enable', 'label' => __( 'Turn on Sectivio', 'sectivio' ), ); } // Messung steht still, obwohl vorher Besuche kamen (meist Caching/JS-Verzögerung). $stall = self::stalled_days(); if ( !empty( $s['enabled'] ) && $stall >= 3 ) { $text = sprintf( /* translators: %d: Anzahl Tage */ _n( 'Sectivio has not measured any visits for %d day, although visits used to come in regularly.', 'Sectivio has not measured any visits for %d days, although visits used to come in regularly.', $stall, 'sectivio' ), $stall ); $text .= ' ' . (( self::rocket_delays_js() ? __( 'WP Rocket delays JavaScript on your website. In WP Rocket, go to “File Optimization”, add the exclusion “sectivio” under “Delay JavaScript execution” and then clear the cache.', 'sectivio' ) : __( 'The most common cause is a caching or optimization plugin that delays or combines scripts. Exclude Sectivio from these features there and clear the cache.', 'sectivio' ) )); $hints[] = array( 'id' => 'stalled', 'prio' => 1, 'where' => array('dashboard', 'report'), 'text' => $text, ); } // Anrufe/E-Mails werden nicht gezählt, obwohl die Website solche Links hat. $types = (array) ($s['goal_types'] ?? array('form')); foreach ( array( 'tel' => 'tel:', 'mailto' => 'mailto:', ) as $type => $scheme ) { if ( in_array( $type, $types, true ) || !self::site_has_link( $scheme ) ) { continue; } $hints[] = array( 'id' => 'goal_' . $type, 'prio' => 2, 'where' => array('dashboard', 'settings', 'report'), 'text' => ( 'tel' === $type ? __( 'Your website has clickable phone numbers, but Sectivio does not count phone calls as enquiries yet. People who call are often your most valuable contacts.', 'sectivio' ) : __( 'Your website has clickable email addresses, but Sectivio does not count emails as enquiries yet.', 'sectivio' ) ), 'action' => 'goal_' . $type, 'label' => ( 'tel' === $type ? __( 'Count phone calls', 'sectivio' ) : __( 'Count emails', 'sectivio' ) ), ); } // Viele Besuche, aber keine einzige Anfrage gezählt. $month = ADSEC_Daily::metrics( array( 'since' => gmdate( 'Y-m-d', time() - 30 * DAY_IN_SECONDS ), ) ); if ( $month['sessions'] >= 100 && 0 === (int) $month['goal_sessions'] ) { $hints[] = array( 'id' => 'no_goals', 'prio' => 2, 'where' => array('dashboard', 'settings', 'report'), 'text' => sprintf( /* translators: %s: Anzahl Besuche */ __( 'In the last 30 days, your website had %s visits, but Sectivio did not count a single enquiry. Without enquiries, Sectivio can only evaluate reading time. Please check whether your goal is set up correctly.', 'sectivio' ), number_format_i18n( $month['sessions'] ) ), 'link' => $set_url . '#adsec-erfolg', 'label' => __( 'Check goal', 'sectivio' ), ); } // Viele Abschnitte ohne erkanntes Thema. if ( $secs['total'] >= 10 && $secs['unknown'] / $secs['total'] >= 0.3 ) { $configured = ADSEC_AI::is_configured(); $hints[] = array( 'id' => 'topics', 'prio' => 3, 'where' => array('dashboard', 'settings', 'report'), 'text' => sprintf( /* translators: %s: Anzahl Abschnitte */ __( 'For %s sections, Sectivio does not yet know what they are about. With detected content, Sectivio learns faster, because similar sections benefit from each other.', 'sectivio' ), number_format_i18n( $secs['unknown'] ) ), 'link' => ( $configured ? $inv_url : $set_url . '#adsec-ki' ), 'label' => ( $configured ? __( 'Have content detected', 'sectivio' ) : __( 'Set up detection', 'sectivio' ) ), ); } // 0.52.0: remind the owner to mention Sectivio in the privacy policy. if ( !ADSEC_Privacy::is_done() ) { $hints[] = array( 'id' => 'privacy', 'prio' => 2, 'where' => array('dashboard', 'settings'), 'text' => __( 'Please mention Sectivio in your privacy policy. Sectivio provides the text, adapted to your settings – a shortcode is all it takes.', 'sectivio' ), 'link' => $set_url . '#adsec-datenschutz', 'label' => __( 'Show text', 'sectivio' ), ); } // Monatsbericht aus — erst nach zwei Wochen vorschlagen. $first = ADSEC_Daily::first_day(); if ( empty( $s['report_enabled'] ) && $first && strtotime( $first . ' UTC' ) < time() - 14 * DAY_IN_SECONDS ) { $hints[] = array( 'id' => 'report', 'prio' => 4, 'where' => array('dashboard', 'report'), 'text' => sprintf( /* translators: %s: E-Mail-Adresse */ __( 'Would you like to see how your website is doing by email once a month? The report will be sent to %s.', 'sectivio' ), ( (string) ($s['report_email'] ?? '') ?: get_option( 'admin_email' ) ) ), 'action' => 'report', 'label' => __( 'Turn on monthly report', 'sectivio' ), ); } $dismissed = (array) get_option( self::DISMISS_OPTION, array() ); $hints = array_values( array_filter( $hints, function ( $h ) use($dismissed) { return empty( $dismissed[$h['id']] ) || $dismissed[$h['id']] < time() - self::DISMISS_DAYS * DAY_IN_SECONDS; } ) ); usort( $hints, function ( $a, $b ) { return $a['prio'] <=> $b['prio']; } ); return $hints; } /** Hinweise für einen Ort (dashboard|settings|report). */ public static function for_context( $where, $limit = 3 ) { $out = array(); foreach ( self::all() as $h ) { if ( in_array( $where, $h['where'], true ) ) { $out[] = $h; } } return array_slice( $out, 0, $limit ); } /** Abschnittszahlen: gesamt und ohne Thema. */ private static function section_counts() { global $wpdb; $t = ADSEC_Database::sections_table(); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table. $out = array( 'total' => (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE excluded = 0', $t ) ), 'unknown' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE excluded = 0 AND topic = 'unbekannt'", $t ) ), ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $out; } /** Tage ohne Messung, wenn die Website davor im Schnitt mind. 2 Besuche/Tag hatte. */ private static function stalled_days() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table. $last = $wpdb->get_var( $wpdb->prepare( 'SELECT MAX(created_at) FROM %i', ADSEC_Database::events_table() ) ); if ( !$last ) { return 0; } $days = (int) floor( (time() - strtotime( $last . ' UTC' )) / DAY_IN_SECONDS ); if ( $days < 3 ) { return 0; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table. $before = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(DISTINCT session_key) FROM %i WHERE created_at >= %s', ADSEC_Database::events_table(), gmdate( 'Y-m-d H:i:s', strtotime( $last . ' UTC' ) - 14 * DAY_IN_SECONDS ) ) ); return ( $before >= 28 ? $days : 0 ); } /** Hat irgendein erfasster Abschnitt einen Link mit diesem Schema (tel:/mailto:)? */ private static function site_has_link( $scheme ) { global $wpdb; $like = '%' . $wpdb->esc_like( 'href="' . $scheme ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table. return (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT 1 FROM %i WHERE html LIKE %s LIMIT 1', ADSEC_Database::sections_table(), $like ) ); } /** WP Rocket mit „JavaScript-Ausführung verzögern" aktiv? */ private static function rocket_delays_js() { return function_exists( 'get_rocket_option' ) && get_rocket_option( 'delay_js' ); } /** * Wartezeit lesbar. $in = true für „in 3 Tagen / in etwa 4 Monaten", * sonst „noch 3 Tage / noch etwa 4 Monate". */ private static function wait( $days, $in = false ) { $days = (int) $days; if ( $days < 14 ) { // Kontext „after in“: im Deutschen „in 3 Tagen“ statt „3 Tage“. // Translator comments sit on their own statements: Freemius' code formatter drops comments inside expressions. if ( $in ) { /* translators: %d: number of days. */ return sprintf( _nx( '%d day', '%d days', $days, 'after in', 'sectivio' ), $days ); } /* translators: %d: number of days. */ return sprintf( _n( '%d day', '%d days', $days, 'sectivio' ), $days ); } if ( $days < 60 ) { /* translators: %d: number of weeks. */ return sprintf( __( 'about %d weeks', 'sectivio' ), (int) round( $days / 7 ) ); } if ( $days < 730 ) { $m = (int) round( $days / 30 ); /* translators: %d: number of months. */ return ( $in ? sprintf( _x( 'about %d months', 'after in', 'sectivio' ), $m ) : sprintf( __( 'about %d months', 'sectivio' ), $m ) ); } return ( $in ? _x( 'more than two years', 'after in', 'sectivio' ) : __( 'more than two years', 'sectivio' ) ); } /* ---------- Anzeige ---------- */ public static function render( $where ) { self::done_notice(); $hints = self::for_context( $where, ( 'settings' === $where ? 2 : 3 ) ); if ( !$hints ) { return; } $back = ( isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : admin_url( 'admin.php?page=adaptive-sections' ) ); ?>
' . esc_html( $done ) . '
'; } } }