plugin = $plugin;
$this->settings = $settings;
$this->state_report = $state_report;
$this->cpt_settings_storages = $cpt_settings_storages;
}
public static function has_route_hooks( Route_Detector $route_detector ): bool {
return true;
}
public function set_route_hooks( Route_Detector $route_detector ): void {
if ( $this->settings->is_automatic_reports_disabled() ) {
$this->set_disabled_hooks();
return;
}
$this->set_enabled_hooks( $route_detector );
}
public static function hook(): string {
return Hard_Layout_Cpt::cpt_name() . '_refresh';
}
// WP Cron is unreliable. Execute also within the dashboard (in case the time has come).
public function reschedule_outdated(): void {
$check_time = wp_next_scheduled( self::hook() );
if ( false !== $check_time &&
$check_time > time() ) {
return;
}
if ( false !== $check_time ) {
// firstly, unschedule the outdated event.
wp_unschedule_event( $check_time, self::hook() );
}
// then send and schedule the next.
$this->send_and_schedule_next();
}
public function init(): void {
$check_time = wp_next_scheduled( self::hook() );
if ( false === $check_time ) {
$this->schedule_next();
return;
}
// WP Cron is unreliable. Execute also within the dashboard (in case the time has come).
self::add_action( 'admin_init', array( $this, 'reschedule_outdated' ) );
}
public function show_automatic_reports_notice(): void {
$dismiss_key = 'av-reports-dismiss';
$nonce_name = 'av-reports-notice';
if ( '' !== Query_Arguments::get_string_for_admin_action( $dismiss_key, $nonce_name ) &&
Avf_User::can_manage() ) {
$this->settings->set_is_automatic_reports_confirmed( true );
$this->settings->save();
return;
}
echo '
';
echo '
';
esc_html_e(
'The Advanced Views plugin sends automatic error and usage reports to developers, enabling faster issue resolution and plugin improvement.',
'acf-views'
);
echo '
';
esc_html_e(
'Automatic reports do not include any private or sensitive information and can be disabled in the plugin settings.',
'acf-views'
);
if ( Avf_User::can_manage() ) {
$hide_url = add_query_arg(
array(
$dismiss_key => 1,
'_wpnonce' => wp_create_nonce( $nonce_name ),
)
);
echo '
';
printf(
'%s',
esc_url( $hide_url ),
esc_html( __( 'Got it, hide', 'acf-views' ) )
);
}
echo '
';
echo '
';
}
public function send_and_schedule_next(): void {
if ( ! $this->settings->is_automatic_reports_disabled() ) {
// always unchedule, as this method can be called from outside (e.g. the License form submission).
$this->unschedule();
$this->make_usage_request();
$this->schedule_next();
}
}
public function unschedule(): void {
$check_time = wp_next_scheduled( self::hook() );
if ( false === $check_time ) {
return;
}
wp_unschedule_event( $check_time, self::hook() );
}
protected function make_usage_request(): void {
File_System_Loader::instance()
->add_loaded_callback(
function () {
$fields = array_merge(
$this->state_report->get_primary_fields(),
$this->get_usage_data(),
);
$this->send_json_request( self::USAGE_ENDPOINT_URL, $fields );
}
);
}
protected function schedule_next(): void {
// next_check_time in seconds. Randomly to avoid DDOS.
$next_check_time = time() + wp_rand( self::DELAY_MIN_HR * 3600, self::DELAY_MAX_HRS * 3600 );
wp_schedule_single_event( $next_check_time, self::hook() );
}
/**
* @return array
*/
protected function get_usage_data(): array {
$error_logs = $this->get_logger()->get_error_logs();
if ( strlen( $error_logs ) > 5000 ) {
$error_logs = substr( $error_logs, 0, 5000 );
}
// IT DOESN'T SEND ANY PRIVATE DATA, only a DOMAIN.
// And the domain is only used to avoid multiple counting from one website.
return array(
'_viewsCount' => $this->count_cpt_entities(
Hard_Layout_Cpt::cpt_name(),
array( Plugin_Environment::STARTER_LAYOUT_ID )
),
'_cardsCount' => $this->count_cpt_entities(
Hard_Post_Selection_Cpt::cpt_name(),
array( Plugin_Environment::STARTER_SELECTION_ID )
),
'_vendorPlugins' => $this->detect_vendor_plugins_usage(),
'_isFsStorageActive' => $this->is_fs_storage_active(),
'_gitRepositoriesCount' => count( $this->settings->get_git_repositories() ),
'_language' => get_bloginfo( 'language' ),
'_phpErrors' => $error_logs,
'_templateEngines' => $this->calc_template_engines_usage(),
);
}
protected function is_fs_storage_active(): bool {
$storage = reset( $this->cpt_settings_storages );
// check only the first one, as both Layout & Selection use the same setting.
if ( $storage instanceof Cpt_Settings_Storage ) {
return $storage->get_file_system()->is_active();
}
return false;
}
/**
* @return array plugin => is active
*/
protected function detect_vendor_plugins_usage(): array {
$is_acf_pro = class_exists( 'acf_pro' );
$is_elementor_pro = class_exists( 'ElementorPro\Plugin' );
// 'is_plugin_active()' is available only later.
return array(
'acfPro' => $is_acf_pro,
'acf' => ! $is_acf_pro && class_exists( 'acf' ) && ! defined( 'ACF_VIEWS_INNER_ACF' ),
'woo' => class_exists( 'WooCommerce' ),
'metaBox' => class_exists( 'RW_Meta_Box' ),
'pods' => class_exists( 'Pods' ),
'elementor' => ! $is_elementor_pro && class_exists( 'Elementor\Plugin' ),
'elementorPro' => $is_elementor_pro,
);
}
/**
* @return array engine => count
*/
protected function calc_template_engines_usage(): array {
$stat = array();
foreach ( $this->cpt_settings_storages as $cpt_settings_storage ) {
foreach ( $cpt_settings_storage->get_all() as $cpt_settings ) {
$engine = $cpt_settings->get_template_engine();
if ( strlen( $engine ) > 0 ) {
$stat[ $engine ] = int( $stat, $engine );
++$stat[ $engine ];
}
}
}
return $stat;
}
/**
* @param string[] $exclude_post_names
*/
protected function count_cpt_entities( string $post_type, array $exclude_post_names = array() ): int {
foreach ( $this->cpt_settings_storages as $cpt_settings_storage ) {
$db_management = $cpt_settings_storage->get_db_management();
$current_post_type = $db_management->get_post_type();
if ( $post_type === $current_post_type ) {
$unique_ids = $db_management->get_unique_ids();
$ids_to_count = array_diff( $unique_ids, $exclude_post_names );
return count( $ids_to_count );
}
}
return 0;
}
protected function set_disabled_hooks(): void {
// still sign-up the CRON job, so if it was scheduled before, then will be called without issues.
self::add_action(
self::hook(),
function (): void {
// nothing to do.
}
);
}
protected function set_enabled_hooks( Route_Detector $route_detector ): void {
self::add_action( 'init', array( $this, 'init' ) );
// CRON job.
self::add_action( self::hook(), array( $this, 'send_and_schedule_next' ) );
$is_cpt_list_screen = $route_detector->is_cpt_admin_route(
Hard_Layout_Cpt::cpt_name(),
Route_Detector::CPT_LIST
) ||
$route_detector->is_cpt_admin_route(
Hard_Post_Selection_Cpt::cpt_name(),
Route_Detector::CPT_LIST
);
if ( $is_cpt_list_screen &&
! $this->settings->is_automatic_reports_confirmed() ) {
self::add_action( 'admin_notices', array( $this, 'show_automatic_reports_notice' ) );
}
}
}