admin_menu_hooks = array(); } /** * Create hooks. */ public function hooks() { add_filter( 'plugin_action_links_' . ADCMDR_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ) ); add_action( 'admin_menu', array( $this, 'check_admin_capabilities' ) ); add_action( 'admin_menu', array( $this, 'create_admin_menu' ) ); add_action( 'admin_head', array( $this, 'set_current_admin_menu_item' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'admin_print_styles', array( $this, 'admin_print_styles' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'in_admin_header', array( $this, 'adcmdr_admin_header' ) ); add_action( 'admin_notices', array( $this, 'maybe_onboard_global' ), 11 ); add_action( 'admin_notices', array( $this, 'settings_cache_notice' ) ); add_filter( 'pre_update_option_adcmdr_addons', array( $this, 'addon_options_updated' ), 10, 2 ); add_filter( 'wp_insert_post_data', array( $this, 'modify_post_title' ), 100, 1 ); $this->admin_reports()->hooks(); $this->admin_notifications()->hooks(); AdminAdsense::instance()->hooks(); AdminOnboarding::instance()->hooks(); $duplicate = new Duplicate(); $duplicate->hooks(); add_action( 'admin_init', function () { new AdminAdPostMeta(); new AdminGroupTermMeta(); new AdminPlacementPostMeta(); new AdminCampaignTerm(); new AdminAdvertiserTerm(); } ); add_filter( 'wo_repeater_draggable_icon', function () { return Admin::sortable_handle( false ); } ); add_filter( 'wo_repeater_draggable_icon_width', function () { return 10; } ); add_filter( 'wo_repeater_sort_handle_selector', function () { return '.adcmdr-ui-sort-icon'; } ); /** * Hook fires when AdSense options are updated. */ add_action( 'update_option_adcmdr_adsense', function () { do_action( 'adcmdr_adsense_publisher_id_changed', AdSense::instance()->current_adsense_publisher_id() ); } ); } /** * Create a new WOSettings instance if necessary. * * @return WOSettings */ protected function sf() { if ( ! $this->sf ) { $this->sf = new WOSettings( AdCommander::ns() ); } return $this->sf; } /** * Create a new AdminReports instance if necessary. * * @return AdminReports */ public function admin_reports() { if ( ! $this->admin_reports ) { $this->admin_reports = new AdminReports(); } return $this->admin_reports; } /** * Create a new AdminNotifications instance if necessary. * * @return AdminNotifications */ public function admin_notifications() { if ( ! $this->admin_notifications ) { $this->admin_notifications = new AdminNotifications(); } return $this->admin_notifications; } /** * Create a new AdminGeo instance if necessary. * * @return AdminGeo */ public function admin_geo() { if ( ! $this->admin_geo && ProBridge::instance()->is_pro_loaded() ) { $this->admin_geo = new AdminGeo(); } return $this->admin_geo; } /** * Create a new AdminDashboard instance if necessary. * * @return AdminReports */ public function admin_dashboard() { if ( ! $this->admin_dashboard ) { $this->admin_dashboard = new AdminDashboard(); } return $this->admin_dashboard; } /** * Create a new AdminSupport instance if necessary. * * @return AdminSupport */ public function admin_support() { if ( ! $this->admin_support ) { $this->admin_support = new AdminSupport(); } return $this->admin_support; } /** * Create a new AdminTargeting instance if necessary. * * @return AdminTargeting */ protected function admin_targeting() { if ( ! $this->admin_targeting ) { $this->admin_targeting = new AdminTargeting(); } return $this->admin_targeting; } /** * The UI element for sorting drag-and-drop lists. * * @param bool $display Whether to display or return the handle. * * @return string */ public static function sortable_handle( $display = true ) { $allowed_html = array( 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array(), ), ); // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Loading image from plugin directory. $img = ''; if ( ! $display ) { return wp_kses( $img, $allowed_html ); } echo wp_kses( $img, $allowed_html ); } /** * The URL to the sortable handle * * @return string */ public static function sortable_handle_url() { return AdCommander::assets_url() . 'img/drag.png'; } /** * Add links to the plugin screen. * * @param array $links Existing links passed into this function. * * @return array */ public function plugin_action_links( $links ) { $action_links = array( array( 'title' => __( 'Settings', 'ad-commander' ), 'url' => self::settings_admin_url(), ), array( 'title' => __( 'Support', 'ad-commander' ), 'url' => self::support_admin_url(), ), ); $plugin_links = array(); foreach ( $action_links as $action_link ) { $plugin_links[ Util::ns( sanitize_title( $action_link['title'] ), '_' ) ] = '' . esc_html( $action_link['title'] ) . ''; } return array_merge( $plugin_links, $links ); } /** * Check that administrator and editor roles have the correct caps. * * @return void */ public function check_admin_capabilities() { $admins = get_role( 'administrator' ); $editors = get_role( 'editor' ); if ( ! $admins->has_cap( AdCommander::capability() ) ) { Install::add_cap( $admins ); } $allow_editors = Options::instance()->get( 'allow_editors', 'admin', true, false ); if ( $allow_editors && ! $editors->has_cap( AdCommander::capability() ) ) { Install::add_cap( $editors ); } elseif ( ! $allow_editors && $editors->has_cap( AdCommander::capability() ) ) { Install::remove_cap( $editors ); } } /** * Enqueue styles if we're on a screen that needs them. * * @return void */ public function admin_print_styles() { wp_enqueue_style( Util::ns( 'menu' ), AdCommander::assets_url() . 'css/admin_global.css', array(), AdCommander::version() ); if ( $this->is_screen() ) { $is_pro = ProBridge::instance()->is_pro_loaded(); $frontend = new Frontend(); $frontend->enqueue_or_print_styles( Util::ns( 'front' ), AdCommander::assets_url() . 'css/style.css', AdCommander::version(), false ); if ( $is_pro ) { $frontend->enqueue_or_print_styles( Util::ns( 'front-pro' ), AdCommanderPro::assets_url() . 'css/style.css', AdCommanderPro::version() ); } $this->enqueue_woadmin_styles(); wp_enqueue_style( Util::ns( 'admin' ), AdCommander::assets_url() . 'css/admin.css', array( 'woadmin' ), AdCommander::version() ); if ( ! $is_pro ) { wp_add_inline_style( Util::ns( 'admin' ), '.woadmin-form-inner input[value*="admin.php?page=ad-commander-settings&tab=adcmdr_addons"] ~ p.submit { display: none; }' ); } } } /** * Enqueue styles for jQuery UI as these are not necessarily included already. * * @return void */ protected function enqueue_jquery_ui_styles() { wp_register_style( 'jquery-ui', AdCommander::assets_url() . 'vendor/jquery-ui.min.css', array(), AdCommander::version() ); wp_enqueue_style( 'jquery-ui' ); } /** * Enqueue admin script for showing/hiding settings. * * @return string */ protected function enqueue_setting_restrict() { wp_enqueue_script( 'jquery' ); $handle = Util::ns( 'settingrestrict' ); wp_register_script( $handle, AdCommander::assets_url() . 'js/setting-restrict.js', array( 'jquery' ), AdCommander::version(), array( 'in_footer' => true ) ); wp_enqueue_script( $handle ); return $handle; } /** * Enqueue scripts if we're on a screen that needs them. * * @return void */ public function admin_enqueue_scripts() { if ( $this->is_screen() ) { wp_enqueue_script( 'jquery' ); } $admin_onboarding = AdminOnboarding::instance(); if ( $admin_onboarding->should_onboard() ) { $admin_onboarding->enqueue(); } if ( $this->is_screen( array( AdCommander::tax_group(), AdCommander::posttype_ad(), 'edit-' . AdCommander::tax_group(), 'edit-' . AdCommander::posttype_ad() ) ) ) { wp_enqueue_script( 'jquery' ); $handle = Util::ns( 'copy' ); wp_register_script( $handle, AdCommander::assets_url() . 'js/copy.js', array( 'jquery' ), AdCommander::version(), array( 'in_footer' => true ) ); wp_enqueue_script( $handle ); } if ( isset( $this->admin_menu_hooks[ self::admin_slug( 'reports' ) ] ) && $this->is_screen( $this->admin_menu_hooks[ self::admin_slug( 'reports' ) ] ) ) { $this->admin_reports()->enqueue(); } elseif ( isset( $this->admin_menu_hooks[ self::admin_slug( 'settings' ) ] ) && $this->is_screen( $this->admin_menu_hooks[ self::admin_slug( 'settings' ) ] ) ) { $settings = array( 'restricted' => array(), 'copied' => __( 'Copied!', 'ad-commander' ), ); foreach ( self::settings() as $setting ) { if ( isset( $setting['sections'] ) ) { foreach ( $setting['sections'] as $section ) { if ( isset( $section['fields'] ) ) { foreach ( $section['fields'] as $field ) { if ( ! is_array( $field ) ) { continue; } if ( isset( $field['restricted'] ) ) { $settings['restricted'][] = sanitize_title( $field['restricted'] ); } } } } } } $settings['restricted'] = array_values( array_unique( $settings['restricted'] ) ); $handle = Util::ns( 'settings' ); wp_register_script( $handle, AdCommander::assets_url() . 'js/settings.js', array( 'jquery' ), AdCommander::version(), array( 'in_footer' => true ) ); wp_enqueue_script( $handle ); Util::enqueue_script_data( $handle, $settings ); AdminAdsense::instance()->admin_enqueue_scripts(); /** * Enqueue pro scripts that aren't otherwise enqueued already. */ if ( ProBridge::instance()->is_pro_loaded() ) { $this->admin_geo()->enqueue(); } } } /** * Check the current screen against an array of screen IDs. * * @param array $screen_ids Screen IDs to compare against. * * @return bool */ public function is_screen( $screen_ids = array() ) { $screen = get_current_screen(); if ( ! isset( $screen->id ) ) { return false; } if ( ! $screen_ids ) { $screen_ids = apply_filters( 'adcmdr_admin_all_screen_ids', array( 'edit-' . AdCommander::tax_group(), 'edit-' . AdCommander::tax_campaign(), 'edit-' . AdCommander::tax_advertiser(), 'edit-' . AdCommander::posttype_ad(), 'edit-' . AdCommander::posttype_placement(), AdCommander::posttype_ad(), AdCommander::posttype_placement(), ) ); $screen_ids = array_merge( $screen_ids, apply_filters( 'adcmdr_admin_menu_hooks', array_values( $this->admin_menu_hooks ) ) ); } $screen_ids = Util::arrayify( $screen_ids ); return in_array( $screen->id, $screen_ids ); } /** * Overwrites global variables so that the correct menu items stay open. * * @return void */ public function set_current_admin_menu_item() { global $parent_file, $submenu_file, $post_type, $taxonomy; if ( $taxonomy === AdCommander::tax_group() ) { $parent_file = self::admin_slug(); $submenu_file = self::admin_group_tax_path(); } elseif ( $taxonomy === AdCommander::tax_campaign() ) { $parent_file = self::admin_slug(); $submenu_file = self::admin_campaign_tax_path(); } elseif ( $taxonomy === AdCommander::tax_advertiser() ) { $parent_file = self::admin_slug(); $submenu_file = self::admin_advertiser_tax_path(); } elseif ( $post_type === AdCommander::posttype_ad() ) { $parent_file = self::admin_slug(); $submenu_file = self::admin_ad_post_type_path(); } elseif ( $post_type === AdCommander::posttype_placement() ) { $parent_file = self::admin_slug(); $submenu_file = self::admin_placement_post_type_path(); } } /** * Creates an admin slug using the text domain and an optional sub string. * * @param null|string $sub Optional sub page slug. * * @return string */ public static function admin_slug( $sub = null ) { $slug = 'ad-commander'; if ( $sub ) { $slug .= '-' . sanitize_title( $sub ); } return $slug; } /** * The path to the admin page. * * @param null $sub Optional sub page. * * @return string */ public static function admin_path( $sub = null ) { return 'admin.php?page=' . self::admin_slug( $sub ); } /** * The path to AD post type. * * @return string */ public static function admin_ad_post_type_path() { return 'edit.php?post_type=' . AdCommander::posttype_ad(); } public static function admin_ad_post_type_url() { return admin_url( self::admin_ad_post_type_path() ); } /** * The path to insert post type. * * @return string */ public static function admin_placement_post_type_path() { return 'edit.php?post_type=' . AdCommander::posttype_placement(); } public static function admin_placement_post_type_url() { return admin_url( self::admin_placement_post_type_path() ); } /** * The path to Group taxonomy. * * @return string */ public static function admin_group_tax_path() { return 'edit-tags.php?taxonomy=' . AdCommander::tax_group() . '&post_type=' . AdCommander::posttype_ad(); } /** * The path to Campaigns taxonomy. * * @return string */ public static function admin_campaign_tax_path() { return 'edit-tags.php?taxonomy=' . AdCommander::tax_campaign() . '&post_type=' . AdCommander::posttype_ad(); } /** * The path to Advertisers taxonomy. * * @return string */ public static function admin_advertiser_tax_path() { return 'edit-tags.php?taxonomy=' . AdCommander::tax_advertiser() . '&post_type=' . AdCommander::posttype_ad(); } /** * The URL to edit a group term * * @return string */ public static function admin_group_term_url( $term_id ) { return admin_url( 'term.php?taxonomy=' . AdCommander::tax_group() . '&tag_ID=' . absint( $term_id ) . '&post_type=' . AdCommander::posttype_ad() ); } /** * The URL to edit a group term * * @return string */ public static function admin_campaign_term_url( $term_id ) { return admin_url( 'term.php?taxonomy=' . AdCommander::tax_campaign() . '&tag_ID=' . absint( $term_id ) . '&post_type=' . AdCommander::posttype_ad() ); } /** * The URL to edit an advertiser term * * @return string */ public static function admin_advertiser_term_url( $term_id ) { return admin_url( 'term.php?taxonomy=' . AdCommander::tax_advertiser() . '&tag_ID=' . absint( $term_id ) . '&post_type=' . AdCommander::posttype_ad() ); } /** * Url to the group taxonomy */ public static function admin_group_tax_url() { return admin_url( self::admin_group_tax_path() ); } /** * The path to edit an ad post - or to the trash if the post is trashed. * * @param int $post_id The post ID to edit. * @param string $post_status The post status. * * @return string */ public static function edit_ad_post_url( $post_id, $post_status = 'publish' ) { if ( $post_status === 'trash' ) { return add_query_arg( array( 'post_status' => 'trash', ), self::admin_ad_post_type_path() ); } return admin_url( 'post.php?post=' . absint( $post_id ) . '&action=edit' ); } /** * URL to create a new ad. * * @return string */ public static function new_ad_post_url() { return admin_url( 'post-new.php?post_type=' . AdCommander::posttype_ad() ); } /** * URL to create a new insert. * * @return string */ public static function new_placement_post_url() { return admin_url( 'post-new.php?post_type=' . AdCommander::posttype_placement() ); } /** * The admin_url for the settings page. * * @return string */ public static function settings_admin_url( $tab = false ) { if ( ! $tab ) { return admin_url( self::admin_path( 'settings' ) ); } $sf = new WOSettings( AdCommander::ns() ); return $sf->get_tab_url( $sf->key( $tab ), self::settings_admin_url() ); } /** * The admin_url for the agents page. * * @return string */ public static function agents_admin_url( $tab = false ) { if ( ! $tab ) { return admin_url( self::admin_path( 'agents' ) ); } $sf = new WOSettings( AdCommander::ns() ); return $sf->get_tab_url( $sf->key( $tab ), self::agents_admin_url() ); } /** * The admin_url for the reports page. * * @return string */ public static function reports_admin_url() { return admin_url( self::admin_path( 'reports' ) ); } /** * The admin_url for the support page. * * @return string */ public static function support_admin_url() { return admin_url( self::admin_path( 'support' ) ); } /** * Link to the plugin manual. * * @param array $args Arguments to add to end of link. * * @return string */ public static function documentation_url( $args = array() ) { return AdCommander::public_site_url( 'documentation', $args ); } /** * Link to the latitude/longitude lookup tool * * @param array $args Arguments to add to end of link. * * @return string */ public static function latlng_url( $args = array() ) { return AdCommander::public_site_url( 'latitude-longitude-lookup', $args ); } /** * Link to a support request. * * @return string */ public static function support_public_url() { return 'https://wordpress.org/support/plugin/ad-commander/'; } /** * Link to report a security issue. * * @param array $args Arguments to add to end of link. * * @return string */ public static function security_issue_url( $args = array() ) { return self::bug_report_url(); } /** * Link to report a bug. * * @param array $args Arguments to add to end of link. * * @return string */ public static function bug_report_url( $args = array() ) { $url = AdCommander::public_site_url( 'support', $args ); $url = add_query_arg( array( 'topic' => urlencode( 'Bug report' ), ), $url ); return $url; } /** * Link to upgrade to pro. * * @param array $args Arguments to add to end of link. * * @return string */ public static function pro_upgrade_url( $args = array() ) { return AdCommander::public_site_url( 'download', $args ); } /** * Link to renew your license. * * @return string */ public static function renew_license_url() { return AdCommander::public_site_url( 'order-history/subscriptions' ); } /** * Link to get priority support. * * @param array $args Arguments to add to end of link. * * @return string */ public static function pro_support_url( $args = array() ) { return AdCommander::public_site_url( 'support', $args ); } /** * Link to get priority support. * * @param array $args Arguments to add to end of link. * * @return string */ public static function newsletter_url( $args = array() ) { return AdCommander::public_site_url( 'newsletter', $args ); } /** * Link to leave a review. * * @return string */ public static function review_url() { return 'https://wordpress.org/support/plugin/ad-commander/reviews/#new-post'; } /** * The action (for use in forms). * * @return string */ public function admin_action() { return self::settings_admin_url(); } /** * The title of the settings page * * @return string */ public function settings_title() { return __( 'Settings', 'ad-commander' ); } /** * The title of the agents page * * @return string */ public function agents_title() { return __( 'AI Agents', 'ad-commander' ); } /** * The title of the Dashboard page * * @return string */ public function dashboard_title() { return __( 'Dashboard', 'ad-commander' ); } /** * The title of the Manage ads page * * @return string */ public function manage_ads_title() { return __( 'Manage Ads', 'ad-commander' ); } /** * The title of the Manage groups page * * @return string */ public function manage_groups_title() { return __( 'Manage Ad Groups', 'ad-commander' ); } /** * The title of the Manage campaigns page * * @return string */ public function manage_campaigns_title() { return __( 'Campaigns', 'ad-commander' ); } /** * The title of the Manage advertisers page * * @return string */ public function manage_advertisers_title() { return __( 'Advertisers', 'ad-commander' ); } /** * The title of the Manage inserts page * * @return string */ public function manage_placements_title() { return __( 'Manage Inserts', 'ad-commander' ); } /** * The title of the Reports page * * @return string */ public function reports_title() { return __( 'Reports', 'ad-commander' ); } /** * The title of the Support page * * @return string */ public function support_title() { return __( 'Support', 'ad-commander' ); } /** * Create all of the admin pages. * Also adds the admin page hooks to an array for later use. * * @return void */ public function create_admin_menu() { $dashboard_title = self::dashboard_title(); $notification_count = $this->admin_notifications()->count(); if ( $notification_count > 0 ) { $dashboard_title .= ' ' . absint( $notification_count ) . ''; } $hook = add_menu_page( self::dashboard_title(), AdCommander::title(), AdCommander::capability(), self::admin_slug(), array( $this, 'dashboard_page' ), 'none', 30 ); $this->admin_menu_hooks[ self::admin_slug() ] = $hook; add_submenu_page( self::admin_slug(), self::dashboard_title(), $dashboard_title, AdCommander::capability(), self::admin_slug(), array( $this, 'dashboard_page' ), 1 ); add_submenu_page( self::admin_slug(), self::manage_ads_title(), self::manage_ads_title(), AdCommander::capability(), self::admin_ad_post_type_path(), '', 5 ); add_submenu_page( self::admin_slug(), self::manage_groups_title(), self::manage_groups_title(), AdCommander::capability(), self::admin_group_tax_path(), '', 10 ); add_submenu_page( self::admin_slug(), self::manage_placements_title(), self::manage_placements_title(), AdCommander::capability(), self::admin_placement_post_type_path(), '', 15 ); add_submenu_page( self::admin_slug(), self::manage_campaigns_title(), self::manage_campaigns_title(), AdCommander::capability(), self::admin_campaign_tax_path(), '', 16 ); add_submenu_page( self::admin_slug(), self::manage_advertisers_title(), self::manage_advertisers_title(), AdCommander::capability(), self::admin_advertiser_tax_path(), '', 17 ); $hook = add_submenu_page( self::admin_slug(), self::reports_title(), self::reports_title(), AdCommander::capability(), self::admin_slug( 'reports' ), array( $this, 'reports_page' ), 20 ); $this->admin_menu_hooks[ self::admin_slug( 'reports' ) ] = $hook; $hook = add_submenu_page( self::admin_slug(), $this->agents_title(), $this->agents_title(), 'manage_options', self::admin_slug( 'agents' ), array( $this, 'agents_page' ), 50 ); $this->admin_menu_hooks[ self::admin_slug( 'agents' ) ] = $hook; $hook = add_submenu_page( self::admin_slug(), $this->settings_title(), $this->settings_title(), 'manage_options', self::admin_slug( 'settings' ), array( $this, 'settings_page' ), 50 ); $this->admin_menu_hooks[ self::admin_slug( 'settings' ) ] = $hook; $hook = add_submenu_page( self::admin_slug(), self::support_title(), self::support_title(), AdCommander::capability(), self::admin_slug( 'support' ), array( $this, 'support_page' ), 70 ); $this->admin_menu_hooks[ self::admin_slug( 'support' ) ] = $hook; } /** * The master settings array. * This array will be parsed by other functions to implement various functionality. * We shouldn't have to edit any existing functions to add new options. * Instead, we just modify this array. * * @return array */ public static function settings() { return apply_filters( 'adcmdr_settings', array( 'general' => array( 'title' => __( 'General', 'ad-commander' ), 'initialize' => array( 'render_method' => array( 'callback' => array( '\ADCmdr\Util', 'default_render_method' ) ), 'newwindow' => 1, 'rel_attributes' => array( 'sponsored' ), 'responsive_banners' => 1, 'filter_priority' => Placement::placement_priority_default(), 'disable_virtual_adstxt' => array( 'callback' => array( '\ADCmdr\AdsTxt', 'should_disable_ads_txt_on_plugin_activation' ) ), 'prefix' => Util::prefix(), ), 'sections' => array( 'general' => array( 'title' => __( 'General', 'ad-commander' ), 'fields' => array( 'render_method' => __( 'Ad & Group Rendering', 'ad-commander' ), 'ad_label' => __( 'Ad Label', 'ad-commander' ), 'prefix' => __( 'Class/ID Prefix', 'ad-commander' ), 'lazy_load_ads' => __( 'Lazy Load Ads', 'ad-commander' ), 'loading_animation' => __( 'Loading Animation', 'ad-commander' ), ), ), 'defaults' => array( 'title' => __( 'Default Banner Ad Settings', 'ad-commander' ), 'fields' => array( 'newwindow' => __( 'Link Target', 'ad-commander' ), 'rel_attributes' => __( 'Link Rel Attributes', 'ad-commander' ), 'responsive_banners' => __( 'Responsive Ad Images', 'ad-commander' ), ), ), 'disable_ads' => array( 'title' => __( 'Disable Ads', 'ad-commander' ), 'fields' => array( 'disable_all' => __( 'Disable All Ads', 'ad-commander' ), 'disable_user_roles' => __( 'Disable Ads for User Roles', 'ad-commander' ), 'bots_disable_ads' => __( 'Disable Ads for Bots', 'ad-commander' ), ), ), 'ads_txt' => array( 'title' => __( 'Virtual ads.txt', 'ad-commander' ), 'fields' => array( 'disable_virtual_adstxt' => __( 'Disable Ads.txt', 'ad-commander' ), 'ads_txt_records' => __( 'Ads.txt Records', 'ad-commander' ), ), ), 'advanced_settings' => array( 'title' => __( 'Advanced / Miscellaneous', 'ad-commander' ), 'fields' => array( 'filter_priority' => __( 'Insert Filter Priority', 'ad-commander' ), 'disable_stylesheets' => __( 'Disable Plugin Stylesheets', 'ad-commander' ), ), ), ), ), 'adsense' => array( 'title' => __( 'AdSense', 'ad-commander' ), 'sections' => array( 'adsense' => array( 'title' => __( 'AdSense Account', 'ad-commander' ), 'fields' => array( 'adsense_account' => __( 'AdSense Account', 'ad-commander' ), 'disable_adsense_account_alerts' => __( 'AdSense Alerts', 'ad-commander' ), ), ), 'auto_ads' => array( 'title' => __( 'Auto Ads', 'ad-commander' ), 'fields' => array( 'insert_adsense_head_code' => __( 'Insert AdSense Code', 'ad-commander' ), 'enable_amp_auto_ads' => __( 'AMP Auto Ads', 'ad-commander' ), ), ), 'amp_defaults' => array( 'title' => __( 'Default AMP Settings', 'ad-commander' ), 'fields' => array( 'adsense_amp_ad_mode' => __( 'AdSense AMP ads', 'ad-commander' ), ), ), ), ), 'tracking' => array( 'title' => __( 'Tracking', 'ad-commander' ), 'initialize' => array( 'enable_local_tracking' => 1, ), 'sections' => array( 'tracking_methods' => array( 'title' => __( 'Tracking Method', 'ad-commander' ), 'fields' => array( 'enable_local_tracking' => __( 'Local Tracking', 'ad-commander' ), 'enable_alt_local_tracking' => array( 'title' => '', 'restricted' => 'enable_local_tracking', ), 'disable_amp_pixel' => array( 'title' => '', 'restricted' => 'enable_local_tracking', ), 'enable_ga_tracking' => __( 'Google Analytics (GA4)', 'ad-commander' ), 'disable_amp_analytics' => array( 'title' => '', 'restricted' => 'enable_ga_tracking', ), 'ga_tracking_ids' => array( 'title' => __( 'GA Tracking IDs', 'ad-commander' ), 'restricted' => 'enable_ga_tracking', ), 'ga_anonymize_ip' => array( 'title' => __( 'GA Anonymize IP', 'ad-commander' ), 'restricted' => 'enable_ga_tracking', ), 'enable_3p_tracking' => __( 'Third-Party Tracking', 'ad-commander' ), ), ), 'disable_tracking' => array( 'title' => __( 'Disable Tracking', 'ad-commander' ), 'fields' => array( 'disable_track_impressions' => __( 'Impression Tracking', 'ad-commander' ), 'disable_track_clicks' => __( 'Click tracking', 'ad-commander' ), 'bots_disable_tracking' => __( 'Disable Tracking for Bots', 'ad-commander' ), ), ), 'click_fraud' => array( 'title' => __( 'Click Fraud Prevention', 'ad-commander' ), 'fields' => array( 'click_fraud_enable' => __( 'Enable', 'ad-commander' ), 'click_fraud_max_clicks' => array( 'title' => __( 'Max Clicks', 'ad-commander' ), 'restricted' => 'click_fraud_enable', ), 'click_fraud_detection_period' => array( 'title' => __( 'Time Period (hours)', 'ad-commander' ), 'restricted' => 'click_fraud_enable', ), 'click_fraud_hide_period' => array( 'title' => __( 'Hide Ads Period (days)', 'ad-commander' ), 'restricted' => 'click_fraud_enable', ), ), ), ), ), 'targeting' => array( 'title' => __( 'Targeting', 'ad-commander' ), 'sections' => array( 'geotargeting' => array( 'title' => __( 'Geotargeting', 'ad-commander' ), 'fields' => array( 'geo_service' => __( 'Geolocation Service', 'ad-commander' ), 'maxmind_account_id' => array( 'title' => __( 'MaxMind Account ID', 'ad-commander' ), 'restricted' => 'geo_service', 'restricted_val' => 'maxmind', ), 'maxmind_license_key' => array( 'title' => __( 'MaxMind License Key', 'ad-commander' ), 'restricted' => 'geo_service', 'restricted_val' => 'maxmind', ), 'maxmind_database_update' => array( 'title' => __( 'MaxMind Database', 'ad-commander' ), 'restricted' => 'geo_service', 'restricted_val' => 'maxmind', ), ), ), ), ), 'privacy' => array( 'title' => __( 'Consent', 'ad-commander' ), 'sections' => array( 'consent' => array( // 'title' => __( 'consent', 'ad-commander' ), 'fields' => array( 'consent_required' => __( 'Require Consent', 'ad-commander' ), 'consent_cookie_name' => array( 'title' => __( 'Consent Cookie Name', 'ad-commander' ), 'restricted' => 'consent_required', ), 'consent_cookie_value' => array( 'title' => __( 'Consent Cookie Value', 'ad-commander' ), 'restricted' => 'consent_required', ), 'consent_cookie_comparison' => array( 'title' => __( 'Cookie Comparison', 'ad-commander' ), 'restricted' => 'consent_required', ), ), ), ), ), 'admin' => array( 'title' => __( 'Admin', 'ad-commander' ), 'sections' => array( 'onboarding' => array( 'title' => __( 'Onboarding', 'ad-commander' ), 'fields' => array( 'disable_onboarding_global' => __( 'Disable Onboarding Message', 'ad-commander' ), 'disable_onboarding_ads' => __( 'Disable Ad Message', 'ad-commander' ), ), ), 'misc' => array( 'title' => __( 'Miscellaneous', 'ad-commander' ), 'fields' => array( 'disable_code_editor' => __( 'Disable Code Editor', 'ad-commander' ), 'disable_doc_links' => __( 'Disable Help Links', 'ad-commander' ), 'allow_editors' => __( 'Allow editors to manage', 'ad-commander' ), 'delete_data' => __( 'Delete data on deactivation', 'ad-commander' ), ), ), ), ), 'addons' => array( 'title' => __( 'Add-ons', 'ad-commander' ), 'sections' => array( 'licenses' => array( 'title' => __( 'Pro', 'ad-commander' ), 'fields' => array( 'pro_license_key' => __( 'Pro License', 'ad-commander' ), ), ), ), ), 'status' => array( 'title' => __( 'System Status', 'ad-commander' ), 'sections' => array(), ), ) ); } public static function agents_settings() { return apply_filters( 'adcmdr_agents_settings', array( 'abilities' => array( 'title' => __( 'Abilities', 'ad-commander' ), 'initialize' => array( 'ability_list_ads' => 1, 'ability_get_ad_stats' => 1, 'ability_toggle_published' => ProBridge::instance()->is_pro_loaded() ? 1 : 0, ), 'sections' => array( 'ability_notice' => array( 'fields' => array(), ), 'ability_general' => array( 'title' => __( 'General', 'ad-commander' ), 'fields' => array( 'ability_list_ads' => __( 'List Ads', 'ad-commander' ), ), ), 'ability_reporting' => array( 'title' => __( 'Reporting', 'ad-commander' ), 'fields' => array( 'ability_get_ad_stats' => __( 'Get Ad Stats', 'ad-commander' ), ), ), 'ability_ad_management' => array( 'title' => __( 'Ad Management', 'ad-commander' ), 'fields' => array( 'ability_toggle_published' => __( 'Enable & Disable Ads', 'ad-commander' ), ), ), ), ), ) ); } /** * Add settings for mulsisite installations. * This is not currently in use but may be added in the future. * * @param array $settings The filtered settings array. * * @return array */ public function multisite_settings( $settings ) { if ( is_multisite() ) { /** * This is currently not in use, because adding unfiltered HTML capabilities is dangerous. * If we can find a safe way to grant this permission, we may do so in the future. */ if ( current_user_can( 'unfiltered_html' ) ) { $settings['admin']['sections']['multisite'] = array( 'title' => __( 'Multisite', 'ad-commander' ), 'fields' => array( 'allow_unfiltered_html' => __( 'Allow unfiltered HTML', 'ad-commander' ), ), ); } } return $settings; } /** * Start the wrap. * * @return void */ protected function start_wrap() { $this->sf()->start(); } /** * End the wrap. * * @return void */ protected function end_wrap() { $this->sf()->end(); } /** * Determine if the code editor should be used. * * @return bool */ public function use_code_editor() { return ! Options::instance()->get( 'disable_code_editor', 'admin', true, false ); } public function textarea_code_args( $args = array() ) { $defaults = $this->use_code_editor() ? array( 'classes' => Util::ns( 'code-editor' ) ) : array( 'classes' => Util::ns( 'code-text' ), ); return array_merge( $defaults, $args ); } /** * Maybe register the code editor. * Enqueing should be done elsewhere to control order of enqueued scripts. */ public function maybe_register_code_editor( $handle, $deps = array() ) { $code_handle = false; if ( $this->use_code_editor() !== false ) { $code_editor_settings = wp_enqueue_code_editor( apply_filters( 'adcmdr_code_editor_args', array( 'type' => 'text/html', 'codemirror' => array( 'autoRefresh' => true ), ) ) ); if ( false !== apply_filters( 'adcmdr_code_editor_settings', $code_editor_settings ) ) { wp_enqueue_script( 'wp-theme-plugin-editor' ); wp_enqueue_style( 'wp-codemirror' ); $code_handle = Util::ns( 'code-editor' ); wp_register_script( $code_handle, AdCommander::assets_url() . 'js/code-editor.js', array_merge( $deps, array( $handle ) ), AdCommander::version(), array( 'in_footer' => true ) ); } } return $code_handle; } /** * Branded Ad Commander header for plugin admin pages. * * @return void */ public function adcmdr_admin_header() { if ( $this->is_screen() ) : ?>
is_pro_loaded() ) : ?>
should_onboard( 'global' ) ) { if ( ! $this->is_screen() || $this->is_screen( array( $this->admin_menu_hooks[ self::admin_slug() ], $this->admin_menu_hooks[ self::admin_slug( 'support' ) ] ) ) ) { $admin_onboarding->onboarding_notice(); } } } /** * Start the poststuff div. * * @param array $classes Classes applied to poststuff div. * * @return void */ protected function start_poststuff( $classes = array() ) { $classes = Util::arrayify( $classes ); $classes[] = Util::ns( 'poststuff' ); ?>
true ) ) { $classes = Util::arrayify( $classes ); $html = '
'; if ( ! $args['display'] ) { return wp_kses_post( $html ); } echo wp_kses_post( $html ); } /** * End the poststuff div. * * @return void */ protected function end_poststuff() { $this->end_div(); } /** * End a div. * * @param array $args An array of arguments. * * @return void|string */ protected function end_div( $args = array( 'display' => true ) ) { $html = '
'; if ( ! $args['display'] ) { return wp_kses_post( $html ); } echo wp_kses_post( $html ); } /** * Create a postbox. * * @param string $title Title of post box. * @param string $content Content in post box. * @param array $classes Classes on postbox. * * @return void */ protected function postbox( $title, $content, $classes = array() ) { $classes = Util::arrayify( $classes ); $classes[] = 'postbox'; $classes[] = Util::ns( 'postbox' ); ?>

admin_dashboard()->page(); } /** * Display the reports page. * * @return void */ public function reports_page() { $this->admin_reports()->page(); } /** * Display the support page. * * @return void */ public function support_page() { $this->admin_support()->page(); } /** * Display the settings page. * * @return void */ public function settings_page() { $this->sf()->settings_page( $this->settings_title(), self::settings_admin_url(), self::settings(), $this->footer_content() ); } /** * Notice to clear cache after updating settings. */ public function settings_cache_notice() { if ( $this->is_screen( $this->admin_menu_hooks[ self::admin_slug( 'settings' ) ] ) ) { echo '

'; esc_html_e( 'Clear all cache including page cache, object cache, and minified javascript after updating settings. Some settings are passed as javascript variables.', 'ad-commander' ); echo '

'; } } /** * Display the agents page. * * @return void */ public function agents_page() { $this->sf()->settings_page( $this->agents_title(), self::agents_admin_url(), self::agents_settings(), $this->footer_content() ); } public function footer_content() { return array( Util::ns( 'status', '_' ) => array( $this, 'debug_info' ), ); } public function debug_info() { ?>

format_debug_line( __( 'Version', 'ad-commander' ), AdCommander::version() ); $lines[] = $this->format_debug_line( __( 'Database Version', 'ad-commander' ), Util::get_dbversion() ); $lines[] = $this->format_debug_line( __( 'Pro Loaded', 'ad-commander' ), ProBridge::instance()->is_pro_loaded() ? __( 'Yes', 'ad-commander' ) : __( 'No', 'ad-commander' ) ); if ( ProBridge::instance()->is_pro_loaded() ) { $pro_version = defined( 'ADCMDR_PRO_LOADED' ) && class_exists( '\ADCmdr\AdCommanderPro' ) ? \ADCmdr\AdCommanderPro::version() : '—'; $lines[] = $this->format_debug_line( __( 'Pro Version', 'ad-commander' ), $pro_version ); $lines[] = $this->format_debug_line( __( 'Pro License', 'ad-commander' ), ProBridge::instance()->is_pro_license_added() ? __( 'Active', 'ad-commander' ) : __( 'Not added', 'ad-commander' ) ); } /** * Plugin Settings */ foreach ( $settings as $group_key => $group ) { if ( 'addons' === $group_key ) { continue; } $opt_key = Util::ns( $group_key, '_' ); $raw_group = get_option( $opt_key, array() ); $lines[] = ''; $lines[] = '## ' . $group['title'] . ' ' . __( 'Settings', 'ad-commander' ); $lines[] = ''; if ( ! empty( $group['sections'] ) ) { foreach ( $group['sections'] as $section_key => $section ) { if ( empty( $section['fields'] ) ) { continue; } foreach ( $section['fields'] as $field_key => $field_label ) { $label = is_array( $field_label ) ? ( isset( $field_label['title'] ) ? $field_label['title'] : $field_key ) : $field_label; if ( empty( $label ) ) { continue; } $value = isset( $raw_group[ $field_key ] ) ? $raw_group[ $field_key ] : null; $lines[] = $this->format_debug_line( $label, $this->format_debug_value( $value, ( in_array( $field_key, array( 'maxmind_license_key', 'pro_license_key' ) ) ) ) ); } if ( $section_key === 'geotargeting' && ProBridge::instance()->is_pro_loaded() && GeoMaxMind::instance()->city_reader() ) { $visitor = GeoPro::instance()->get_visitor( true ); if ( $visitor && ! empty( $visitor ) ) { $lines[] = ''; $lines[] = '### ' . __( 'Current Visitor Geo', 'ad-commander' ); $visitor_labels = array( 'lookup_types' => __( 'Lookup Types', 'ad-commander' ), 'city' => __( 'City', 'ad-commander' ), 'region' => __( 'Region', 'ad-commander' ), 'country_code' => __( 'Country Code', 'ad-commander' ), 'continent_code' => __( 'Continent Code', 'ad-commander' ), 'lat' => __( 'Latitude', 'ad-commander' ), 'lng' => __( 'Longitude', 'ad-commander' ), ); foreach ( $visitor_labels as $key => $label ) { if ( isset( $visitor[ $key ] ) ) { $value = is_array( $visitor[ $key ] ) ? implode( ', ', $visitor[ $key ] ) : $visitor[ $key ]; $lines[] = $this->format_debug_line( $label, $value ); } } } } } } } /** * WordPress Environment */ global $wpdb; $lines[] = ''; $lines[] = '## ' . __( 'WordPress Environment', 'ad-commander' ); $lines[] = ''; $lines[] = $this->format_debug_line( __( 'Home URL', 'ad-commander' ), home_url() ); $lines[] = $this->format_debug_line( __( 'Site URL', 'ad-commander' ), site_url() ); $lines[] = $this->format_debug_line( __( 'REST API Base URL', 'ad-commander' ), rest_url() ); $lines[] = $this->format_debug_line( __( 'WordPress Version', 'ad-commander' ), get_bloginfo( 'version' ) ); $lines[] = $this->format_debug_line( __( 'WordPress Multisite', 'ad-commander' ), is_multisite() ? __( 'Yes', 'ad-commander' ) : __( 'No', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'WordPress Memory Limit', 'ad-commander' ), WP_MEMORY_LIMIT ); $lines[] = $this->format_debug_line( __( 'WordPress Debug Mode', 'ad-commander' ), ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? __( 'Yes', 'ad-commander' ) : __( 'No', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'WordPress Debug Log', 'ad-commander' ), ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) ? __( 'Yes', 'ad-commander' ) : __( 'No', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'WordPress Cron', 'ad-commander' ), ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ? __( 'No', 'ad-commander' ) : __( 'Yes', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'WordPress Locale', 'ad-commander' ), get_locale() ); /** * Active Theme */ $theme = wp_get_theme(); $theme_author = $theme->get( 'Author' ); $theme_detail = $theme_author ? 'by ' . wp_strip_all_tags( $theme_author ) . ' — ' . $theme->get( 'Version' ) : $theme->get( 'Version' ); $lines[] = ''; $lines[] = '## ' . __( 'Active Theme', 'ad-commander' ); $lines[] = ''; $lines[] = $this->format_debug_line( $theme->get( 'Name' ), $theme_detail ); if ( $theme->parent() ) { $lines[] = $this->format_debug_line( __( 'Parent Theme', 'ad-commander' ), $theme->parent()->get( 'Name' ) . ' — ' . $theme->parent()->get( 'Version' ) ); } /** * Active Plugins */ $active_plugins = get_option( 'active_plugins', array() ); $all_plugins = get_plugins(); $lines[] = ''; $lines[] = '## ' . __( 'Active Plugins', 'ad-commander' ); $lines[] = ''; foreach ( $active_plugins as $plugin_path ) { if ( ! isset( $all_plugins[ $plugin_path ] ) ) { continue; } $plugin = $all_plugins[ $plugin_path ]; $plugin_detail = isset( $plugin['AuthorName'] ) && $plugin['AuthorName'] ? 'by ' . $plugin['AuthorName'] . ' — ' . $plugin['Version'] : $plugin['Version']; $lines[] = $this->format_debug_line( $plugin['Name'], $plugin_detail ); } /** * Server Environment */ $lines[] = ''; $lines[] = '## ' . __( 'Server Environment', 'ad-commander' ); $lines[] = ''; $lines[] = $this->format_debug_line( __( 'Web Server', 'ad-commander' ), isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'PHP Version', 'ad-commander' ), function_exists( 'phpversion' ) ? phpversion() : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'PHP Memory Limit', 'ad-commander' ), function_exists( 'ini_get' ) ? ini_get( 'memory_limit' ) : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'PHP Max Execution Time', 'ad-commander' ), function_exists( 'ini_get' ) ? ini_get( 'max_execution_time' ) : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'PHP Max Upload Size', 'ad-commander' ), function_exists( 'ini_get' ) ? ini_get( 'upload_max_filesize' ) : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'PHP Max Post Size', 'ad-commander' ), function_exists( 'ini_get' ) ? ini_get( 'post_max_size' ) : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'cURL Enabled', 'ad-commander' ), function_exists( 'curl_version' ) ? __( 'Yes', 'ad-commander' ) . ' (' . curl_version()['version'] . ')' : __( 'No', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'OpenSSL', 'ad-commander' ), defined( 'OPENSSL_VERSION_TEXT' ) ? OPENSSL_VERSION_TEXT : __( 'No', 'ad-commander' ) ); /** * Database */ $lines[] = ''; $lines[] = '## ' . __( 'Database', 'ad-commander' ); $lines[] = ''; $lines[] = $this->format_debug_line( __( 'Database System', 'ad-commander' ), ! empty( $wpdb->db_server_info() ) ? $wpdb->db_server_info() : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'Database Version', 'ad-commander' ), $wpdb->db_version() ); $lines[] = $this->format_debug_line( __( 'Database Character Set', 'ad-commander' ), ! empty( $wpdb->charset ) ? $wpdb->charset : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'Database Collation', 'ad-commander' ), ! empty( $wpdb->collate ) ? $wpdb->collate : __( 'Unknown', 'ad-commander' ) ); $lines[] = $this->format_debug_line( __( 'Database Prefix', 'ad-commander' ), $wpdb->prefix ); /** * Date and Time */ $lines[] = ''; $lines[] = '## ' . __( 'Date and Time', 'ad-commander' ); $lines[] = ''; $lines[] = $this->format_debug_line( __( 'WordPress Timezone', 'ad-commander' ), wp_timezone_string() ); $lines[] = $this->format_debug_line( __( 'Server Time (UTC)', 'ad-commander' ), gmdate( 'Y-m-d H:i:s' ) ); $lines[] = $this->format_debug_line( __( 'Local Time', 'ad-commander' ), wp_date( 'Y-m-d H:i:s' ) ); $output = implode( "\n", $lines ); ?>
sf()->add_sections_and_settings( $this->settings(), $this ); $this->sf()->add_sections_and_settings( self::agents_settings(), $this ); } /** * Sanitize input by type. * * @param array $input The input to sanitize. * * @return array */ private function sanitize_by_key_type( $input ) { $output = array(); if ( $input ) { foreach ( $input as $key => $value ) { switch ( $key ) { case 'render_method': case 'adsense_account': case 'ga_tracking_ids': case 'rel_attributes': case 'geo_service': case 'maxmind_license_key': case 'maxmind_account_id': case 'maxmind_database_update': case 'ad_label_text': case 'pro_license_key': case 'disable_user_roles': case 'consent_cookie_name': case 'consent_cookie_value': case 'consent_cookie_comparison': case 'prefix': case 'adsense_amp_ad_mode': $type = 'str'; break; case 'filter_priority': case 'automatic_placement': case 'click_fraud_max_clicks': case 'click_fraud_detection_period': case 'click_fraud_hide_period': $type = 'int'; break; case 'ads_txt_records': $type = 'textarea'; break; default: // wo_log( 'missing type for ' . $key ); $type = 'bool'; break; } $output[ $key ] = WOAdmin::sanitize_by_type( $value, $type ); } } return $output; } /** * Sanitize general group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_general( $input ) { Maintenance::flush_css_transients(); return $this->sanitize_by_key_type( $input ); } /** * Sanitize abilities group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_abilities( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Sanitize admin group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_admin( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Sanitize tracking group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_tracking( $input ) { $input = $this->sanitize_by_key_type( $input ); if ( ProBridge::instance()->pro_feature_check( 'tracking_no_admin_ajax' ) ) { if ( isset( $input['enable_alt_local_tracking'] ) && $input['enable_alt_local_tracking'] === 1 ) { $errors = FrontTrackConfig::check(); if ( ! empty( $errors ) ) { $input['enable_alt_local_tracking'] = 0; } else { $force_params = array(); if ( isset( $input['bots_disable_tracking'] ) ) { if ( $input['bots_disable_tracking'] === 1 ) { $force_params['bots_disable_tracking'] = 1; } else { $force_params['bots_disable_tracking'] = 0; } } FrontTrackConfig::write( $force_params ); } } else { $errors = FrontTrackConfig::check(); if ( ! empty( $errors ) ) { FrontTrackConfig::delete(); } } } return $input; } /** * Sanitize adsense group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_adsense( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Sanitize privacy group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_privacy( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Sanitize geotargeting group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_targeting( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Sanitize add-on group. * * @param array $input The input to sanitize. * * @return array */ public function sanitize_adcmdr_addons( $input ) { return $this->sanitize_by_key_type( $input ); } /** * Create an underscored key from a string. * * @param string $key The key to convert. * * @return string */ public function _key( $key ) { return str_replace( '-', '_', $key ); } /** * Create a nonce array. * This is not an actual nonce. This array is used for creating and checking unique nonces. * * @param string $action The action.. * @param string $key The key to create the nonce string from. * * @return array */ protected function nonce( $action, $key ) { return array( 'action' => $action, 'name' => $this->nonce_string( $key ), ); } /** * Create a nonce field. * * @param array $nonce_arr A nonce array. * @param bool $referrer Whether to include the referrer field. * * @return void */ protected function nonce_field( $nonce_arr, $referrer = false ) { wp_nonce_field( $nonce_arr['action'], $nonce_arr['name'], $referrer ); } /** * Create a string for use in your nonce. * * @param string $action Action name. * * @return string */ protected function nonce_string( $action ) { return sanitize_title( Util::ns( 'n-' . $action ) ); } /** * Create an action string for use in your nonce. * * @param string $action Action name. * * @return string */ protected function action_string( $action ) { return self::_key( sanitize_title( Util::ns( $action, '_' ) ) ); } /** * Authorizes actions for non-settings pages. * Interfaces with WOAdmin to check nonce, capability, and required $_POST keys. * * @param mixed $action_key The action key to authorized. * @param array $required_post Any required post keys. * @param string|bool $capability The capability required to process this action. * * @return void */ protected function check_ajax_action( $action_key, $required_post = array(), $capability = false ) { /** * In some cases we may want 'manage_options' instead of the custom AdCommander::capability(). * This would be for anything in the Settings section. * * Other areas use the custom cap so that editors can manage ads, groups, etc., if allowed. */ if ( ! $capability ) { $capability = AdCommander::capability(); } $this->authorize_ajax_action( $this->nonce_string( $action_key ), 'security', $capability, $required_post ); } /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_defaults() { ?>

info( sprintf( __( 'Consent management only partially works with server-side rendering. %1$sUpdate render settings.%2$s %3$s', 'ad-commander' ), '', '', Doc::doc_link( 'requiring_consent', false, __( 'Learn more', 'ad-commander' ) ) ), array( 'adcmdr-metaitem__warning' ) ); } ?>

required_pro_version_message( '1.2.1' ); } /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_advanced_settings() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_admin() { } /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_notifications() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_misc() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_onboarding() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_geotargeting() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_multisite() {} /** * Call back for settings section. * * @return void */ public function settings_callback_adcmdr_ads_txt() { ?>

info( __( 'WordPress abilities are not available in your environment. Abilities require WordPress 6.9 or higher.', 'ad-commander' ), array( 'adcmdr-metaitem__error' ) ); } $this->info( __( 'Ad Commander uses the WordPress Abilities API to expose functionality to AI Agents. These features still require authentication by your agent in order to access. Learn more in our documentation about authenticating your agent.', 'ad-commander' ) ); Doc::doc_link( 'ai_agents', true, __( 'Documentation', 'ad-commander' ) ); } public function settings_callback_adcmdr_ability_general() {} public function settings_callback_adcmdr_ability_reporting() {} public function settings_callback_adcmdr_ability_ad_management() {} public function field_adcmdr_ability_get_ad_stats() { $id = array( $this->sf()->key( 'abilities' ) => 'ability_get_ad_stats' ); $this->sf()->checkbox( $id, $this->sf()->get( 'ability_get_ad_stats', 'abilities' ) ); $this->sf()->label( $id, __( 'Enable ability to get ad statistics', 'ad-commander' ) ); } public function field_adcmdr_ability_toggle_published() { $disabled = ! ProBridge::instance()->is_pro_loaded(); $id = array( $this->sf()->key( 'abilities' ) => 'ability_toggle_published' ); $this->sf()->checkbox( $id, $this->sf()->get( 'ability_toggle_published', 'abilities' ), 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, __( 'Ability to toggle ads between published and draft status.', 'ad-commander' ) ); if ( $disabled ) { /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped in pro_label and also includes HTML */ echo ProBridge::instance()->pro_label( true ); } } public function field_adcmdr_ability_list_ads() { $id = array( $this->sf()->key( 'abilities' ) => 'ability_list_ads' ); $this->sf()->checkbox( $id, $this->sf()->get( 'ability_list_ads', 'abilities' ) ); $this->sf()->label( $id, __( 'Enable ability to get a list of ads', 'ad-commander' ) ); $this->sf()->message( __( 'This ability is often used by other abilities. It is recommended to have it enabled if any other abilities are enabled.', 'ad-commander' ) ); } /** * Perform actions during add-on options updates. * Specifically, determine if the license key has changed. * * @param mixed $new_value The new value of the option. * @param mixed $old_value The old value of the option. * * @return mixed */ public function addon_options_updated( $new_value, $old_value ) { if ( $new_value !== $old_value ) { $old_license = isset( $old_value['pro_license_key'] ) ? $old_value['pro_license_key'] : null; $new_license = isset( $new_value['pro_license_key'] ) ? $new_value['pro_license_key'] : null; if ( $new_license !== $old_license ) { $status = ProBridge::instance()->pro_license_status(); if ( $old_license && ! in_array( $status, array( 'processing', 'pending', 'deactivated' ) ) ) { ProBridge::instance()->pro_license_deactivate( $old_license ); } ProBridge::instance()->pro_license_reset_status( true, ( $new_license ) ? true : false ); } } return $new_value; } /** * Callback for settings field. * * @return void */ public function field_adcmdr_pro_license_key() { $pro_bridge = ProBridge::instance(); if ( $pro_bridge->is_pro_loaded() ) { $id = array( $this->sf()->key( 'addons' ) => 'pro_license_key' ); $current_value = $this->sf()->get( 'pro_license_key', 'addons' ); $disabled = false; $message = null; $message_secondary = null; $clr = 'neutral'; $status = $pro_bridge->pro_license_status(); if ( $current_value || $status ) { if ( $status === 'pending' || $status === 'processing' ) { $message = '' . esc_html__( 'License update is pending...', 'ad-commander' ); } elseif ( $status === 'valid' ) { $clr = 'yes'; $message = ' ' . esc_html__( 'License key active.', 'ad-commander' ); } elseif ( $status === 'expired' ) { $clr = 'no'; /* translators: %1$s: anchor tag with URL, %2$s call to action text, %3$s: close anchor tag */ $message = ' ' . sprintf( esc_html__( 'License key expired - %1$s%2$s%3$s', 'ad-commander' ), '', esc_html__( 'Renew your license', 'ad-commander' ) . ' >', '' ); } elseif ( method_exists( License::class, 'no_seats_license_statuses' ) && in_array( $status, License::no_seats_license_statuses() ) ) { $clr = 'no'; /* translators: %1$s: anchor tag with URL, %2$s call to action text, %3$s: close anchor tag */ $message = ' ' . sprintf( esc_html__( 'License has no remaining activations - %1$s%2$s%3$s', 'ad-commander' ), '', esc_html__( 'Purchase a new license', 'ad-commander' ) . ' >', '' ); } elseif ( method_exists( License::class, 'invalid_license_statuses' ) && in_array( $status, License::invalid_license_statuses() ) ) { $clr = 'no'; if ( method_exists( License::class, 'connection_error_statuses' ) && in_array( $status, License::connection_error_statuses() ) ) { /* translators: %1$s: response code */ $message = ' ' . sprintf( esc_html__( 'License key could not be validated (response: %1$s)', 'ad-commander' ), sanitize_text_field( $status ) ); if ( $status === 'forbidden' ) { $message_secondary = esc_html__( 'A forbidden error indicates your server was potentially blocked by our WAF or other security measure. Please contact support and include your server\'s external IP address and date/time that you attempted to validate your license key.', 'ad-commander' ); } else { $message_secondary = esc_html__( 'Your server had trouble connecting to our license server. This is NOT a 403 forbidden error from our WAF. Either your server is blocking external POST requests to our server or there was a temporary connection issue between our servers. Wait a few minutes, then try removing, re-saving, then re-adding your license key to attempt the connection again. If the problem persists, please contact support with your server\'s external IP address and date/time that you attempted to validate your license key.', 'ad-commander' ); } } else { /* translators: %1$s: response code, %2$s anchor tag with URL, %3$s call to action text, %4$s: close anchor tag */ $message = ' ' . sprintf( esc_html__( 'License key invalid (response: %1$s) - %2$s%3$s%4$s', 'ad-commander' ), sanitize_text_field( $status ), '', esc_html__( 'Purchase a license', 'ad-commander' ) . ' >', '' ); } } elseif ( $status !== 'deactivated' ) { $clr = 'no'; /* translators: %1$s: anchor tag with URL, %2$s call to action text, %3$s: close anchor tag */ $message = ' ' . sprintf( esc_html__( 'An unkonwn error occurred - Please %1$s%2$s%3$s', 'ad-commander' ), '', esc_html__( 'contact support', 'ad-commander' ) . ' >', '' ); } $pro_bridge->enqueue_pro_license_script_data( $status ); } $this->sf()->input( $id, $current_value, 'text', array( 'disabled' => $disabled ) ); if ( $message ) { $this->sf()->message( '' . $message . '' ); } if ( $message_secondary ) { $this->sf()->message( '' . esc_html( $message_secondary ) . '' ); } } else { echo wp_kses_post( $this->get_addons() ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_geo_service() { $id = array( $this->sf()->key( 'targeting' ) => 'geo_service' ); $current_value = null; $args = array( 'empty_text' => __( 'Select a service', 'ad-commander' ), ); $options = array( 'disabled:maxmind' => __( 'MaxMind', 'ad-commander' ), ); if ( ProBridge::instance()->is_pro_loaded() ) { $current_value = $this->sf()->get( 'geo_service', 'targeting' ); $pro_options = array(); foreach ( $options as $key => $text ) { $pro_options[ str_replace( 'disabled:', '', $key ) ] = $text; } $options = $pro_options; } else { $args['empty_text'] .= ProBridge::pro_label(); } $this->sf()->select( $id, $options, $current_value, $args ); Doc::doc_link( 'geo_targeting' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_maxmind_license_key() { if ( ProBridge::instance()->is_pro_loaded() ) { $this->admin_geo()->field_maxmind_license_key(); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_maxmind_account_id() { if ( ProBridge::instance()->is_pro_loaded() ) { if ( ProBridge::instance()->pro_feature_check( 'maxmindapiupdate' ) ) { $this->admin_geo()->field_maxmind_account_id(); } else { // add a message stating pro needs to be upgraded for this feature $this->info( __( 'This feature requires an update to the Pro add-on. Please update to the latest version of Pro to use this feature.', 'ad-commander' ), array( 'adcmdr-metaitem__error' ) ); } } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_maxmind_database_update() { if ( ProBridge::instance()->is_pro_loaded() ) { $this->admin_geo()->field_maxmind_database_update(); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_render_method() { $pro_only = array( 'clientside', 'smart' ); $render_options = array( 'serverside' => esc_html__( 'Server-side', 'ad-commander' ), 'smart' => esc_html__( 'Smart (Combination)', 'ad-commander' ), 'clientside' => esc_html__( 'Client-side Only', 'ad-commander' ), ); $options = $render_options; if ( ! ProBridge::instance()->is_pro_loaded() ) { $no_pro_options = array(); foreach ( $render_options as $key => $text ) { if ( in_array( $key, $pro_only ) ) { $no_pro_options[ 'disabled:' . $key ] = $text . ProBridge::pro_label(); } else { $no_pro_options[ $key ] = $text; } } $options = $no_pro_options; } $message = array( '' . $render_options['serverside'] . esc_html__( ':', 'ad-commander' ) . ' ' . esc_html__( 'Render ads with other page content. Not usually compatible with page caching and some targeting settings.', 'ad-commander' ), '' . $render_options['smart'] . esc_html__( ':', 'ad-commander' ) . ' ' . esc_html__( 'Client-side if group settings, targeting methods, or other settings are likely to conflict with page caching. Otherwise, uses server-side.', 'ad-commander' ), '' . $render_options['clientside'] . esc_html__( ':', 'ad-commander' ) . ' ' . esc_html__( 'Render ads after other page content loads using javascript. Circumvents page caching and works with all visitor targeting methods.', 'ad-commander' ), ); array_walk( $message, function ( &$item ) { $item = '
  • • ' . $item . '
  • '; } ); $message = ''; $id = array( $this->sf()->key( 'general' ) => 'render_method' ); $current_value = $this->sf()->get( 'render_method', 'general' ); $this->sf()->select( $id, $options, $current_value ? $current_value : array_key_first( $options ), ); Doc::doc_link( 'rendering' ); $this->sf()->message( $message ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_ad_label() { $id = array( $this->sf()->key( 'general' ) => 'ad_label' ); $this->sf()->checkbox( $id, $this->sf()->get( 'ad_label', 'general' ) ); $this->sf()->label( $id, __( 'Display ad label above ads or groups', 'ad-commander' ) ); echo wp_kses_post( '
    ' ); $id = array( $this->sf()->key( 'general' ) => 'ad_label_text' ); $this->sf()->input( $id, $this->sf()->get( 'ad_label_text', 'general', __( 'Advertisement', 'ad-commander' ) ) ); echo wp_kses_post( '
    ' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_prefix() { $id = array( $this->sf()->key( 'general' ) => 'prefix' ); $this->sf()->input( $id, $this->sf()->get( 'prefix', 'general', Util::prefix() ) ); $this->sf()->message( __( 'Using a unique prefix may help circumvent ad blockers. Recommended length is 5-7 characters and it should contain only alpha letters.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_loading_animation() { $id = array( $this->sf()->key( 'general' ) => 'loading_animation' ); $this->sf()->checkbox( $id, $this->sf()->get( 'loading_animation', 'general' ) ); $this->sf()->label( $id, __( 'Show loading animation before ads display', 'ad-commander' ) ); $this->sf()->message( __( 'Only applies to ads and groups loaded client-side, and only to the first ad on the page.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_lazy_load_ads() { $id = array( $this->sf()->key( 'general' ) => 'lazy_load_ads' ); $label = __( "Don't load ads until they enter viewport", 'ad-commander' ); $current_value = 0; $disabled = true; if ( ProBridge::instance()->is_pro_loaded() ) { $current_value = $this->sf()->get( 'lazy_load_ads', 'general' ); $disabled = false; } else { $label .= ProBridge::pro_label(); } $this->sf()->checkbox( $id, $current_value, 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, $label ); Doc::doc_link( 'lazy_load_ads' ); $message = esc_html__( 'Lazy loading ads requires smart or client-side rendering.', 'ad-commander' ); if ( ! $disabled && $current_value === 1 && Util::render_method() === 'serverside' ) { $message = '' . $message . ''; } $message .= ' ' . esc_html__( 'If tracking is enabled, impressions will track when the ad loads.', 'ad-commander' ); $this->sf()->message( $message ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_onboarding_global() { $id = array( $this->sf()->key( 'admin' ) => 'disable_onboarding_global' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_onboarding_global', 'admin' ) ); $this->sf()->label( $id, __( 'Disable onboarding message and help links', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_code_editor() { $id = array( $this->sf()->key( 'admin' ) => 'disable_code_editor' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_code_editor', 'admin' ) ); $this->sf()->label( $id, __( 'Disable code editor throughout admin. Use regular textarea instead.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_onboarding_ads() { $id = array( $this->sf()->key( 'admin' ) => 'disable_onboarding_ads' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_onboarding_ads', 'admin' ) ); $this->sf()->label( $id, __( 'Disable message that appears after new ad is published', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_doc_links() { $id = array( $this->sf()->key( 'admin' ) => 'disable_doc_links' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_doc_links', 'admin' ) ); $this->sf()->label( $id, __( 'Disable documentation links throughout plugin', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_virtual_adstxt() { $id = array( $this->sf()->key( 'general' ) => 'disable_virtual_adstxt' ); $disabled = false; $disabled_text = ''; $current_value = $this->sf()->get( 'disable_virtual_adstxt', 'general' ); $ads_txt = new AdsTxt(); $can_generate = $ads_txt->can_generate_ads_txt( true ); if ( $can_generate === false || ( is_array( $can_generate ) && isset( $can_generate['can'] ) && $can_generate['can'] === false ) ) { if ( isset( $can_generate['message'] ) && $can_generate['message'] !== '' ) { $disabled_text = $can_generate['message']; } else { $disabled_text = __( 'Virtual ads.txt is disabled due to incompatibility with your site.', 'ad-commander' ); } $disabled = true; $current_value = 1; } elseif ( $ads_txt->alternate_file_exists() ) { $disabled_text = __( 'A physical ads.txt or one generated by another plugin already exists. We suggest disabling this setting or removing the other ads.txt.', 'ad-commander' ); } elseif ( is_array( $can_generate ) && isset( $can_generate['message'] ) && $can_generate['message'] !== '' ) { $disabled_text = $can_generate['message']; } $this->sf()->checkbox( $id, $current_value, 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, __( 'Do not generate a virtual ads.txt', 'ad-commander' ) ); if ( $disabled_text !== '' ) { $this->info( $disabled_text, array( 'adcmdr-metaitem__warning' ) ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_ads_txt_records() { $id = array( $this->sf()->key( 'general' ) => 'ads_txt_records' ); $this->sf()->message( __( 'One record per line. AdSense is automatically included if publisher ID is set. Add additional records here.', 'ad-commander' ) ); $ads_txt = new AdsTxt(); $output = $ads_txt->adsense_ads_txt_line(); if ( $output != '' ) { $this->sf()->message( '' . $output . '', array( 'classes' => 'adcmdr-readonly', 'element' => 'div', ) ); } $this->sf()->textarea( $id, $this->sf()->get( 'ads_txt_records', 'general' ) ); /* translators: %1$s: anchor tag with URL, %2$s text representation of URL, %3$s: close anchor tag */ $message = sprintf( __( 'You can preview your ads.txt at %1$s%2$s%3$s', 'ad-commander' ), '', site_url( 'ads.txt' ), '' ); $this->sf()->message( $message ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_newwindow() { $id = array( $this->sf()->key( 'general' ) => 'newwindow' ); $this->sf()->checkbox( $id, $this->sf()->get( 'newwindow', 'general' ) ); $this->sf()->label( $id, __( 'Open links in a new window', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_rel_attributes() { $id = array( $this->sf()->key( 'general' ) => 'rel_attributes' ); $checkboxes = array( 'sponsored' => 'rel=sponsored', 'nofollow' => 'rel=nofollow', 'noreferrer' => 'rel=noreferer', 'noopener' => 'rel=noopener', ); $this->sf()->checkgroup( $id, $checkboxes, $this->sf()->get( 'rel_attributes', 'general' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_responsive_banners() { $id = array( $this->sf()->key( 'general' ) => 'responsive_banners' ); $this->sf()->checkbox( $id, $this->sf()->get( 'responsive_banners', 'general' ) ); $this->sf()->label( $id, __( 'Include responsive styles for images in ads', 'ad-commander' ) ); /* translators: %1$s: CSS 'auto' code, %2$s, CSS '100%' code */ $message = sprintf( __( 'Images will have max-width set to %1$s and height set to %2$s. Theme styles may override this setting.', 'ad-commander' ), '100%', 'auto' ); $this->sf()->message( $message ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_filter_priority() { $id = array( $this->sf()->key( 'general' ) => 'filter_priority' ); $priority = $this->sf()->get( 'filter_priority', 'general' ); $this->sf()->input( $id, $priority ? $priority : Placement::placement_priority_default(), 'number' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_stylesheets() { $id = array( $this->sf()->key( 'general' ) => 'disable_stylesheets' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_stylesheets', 'general' ) ); $this->sf()->label( $id, __( 'Disable all front-end plugin stylesheets (some ads may not display correctly)', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_bots_disable_ads() { $id = array( $this->sf()->key( 'general' ) => 'bots_disable_ads' ); $this->sf()->checkbox( $id, $this->sf()->get( 'bots_disable_ads', 'general' ) ); $this->sf()->label( $id, __( 'Hide ads for suspected bots. Read help doc first.', 'ad-commander' ) ); Doc::doc_link( 'bots' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_all() { $id = array( $this->sf()->key( 'general' ) => 'disable_all' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_all', 'general' ) ); $this->sf()->label( $id, __( 'Disable all ads for everyone', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_user_roles() { $roles = TargetingVisitor::parse_roles_to_values(); $id = array( $this->sf()->key( 'general' ) => 'disable_user_roles' ); $this->sf()->checkgroup( $id, $roles, $this->sf()->get( 'disable_user_roles', 'general' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_allow_editors() { $id = array( $this->sf()->key( 'admin' ) => 'allow_editors' ); $this->sf()->checkbox( $id, $this->sf()->get( 'allow_editors', 'admin' ) ); $this->sf()->label( $id, __( 'Allow editors to manage Ad Commander', 'ad-commander' ) ); $this->sf()->message( __( 'Only administrators - or those with manage_options capability - will have access to settings.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_allow_unfiltered_html() { $id = array( $this->sf()->key( 'admin' ) => 'allow_unfiltered_html' ); $this->sf()->checkbox( $id, $this->sf()->get( 'allow_unfiltered_html', 'admin' ) ); $this->sf()->label( $id, __( 'Allow unfiltered HTML by administrators and editors', 'ad-commander' ) ); $this->sf()->message( __( 'By default, only Super Admins can add unfiltered HTML in a multisite installation.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_delete_data() { $id = array( $this->sf()->key( 'admin' ) => 'delete_data' ); $this->sf()->checkbox( $id, $this->sf()->get( 'delete_data', 'admin' ) ); /* translators: %1$s: open span tag with class; %2$s close span tag */ $this->sf()->label( $id, sprintf( __( '%1$sDANGER:%2$s Delete all plugin data (ads, groups, stats, options, etc) when plugin deactivated', 'ad-commander' ), '', '' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_track_impressions() { $id = array( $this->sf()->key( 'tracking' ) => 'disable_track_impressions' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_track_impressions', 'tracking' ) ); $this->sf()->label( $id, __( 'Disable impression tracking for all ads', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_track_clicks() { $id = array( $this->sf()->key( 'tracking' ) => 'disable_track_clicks' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_track_clicks', 'tracking' ) ); $this->sf()->label( $id, __( 'Disable click tracking for all ads', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_enable_local_tracking() { $id = array( $this->sf()->key( 'tracking' ) => 'enable_local_tracking' ); $this->sf()->checkbox( $id, $this->sf()->get( 'enable_local_tracking', 'tracking' ) ); $this->sf()->label( $id, __( 'Enable local tracking', 'ad-commander' ) ); Doc::doc_link( 'tracking_methods' ); $this->sf()->message( __( 'Local tracking is required for Ad Commander reports and to expire ads at maximum impressions or clicks. If not using these features, disabling may increase performance.', 'ad-commander' ) ); $is_database_compatible = Tracking::check_database_compatibility(); if ( $is_database_compatible !== true ) { $this->info( $is_database_compatible, array( 'adcmdr-metaitem__error' ) ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_enable_alt_local_tracking() { $id = array( $this->sf()->key( 'tracking' ) => 'enable_alt_local_tracking' ); $label = __( 'Use non-WordPress local tracking script (beta)', 'ad-commander' ); $disabled = false; if ( ! ProBridge::instance()->is_pro_loaded() ) { $disabled = true; $label .= ProBridge::pro_label(); } $this->sf()->checkbox( $id, $this->sf()->get( 'enable_alt_local_tracking', 'tracking' ), 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, $label ); Doc::doc_link( 'tracking_methods' ); $this->sf()->message( __( 'Circumvents admin-ajax.php. May impact performance. Read documentation first.', 'ad-commander' ) ); if ( ProBridge::instance()->pro_feature_check( 'tracking_no_admin_ajax' ) ) { $errors = FrontTrackConfig::check(); if ( ! empty( $errors ) ) { $this->info( 'Alternate local tracking is not available in your environment. The following errors were encountered: ' . implode( ' ', $errors ), array( 'adcmdr-metaitem__warning' ) ); } } elseif ( ProBridge::instance()->is_pro_loaded() ) { $this->info( 'Alternate local tracking requires Ad Commander Pro 1.4.1 or higher.', array( 'adcmdr-metaitem__error' ) ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_enable_3p_tracking() { $id = array( $this->sf()->key( 'tracking' ) => 'enable_3p_tracking' ); $label = __( 'Enable 3rd-party tracking', 'ad-commander' ); $current_value = 0; $disabled = true; if ( ProBridge::instance()->is_pro_loaded() ) { $current_value = $this->sf()->get( 'enable_3p_tracking', 'tracking' ); $disabled = false; } else { $label .= ProBridge::pro_label(); } $this->sf()->checkbox( $id, $current_value, 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, $label ); Doc::doc_link( 'third_party_tracking' ); $this->sf()->message( __( 'Third-party tracking requires implementation of custom tracking code. See help doc for more information.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_enable_ga_tracking() { $id = array( $this->sf()->key( 'tracking' ) => 'enable_ga_tracking' ); $label = __( 'Enable Google Analytics tracking', 'ad-commander' ); $disabled = true; $current_value = 0; if ( ProBridge::instance()->is_pro_loaded() ) { $current_value = $this->sf()->get( 'enable_ga_tracking', 'tracking' ); $disabled = false; } else { $label .= ProBridge::pro_label(); } $this->sf()->checkbox( $id, $current_value, 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, $label ); Doc::doc_link( 'tracking_methods' ); $this->sf()->message( __( 'GA tracking will send events for impression and click to Google Analytics.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_ga_tracking_ids() { $id = array( $this->sf()->key( 'tracking' ) => 'ga_tracking_ids' ); $this->sf()->input( $id, $this->sf()->get( 'ga_tracking_ids', 'tracking' ) ); /* translators: %1$s: sample Google Analytics ID */ $this->sf()->message( sprintf( __( 'One or more GA4 property IDs where clicks and impressions should be sent. Comma separated. Example: %1$s', 'ad-commander' ), 'G-XXXXXXXXXX' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_ga_anonymize_ip() { $id = array( $this->sf()->key( 'tracking' ) => 'ga_anonymize_ip' ); $this->sf()->checkbox( $id, $this->sf()->get( 'ga_anonymize_ip', 'tracking' ) ); $this->sf()->label( $id, __( 'Tell Google Ananlytics to anonymize IPs', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_amp_analytics() { $id = array( $this->sf()->key( 'tracking' ) => 'disable_amp_analytics' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_amp_analytics', 'tracking' ) ); $this->sf()->label( $id, __( 'Disable AMP analytics', 'ad-commander' ) ); $this->sf()->message( esc_html__( 'Used for GA impression tracking of AMP sites.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_amp_pixel() { $id = array( $this->sf()->key( 'tracking' ) => 'disable_amp_pixel' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_amp_pixel', 'tracking' ) ); $this->sf()->label( $id, __( 'Disable AMP pixel', 'ad-commander' ) ); $this->sf()->message( esc_html__( 'Used for local impression tracking of AMP sites.', 'ad-commander' ) ); if ( ! is_ssl() ) { $this->info( esc_html__( 'AMP pixel requires your site to use HTTPS.', 'ad-commander' ), array( 'adcmdr-metaitem__warning' ) ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_bots_disable_tracking() { $id = array( $this->sf()->key( 'tracking' ) => 'bots_disable_tracking' ); $this->sf()->checkbox( $id, $this->sf()->get( 'bots_disable_tracking', 'tracking' ) ); $this->sf()->label( $id, __( 'Disable tracking for bots. Read help doc first.', 'ad-commander' ) ); Doc::doc_link( 'bots' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_click_fraud_enable() { $id = array( $this->sf()->key( 'tracking' ) => 'click_fraud_enable' ); $disabled = ! ProBridge::instance()->is_pro_loaded(); $this->sf()->checkbox( $id, $this->sf()->get( 'click_fraud_enable', 'tracking' ), 1, array( 'disabled' => $disabled ) ); $this->sf()->label( $id, __( 'Enable click fraud prevention', 'ad-commander' ) . ( $disabled ? ProBridge::instance()->pro_label() : '' ) ); Doc::doc_link( 'click_fraud' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_click_fraud_max_clicks() { $id = array( $this->sf()->key( 'tracking' ) => 'click_fraud_max_clicks' ); $disabled = ! ProBridge::instance()->is_pro_loaded(); $this->sf()->input( $id, $this->sf()->get( 'click_fraud_max_clicks', 'tracking' ), 'number', array( 'disabled' => $disabled ) ); $this->sf()->message( __( 'Maximum number of ad clicks (e.g., 3) allowed before ads are hidden from the visitor.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_click_fraud_detection_period() { $id = array( $this->sf()->key( 'tracking' ) => 'click_fraud_detection_period' ); $disabled = ! ProBridge::instance()->is_pro_loaded(); $this->sf()->input( $id, $this->sf()->get( 'click_fraud_detection_period', 'tracking' ), 'number', array( 'disabled' => $disabled ) ); $this->sf()->message( __( 'Time period in hours (e.g., 3) during which clicks are counted.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_click_fraud_hide_period() { $id = array( $this->sf()->key( 'tracking' ) => 'click_fraud_hide_period' ); $disabled = ! ProBridge::instance()->is_pro_loaded(); $this->sf()->input( $id, $this->sf()->get( 'click_fraud_hide_period', 'tracking' ), 'number', array( 'disabled' => $disabled ) ); $this->sf()->message( __( 'Number of days (e.g., 7) to hide ads from the visitor after exceeding the click limit.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_adsense_account() { $pub_id = $this->sf()->get( 'adsense_account', 'adsense' ); $adsense_connected = AdminAdsense::instance()->has_access_token( $pub_id ); if ( $adsense_connected && ! $pub_id ) { AdminAdsense::instance()->revoke_access( $pub_id ); $adsense_connected = false; } ?>
    disabled>
    sf()->message( esc_html__( 'Or enter your publisher ID manually for limited functionality.', 'ad-commander' ) ); } elseif ( $adsense_connected && $pub_id ) { $adsense_api = AdminAdSense::get_adsense_api_account(); if ( isset( $adsense_api['accounts'][ $pub_id ] ) && isset( $adsense_api['accounts'][ $pub_id ]['account_details'] ) ) { $pub_label .= ' (' . '' . esc_html( $adsense_api['accounts'][ $pub_id ]['account_details']['account_name'] ) . ')'; } if ( ! Options::instance()->get( 'disable_adsense_account_alerts', 'adsense', true ) ) { $alerts = AdminNotifications::instance()->test_adsense_alerts(); if ( $alerts && ! empty( $alerts ) ) { $account_message = $this->info( ' ' . '' . esc_html__( 'View account alerts', 'ad-commander' ) . ' >', array( 'adcmdr-metaitem__warning' ), array( 'display' => false ) ); } } } $pub_label .= esc_html_x( ':', 'After publisher ID label', 'ad-commander' ); $id = array( $this->sf()->key( 'adsense' ) => 'adsense_account' ); $this->sf()->label( $id, $pub_label ); echo '
    '; $this->sf()->input( $id, $pub_id, 'text', array( 'placeholder' => 'pub-XXXXXXXXXX', 'readonly' => (bool) $adsense_connected, ) ); if ( ! $adsense_connected && $pub_id && ! AdSense::instance()->is_publisher_id_valid( $pub_id ) ) { $this->info( esc_html__( 'Your publisher ID appears to be an invalid format. It should start with pub-', 'ad-commander' ), array( 'adcmdr-metaitem__error' ) ); } echo wp_kses_post( $account_message ); ?>

    sf()->key( 'adsense' ) => 'adsense_amp_ad_mode' ); $modes = AdSense::amp_modes(); unset( $modes['site_default'] ); $disabled = ! ProBridge::instance()->is_pro_loaded(); if ( $disabled ) { $modes = Util::disable_options( $modes ); foreach ( $modes as $key => $text ) { $modes[ $key ] = $text . ProBridge::instance()->pro_label(); } } $this->sf()->radiogroup( $id, $modes, $this->sf()->get( 'adsense_amp_ad_mode', 'adsense', 'automatic' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_insert_adsense_head_code() { $id = array( $this->sf()->key( 'adsense' ) => 'insert_adsense_head_code' ); $this->sf()->checkbox( $id, $this->sf()->get( 'insert_adsense_head_code', 'adsense' ) ); $this->sf()->label( $id, __( 'Insert AdSense script in head on every page (for auto ads and site verification)', 'ad-commander' ) ); if ( Options::instance()->get( 'insert_adsense_head_code', 'adsense', true ) ) { $this->sf()->message( '' . htmlentities( AdSense::instance()->get_adsense_script_tag( false, 'pub-XXXXXXXXXX' ) ) . '' ); } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_enable_amp_auto_ads() { $id = array( $this->sf()->key( 'adsense' ) => 'enable_amp_auto_ads' ); $this->sf()->checkbox( $id, $this->sf()->get( 'enable_amp_auto_ads', 'adsense' ) ); $this->sf()->label( $id, __( 'Insert AMP auto ads code on every page', 'ad-commander' ) ); if ( Options::instance()->get( 'enable_amp_auto_ads', 'adsense', true ) ) { $this->sf()->message( '' . htmlentities( Amp::instance()->get_amp_auto_ads_script_tag() ) . '' ); $this->sf()->message( '' . htmlentities( Amp::instance()->get_amp_auto_ads_element( false, 'pub-XXXXXXXXXX' ) ) . '' ); if ( ! Amp::instance()->has_amp_plugin() ) { $this->info( __( 'No AMP plugin found. View documentation for more information.', 'ad-commander' ) . ' ' . Doc::doc_link( 'amp', false ), array( 'adcmdr-metaitem__warning' ) ); } } } /** * Callback for settings field. * * @return void */ public function field_adcmdr_disable_adsense_account_alerts() { $pub_id = $this->sf()->get( 'adsense_account', 'adsense' ); $adsense_connected = AdminAdsense::instance()->has_access_token( $pub_id ); $id = array( $this->sf()->key( 'adsense' ) => 'disable_adsense_account_alerts' ); $this->sf()->checkbox( $id, $this->sf()->get( 'disable_adsense_account_alerts', 'adsense' ), 1, array( 'disabled' => ! $adsense_connected ) ); $this->sf()->label( $id, __( 'Disable AdSense connected account alerts in the Ad Commander Dashboard', 'ad-commander' ), array( 'disabled' => ! $adsense_connected ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_consent_required() { $id = array( $this->sf()->key( 'privacy' ) => 'consent_required' ); $this->sf()->checkbox( $id, $this->sf()->get( 'consent_required', 'privacy' ), 1 ); $this->sf()->label( $id, __( 'Require consent before displaying ads.', 'ad-commander' ) ); Doc::doc_link( 'requiring_consent' ); echo wp_kses_post( '
    ' ); $this->sf()->message( __( 'If enabled, this setting can be overridden by individual ads, groups, or inserts.', 'ad-commander' ) ); echo wp_kses_post( '
    ' ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_consent_cookie_name() { $id = array( $this->sf()->key( 'privacy' ) => 'consent_cookie_name' ); $this->sf()->input( $id, $this->sf()->get( 'consent_cookie_name', 'privacy' ) ); /* translators: %1$s: sample Google Analytics ID */ $this->sf()->message( __( 'The name of the cookie that indicates a user has given consent.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_consent_cookie_value() { $id = array( $this->sf()->key( 'privacy' ) => 'consent_cookie_value' ); $this->sf()->input( $id, $this->sf()->get( 'consent_cookie_value', 'privacy' ) ); /* translators: %1$s: sample Google Analytics ID */ $this->sf()->message( __( 'The cookie should have this value. Leave blank to accept any value.', 'ad-commander' ) ); } /** * Callback for settings field. * * @return void */ public function field_adcmdr_consent_cookie_comparison() { $options = array( 'equals' => __( 'Equals', 'ad-commander' ), 'contains' => __( 'Contains', 'ad-commander' ), 'equals_insensitive' => __( 'Equals (case-insensitive)', 'ad-commander' ), 'contains_insensitive' => __( 'Contains (case-insensitive)', 'ad-commander' ), ); $id = array( $this->sf()->key( 'privacy' ) => 'consent_cookie_comparison' ); $current_value = $this->sf()->get( 'consent_cookie_comparison', 'privacy' ); $this->sf()->select( $id, $options, $current_value ? $current_value : array_key_first( $options ), ); $this->sf()->message( __( 'The manner in which to check the cookie value. If the cookie value above is blank, this setting is ignored and Ad Commander will check if the cookie exists.', 'ad-commander' ) ); } /** * Create classes for use on a meta item. * * @param array $suffixes An array of suffices to append. * * @return string */ public static function metaitem_classes( $suffixes = array() ) { $classes = array( 'adcmdr-metaitem' ); if ( $suffixes && ! is_array( $suffixes ) ) { $suffixes = array( $suffixes ); } if ( $suffixes ) { foreach ( $suffixes as $suffix ) { $classes[] = 'adcmdr-metaitem--' . $suffix; } } return implode( ' ', $classes ); } /** * Interfaces with WOMeta message and adds classes. * * @param string $message The message to display. * @param array $classes Any additional classes. * @param array $args Additional arguments. * * @return string */ protected function info( $message, $classes = array(), $args = array() ) { $classes = Util::arrayify( $classes ); $classes[] = 'adcmdr-metaitem__info'; $args['classes'] = $classes; $args['element'] = 'div'; $args['inner_element'] = 'p'; return $this->sf()->wo_forms->message( $message, $args ); } protected function required_pro_version_message( $version, $display = true ) { if ( ProBridge::instance()->is_pro_loaded() && ! ProBridge::instance()->pro_version_required( $version ) ) { /* translators: %s: required Pro version number */ return $this->info( sprintf( __( 'This feature requires Ad Commander Pro %s or greater. Please update your version of Pro.', 'ad-commander' ), $version ), 'adcmdr-notification adcmdr-notice-error', array( 'display' => $display ) ); } } /** * Display an add-on table. * * @return string */ protected function get_addons() { $html = ''; if ( ! ProBridge::instance()->is_pro_loaded() ) { $lis = array( __( 'Priority support and updates', 'ad-commander' ), __( 'Client-side and smart rendering modes (cache busting)', 'ad-commander' ), __( 'Google Analytics tracking (GA4)', 'ad-commander' ), __( 'Visitor targeting options', 'ad-commander' ), __( 'Geotargeting with MaxMind', 'ad-commander' ), __( 'Group and insert targeting options', 'ad-commander' ), __( 'Advanced automatic insert positions', 'ad-commander' ), __( 'Weighted and evenly distributed ads', 'ad-commander' ), __( 'Advanced group layouts', 'ad-commander' ), __( 'Expire ads by date or stats', 'ad-commander' ), __( 'Increased AdSense API calls', 'ad-commander' ), __( '...and more', 'ad-commander' ), ); foreach ( $lis as &$li ) { $li = ' ' . $li; } $html .= $this->start_div( Util::ns( 'addons' ), array( 'display' => false ) ) . $this->start_div( array( Util::ns( 'addon-features' ), Util::ns( 'pro-features' ) ), array( 'display' => false ) ) . Html::h3( AdCommander::title() . ' Pro' ) . Html::ul( $lis, Util::ns( 'addon' ) ) . $this->start_div( 'adcmdr-btn-group', array( 'display' => false ) ) . Html::abtn( self::pro_upgrade_url( array( 'utm_medium' => 'button' ) ), __( 'Upgrade to Pro', 'ad-commander' ) ) . Html::abtn( AdCommander::public_site_url( '', array( 'utm_medium' => 'button' ) ), 'Learn more', '_blank', true ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ); } if ( ! AddonBridge::instance()->is_addon_loaded( 'tools' ) ) { $lis = array( __( 'Export Ad Commander ads', 'ad-commander' ), __( 'Import Ad Commander ads from another site', 'ad-commander' ), __( 'Reset all ad statistics', 'ad-commander' ), __( 'Reset single ad statistics', 'ad-commander' ), __( 'Purge rogue statistics (deleted ads)', 'ad-commander' ), __( 'Available in the WordPress plugin directory', 'ad-commander' ), ); foreach ( $lis as &$li ) { $li = ' ' . $li; } $html .= $this->start_div( Util::ns( 'addons' ), array( 'display' => false ) ) . $this->start_div( Util::ns( 'addon-features' ), array( 'display' => false ) ) . Html::h3( __( 'Ad Commander Tools', 'ad-commander' ) ) . Html::ul( $lis, Util::ns( 'addon' ) ) . $this->start_div( 'adcmdr-btn-group', array( 'display' => false ) ) . Html::abtn( AddonBridge::addon_url( 'tools' ), __( 'Download Add-on', 'ad-commander' ) ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ); } if ( ! AddonBridge::instance()->is_addon_loaded( 'scrnsa' ) ) { $p = esc_html__( "Screen Stay Awake prevents your website visitor's screen from turning off. The Screen Wake Lock API is used and no visitor prompt is required.", 'ad-commander' ); $p .= '

    '; $p .= esc_html__( 'This plugin is great for recipe websites and other how-to websites where a visitor would prefer their screen stay on.', 'ad-commander' ); $html .= $this->start_div( Util::ns( 'addons' ), array( 'display' => false ) ) . $this->start_div( Util::ns( 'addon-features' ), array( 'display' => false ) ) . Html::h3( __( 'Screen Stay Awake', 'ad-commander' ) ) . Html::p( $p ) . $this->start_div( 'adcmdr-btn-group', array( 'display' => false ) ) . Html::abtn( AddonBridge::addon_url( 'scrnsa' ), __( 'Download Plugin', 'ad-commander' ) ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ) . $this->end_div( array( 'display' => false ) ); } if ( $html ) { $html = '
    ' . $html . '
    '; } return $html; } /** * Modify the ad or insert post title if it's blank. * * @param array $post_data The current post data. * * @return array */ public function modify_post_title( $post_data ) { if ( $post_data['post_type'] === AdCommander::posttype_ad() || $post_data['post_type'] === AdCommander::posttype_placement() ) { if ( ( ! isset( $post_data['post_title'] ) || trim( $post_data['post_title'] ) === '' ) && isset( $post_data['post_modified_gmt'] ) ) { $prefix = $post_data['post_type'] === AdCommander::posttype_ad() ? esc_html__( 'Ad', 'ad-commander' ) : esc_html__( 'Insert', 'ad-commander' ); $post_data['post_title'] = $prefix . ' | ' . get_date_from_gmt( $post_data['post_modified_gmt'], get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ); } } return $post_data; } }