'_affiliate_links_target', 'title' => 'Link Target URL', 'description' => 'Enter your affiliate URL or make use of wecantrack\'s affiliate link generator and simply add the desired landing page URL (eg. https://www.nike.com) and tick the \'Generate affiliate link\' setting below.', 'type' => 'url', 'required' => 'required', 'sanitize_callback' => 'esc_url_raw', ), array( 'name' => '_affiliate_links_generate_link', 'title' => 'Generate Affiliate Link', 'description' => 'More information about the link generator here.', 'global_name' => 'generate_link', 'type' => 'checkbox', 'allow_html' => true, ), array( 'name' => '_affiliate_links_target_two', 'title' => 'Second Target URL', 'description' => 'Enter second Target URL', 'type' => 'url', 'sanitize_callback' => 'esc_url_raw', ), array( 'name' => '_affiliate_links_description', 'title' => 'Link Description', 'description' => 'Describe your link', 'type' => 'text', ), array( 'name' => '_affiliate_links_iframe', 'global_name' => 'iframe', 'title' => 'Mask Link', 'type' => 'checkbox', 'description' => 'Open link in iframe', ), array( 'name' => '_affiliate_links_nofollow', 'global_name' => 'nofollow', 'title' => 'Nofollow Link', 'type' => 'checkbox', 'description' => 'Add "X-Robots-Tag: noindex, nofollow" to HTTP headers', ), array( 'name' => '_affiliate_links_redirect', 'global_name' => 'redirect', 'title' => 'Redirect Type', 'type' => 'radio', 'description' => 'Set redirection HTTP status code', 'values' => array( '301' => '301 Moved Permanently', '302' => '302 Found', '307' => '307 Temporary Redirect', ), ), ); public $admin_grid_fields = array( '_affiliate_links_target', '_affiliate_links_description', '_affiliate_links_redirect', ); /** * Hook into the appropriate actions when the class is constructed. */ public function __construct() { // Add metabox actions. add_action( 'load-post.php', array( $this, 'init_metabox' ) ); add_action( 'load-post-new.php', array( $this, 'init_metabox' ) ); // Add custom field values to admin grid columns. add_filter( 'manage_posts_columns', array( $this, 'columns_head' ) ); add_action( 'manage_posts_custom_column', array( $this, 'columns_content' ), 10, 2 ); add_action( 'restrict_manage_posts', array( $this, 'restrict_links_by_cat' ) ); // Add custom styling. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); // Remove the Yoast SEO columns add_action( 'manage_edit-' . Affiliate_Links::$post_type . '_columns', array( $this, 'hide_yoast_columns' ) ); // remove unnecessary screen options add_action( 'current_screen', array( $this, 'get_screen_options' ) ); // remove view mode screen options add_filter( 'view_mode_post_types', array( $this, 'remove_view_mode', ) ); } /** * Remove unused Yoast columns */ public function hide_yoast_columns( $columns ) { if ( ! current_user_can( 'manage_options' ) ) { return $columns; } unset( $columns['wpseo-score'] ); unset( $columns['wpseo-title'] ); unset( $columns['wpseo-metadesc'] ); unset( $columns['wpseo-focuskw'] ); return $columns; } /** * Add admin css file. */ public function enqueue_scripts( $hook ) { global $post; if ( $hook != 'post.php' and $hook != 'post-new.php' and $hook != 'affiliate-links_page_affiliate_links' ) { return; } // css wp_register_style( 'affiliate-links-css', AFFILIATE_LINKS_PLUGIN_URL . 'admin/css/affiliate-links-admin.css', false, '1.6' ); wp_enqueue_style( 'affiliate-links-css' ); // js wp_register_script( 'affiliate-links-js', AFFILIATE_LINKS_PLUGIN_URL . 'admin/js/affiliate-links-admin.js', array( 'jquery' ), '1.6', true ); if ( $post ) { wp_localize_script( 'affiliate-links-js', 'afLinksAdmin', array( 'linkId' => $post->ID, 'permalink' => get_the_permalink( $post->ID ), 'shortcode' => 'af_link', ) ); } wp_enqueue_script( 'affiliate-links-js', false, array( 'jquery' ), '1.6', true ); } public function get_screen_options( $screen ) { if ( 'edit-affiliate-links' !== $screen->id ) { add_filter( "manage_{$screen->id}_columns", array( $this, 'manage_screen_options', ) ); } } public function manage_screen_options( $columns ) { if ( isset( $columns['hits'] ) ) { return array(); } return $columns; } /** * Modify admin grid column headers. */ public function columns_head( $defaults ) { global $typenow; if ( $typenow == Affiliate_Links::$post_type ) { $defaults['permalink'] = __( 'Link URL', 'affiliate-links' ); foreach ( $this->get_fields() as $field ) { if ( in_array( $field['name'], $this->admin_grid_fields ) ) { $defaults[ $field['name'] ] = $field['title']; } } $defaults['_affiliate_links_stat'] = __( 'Hits', 'affiliate-links' ); } return $defaults; } /** * Modify admin grid columns. */ public function columns_content( $column_name, $post_id ) { switch ( $column_name ) { case 'permalink': echo esc_html( get_the_permalink( $post_id ) ); break; case '_affiliate_links_target': echo esc_html( get_post_meta( $post_id, '_affiliate_links_target', true ) ); break; case '_affiliate_links_stat': echo esc_html( $this->get_link_hits( $post_id ) ); break; case '_affiliate_links_description': echo esc_html( get_post_meta( $post_id, '_affiliate_links_description', true ) ); break; case '_affiliate_links_redirect': echo esc_html( get_post_meta( $post_id, '_affiliate_links_redirect', true ) ); break; case '_affiliate_links_nofollow': echo esc_html( get_post_meta( $post_id, '_affiliate_links_nofollow', true ) ); break; } } /** * Add link category filter to admin grid. */ function restrict_links_by_cat() { global $typenow; global $wp_query; if ( $typenow == Affiliate_Links::$post_type ) { if ( ! empty( $wp_query->query[ Affiliate_Links::$taxonomy ] ) ) { $selected = $wp_query->query[ Affiliate_Links::$taxonomy ]; } else { $selected = 0; } wp_dropdown_categories( array( 'show_option_all' => __( 'All Categories', 'affiliate-links' ), 'taxonomy' => Affiliate_Links::$taxonomy, 'value_field' => 'slug', 'name' => Affiliate_Links::$taxonomy, 'orderby' => 'name', 'selected' => $selected, 'hierarchical' => true, 'depth' => 3, 'show_count' => true, 'hide_empty' => true, 'hide_if_empty' => 1, ) ); } } /** * Add appropriate actions. */ public function init_metabox() { add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ), 0 ); add_action( 'save_post', array( $this, 'save' ) ); } /** * Adds the meta box container. */ public function add_meta_box( $post_type ) { $post_types = array( Affiliate_Links::$post_type ); if ( in_array( $post_type, $post_types ) ) { add_meta_box( 'affiliate_links_settings', __( 'Link Settings', 'affiliate-links' ), array( $this, 'render_metabox_content' ), $post_type, 'normal', 'high' ); add_meta_box( 'affiliate_links_embed', __( 'Link Embedding', 'affiliate-links' ), array( $this, 'render_metabox_embed' ), $post_type, 'normal', 'high' ); } } public function is_form_skip_save( $post_id ) { return ( ! isset( $_POST['affiliate_links_custom_box_nonce'] ) ) || ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['affiliate_links_custom_box_nonce'] ) ), 'affiliate_links_custom_box' ) ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( ! current_user_can( 'edit_post', $post_id ) ); } /** * Save metabox. */ public function save($post_id) { // Check if we should skip saving if ($this->is_form_skip_save($post_id)) { return $post_id; } // Update all meta fields from standard and embedded fields $all_fields = array_merge($this->get_fields(), $this->get_embedded_metabox_fields()); foreach ($all_fields as $field) { update_post_meta($post_id, $field['name'], $this->get_sanitized_value($field)); } // Reset stat count if it's set // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified in is_form_skip_save() if (isset($_POST['_affiliate_links_stat'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified in is_form_skip_save() $count = (int) sanitize_key($_POST['_affiliate_links_stat']); update_post_meta($post_id, '_affiliate_links_stat', $count); } // Generate affiliate URL if the option is selected // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified in is_form_skip_save() if (!empty($_POST['_affiliate_links_generate_link'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Nonce already verified in is_form_skip_save() $landing_page_url = esc_url_raw(wp_unslash($_POST['_affiliate_links_target'] ?? '')); if ($landing_page_url) { $affiliate_url = $this->generate_affiliate_url($landing_page_url); if ($affiliate_url) { update_post_meta($post_id, '_affiliate_links_target', $affiliate_url); } } } } private function generate_affiliate_url($landing_page_url) { // Get the API key from the plugin settings $api_key = Affiliate_Links_Settings::get_option( 'affiliate_api_key' ); if (!$api_key || !$landing_page_url) { return false; } $api_endpoint = "https://api.wecantrack.com/api/v1/affiliate/generate-link"; $payload = json_encode(array('landing_page_url' => $landing_page_url)); // Send the API key in a header rather than the URL query string so it does // not leak into server/proxy access logs. The wecantrack API checks // X-API-Key first and only falls back to the api_key param, so this is // backward compatible. $response = wp_remote_post($api_endpoint, array( 'method' => 'POST', 'body' => $payload, 'headers' => array( 'Content-Type' => 'application/json', 'X-API-Key' => $api_key, ), )); // Check for errors if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['links'][0]['affiliate_url'])) { return esc_url_raw($data['links'][0]['affiliate_url']); } return false; } public function get_sanitized_value( $field ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Called from save() where nonce is already verified if ( ! isset( $_POST[ $field['name'] ] ) ) { return ''; } $sanitize_callback = ( isset( $field['sanitize_callback'] ) ) ? $field['sanitize_callback'] : 'sanitize_text_field'; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Called from save() where nonce is already verified, sanitization happens via callback return call_user_func( $sanitize_callback, wp_unslash( $_POST[ $field['name'] ] ) ); } public function get_fields() { return apply_filters( 'af_links_get_fields', $this->fields ); } /** * Render metabox content. */ public function render_metabox_content( $post ) { global $post_type_object; echo '
' . esc_html__( 'Before you can use this link you need to publish it.', 'affiliate-links' ) . '
'; } } } /** * Generate settings field html. */ public function render_field( $field, $value ) { $func_name = 'render_' . $field['type'] . '_field'; if ( method_exists( __CLASS__, $func_name ) ) { call_user_func_array( array( $this, $func_name ), array( 'field' => $field, 'value' => $value, ) ); } else { call_user_func_array( array( $this, 'render_text_field' ), array( 'field' => $field, 'value' => $value, ) ); } } /** * Generate text input field. */ public function render_text_field( $field, $value ) { $name = esc_attr( $field['name'] ); $title = esc_attr( $field['title'] ); $desc = esc_html( $field['description'] ); $type = esc_attr( $field['type'] ); ?>