settings = $settings; $this->sites = $sites; $this->email_log = $email_log; $this->access = $access; $this->slack = $slack; $this->advisor = $advisor; $this->brief = $brief; } /** * {@inheritDoc} * * @return string */ public function menu_title() { return __( 'Settings', 'turtlereport' ); } /** * {@inheritDoc} * * @return string */ public function capability() { return Capabilities::MANAGE; } /** * {@inheritDoc} * * @return void */ public function render() { View::render( 'settings', array( 'settings' => $this->settings->all(), 'logo_url' => $this->settings->logo_url(), 'cron_off' => defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON, 'managers' => self::managers(), 'defaults' => $this->settings->email_defaults(), 'schedules' => $this->schedules(), 'intervals' => Schedule::interval_labels(), 'cron' => self::cron_status(), 'slack' => array( 'configured' => $this->slack && $this->slack->configured(), 'hint' => $this->slack ? $this->slack->hint() : '', ), 'ai' => $this->ai(), 'brief' => $this->brief_state(), 'job_failures' => get_option( Jobs::FAILURES, array() ), ) ); } /** * The weekly brief's state for the settings form. * * @return array{next:int,to:string[],outcome:array} */ private function brief_state() { $brief = $this->brief ? call_user_func( $this->brief ) : null; if ( ! $brief ) { return array( 'next' => 0, 'to' => array(), 'outcome' => array(), ); } return array( 'next' => $brief->enabled() ? $brief->next_slot() : 0, 'to' => $brief->recipients(), 'outcome' => $brief->last_outcome(), ); } /** * Every site's email schedule, for the overview table. * * @return array[] Rows with name, edit_url, active, enabled, interval, people, next, last_sent and last_error. */ private function schedules() { if ( ! $this->sites || ! $this->email_log ) { return array(); } $recipients = new Recipients( $this->access ); $rows = array(); foreach ( $this->sites->all() as $site ) { $log = $this->email_log->get( $site->id ); $people = $recipients->resolve( $site ); $rows[] = array( 'id' => $site->id, 'label' => $site->site_label(), 'frequency' => $site->email['frequency'], 'weekday' => (int) $site->email['weekday'], 'monthday' => (int) $site->email['monthday'], 'hour' => (int) $site->email['hour'], 'to_viewers' => ! empty( $site->email['to_viewers'] ), 'viewers' => count( $recipients->viewer_ids( $site ) ), 'users' => count( (array) $site->email['user_ids'] ), 'addresses' => implode( ', ', (array) $site->email['recipients'] ), 'emails' => array_column( $people, 'email' ), 'name' => $site->display_name(), 'edit_url' => SitesPage::url( array( 'action' => 'edit', 'site' => $site->id, ) ) . '#tly-email-panel', 'active' => Schedule::is_active( $site->email ), 'enabled' => ! empty( $site->email['enabled'] ), 'interval' => Schedule::short_label( $site->email['frequency'] ), 'people' => count( $people ), 'next' => (int) $log['next'], 'last_sent' => (int) $log['last_sent'], 'last_error' => (string) $log['last_error'], 'review' => \TurtleReport\Policy\Options::MODE_REVIEW === $site->delivery['mode'], 'history' => DeliveriesPage::url( array( 'site' => $site->id ) ), ); } usort( $rows, static function ( $a, $b ) { return strnatcasecmp( $a['name'], $b['name'] ); } ); return $rows; } /** * Which reports each person gets, for agencies with many sites: email * address => site names, from the schedules that are switched on. * * @param array[] $schedules Rows from schedules(). * @return array Sorted by address. */ public static function by_person( array $schedules ) { $out = array(); foreach ( $schedules as $row ) { if ( empty( $row['active'] ) ) { continue; } foreach ( $row['emails'] as $address ) { $out[ strtolower( $address ) ][] = $row['name']; } } ksort( $out ); return $out; } /** * Health of WP-Cron as TurtleReport sees it. * * @return array{disabled:bool,next:int,late:bool,beat:int,stalled:bool,url:string} */ public static function cron_status() { $next = (int) wp_next_scheduled( Jobs::HOOK ); $beat = Jobs::last_beat(); return array( 'disabled' => defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON, 'next' => $next, // The hourly sweep is more than 15 minutes overdue: cron isn't being triggered. 'late' => $next > 0 && $next < time() - 15 * MINUTE_IN_SECONDS, 'beat' => $beat, // No TurtleReport job ran for two hours although the hourly one should have. 'stalled' => $beat > 0 && $beat < time() - 2 * HOUR_IN_SECONDS, 'url' => site_url( 'wp-cron.php' ), ); } /** * Users who manage TurtleReport, offered as alert recipients. * * @return array[] Items with id, name and email. */ public static function managers() { $out = array(); foreach ( get_users( array( 'capability' => Capabilities::manage_primitive(), 'number' => 100, 'orderby' => 'display_name', ) ) as $user ) { $out[] = array( 'id' => (int) $user->ID, 'name' => (string) $user->display_name, 'email' => (string) $user->user_email, ); } return $out; } /** * {@inheritDoc} * * @return void */ public function enqueue() { $this->enqueue_admin_style(); wp_enqueue_media(); wp_enqueue_style( 'wp-color-picker' ); AdminAssets::enqueue( 'admin', 'turtlereport-admin', array( 'wp-color-picker' ) ); AdminAssets::enqueue( 'settings', 'turtlereport-settings' ); } /** * The AI Advisor panel's data. Holds no key, only its last characters. * * @return array|null */ private function ai() { $advisor = $this->advisor ? call_user_func( $this->advisor ) : null; if ( ! $advisor ) { return null; } $providers = array(); foreach ( $advisor->providers() as $p ) { $keys = $advisor->keys_for( $p->id() ); $default = \TurtleReport\Advisor\Advisor::default_model_for( $p ); $models = array_values( array_unique( array_merge( array( $default ), $p->models() ) ) ); $providers[ $p->id() ] = array( 'label' => $p->label(), 'models' => $models, 'default' => $default, 'configured' => $keys->configured(), 'hint' => $keys->hint(), 'console' => method_exists( $p, 'console' ) ? (string) $p->console() : '', 'placeholder' => method_exists( $p, 'key_placeholder' ) ? (string) $p->key_placeholder() : '', ); } $u = $advisor->usage()->get(); return array( 'configured' => $advisor->keys()->configured(), 'hint' => $advisor->keys()->hint(), 'providers' => $providers, 'usage' => $u, 'log' => array_reverse( array_slice( $advisor->log()->recent(), -8 ) ), ); } }