v1.6.0
This commit is contained in:
parent
83d629568a
commit
98b1cf0f3b
17 changed files with 2968 additions and 1313 deletions
|
|
@ -8,9 +8,11 @@
|
|||
namespace MediaRightsAudit\Admin;
|
||||
|
||||
/**
|
||||
* Registers and renders the plugin Settings page.
|
||||
* Registers and renders the plugin Settings page with tab navigation.
|
||||
*
|
||||
* Settings are stored in a single option array: robotstxt_mediaaudit_settings.
|
||||
* All tabs share a single option (robotstxt_mediaaudit_settings). A hidden
|
||||
* _tab sentinel in each form submission tells sanitize() which fields to
|
||||
* process, so saving one tab never clears another tab's data.
|
||||
*/
|
||||
class Settings {
|
||||
|
||||
|
|
@ -24,6 +26,198 @@ class Settings {
|
|||
*/
|
||||
const OPTION_NAME = 'robotstxt_mediaaudit_settings';
|
||||
|
||||
/**
|
||||
* Default alert hostnames seeded on first activation.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
private const DEFAULT_FILTER_INCLUDE = array(
|
||||
'123rf.com',
|
||||
'afp.com',
|
||||
'alamy.com',
|
||||
'arcangel.com',
|
||||
'artgrid.io',
|
||||
'audioblocks.com',
|
||||
'auroraphotos.com',
|
||||
'backgridusa.com',
|
||||
'bigstockphoto.com',
|
||||
'blendimages.com',
|
||||
'bridgemanimages.com',
|
||||
'canstockphoto.com',
|
||||
'cavanimages.com',
|
||||
'creativemarket.com',
|
||||
'crestock.com',
|
||||
'cultura-rm.com',
|
||||
'depositphotos.com',
|
||||
'designbundles.net',
|
||||
'diomedia.com',
|
||||
'dreamstime.com',
|
||||
'elements.envato.com',
|
||||
'epa.eu',
|
||||
'filmsupply.com',
|
||||
'flaticon.com',
|
||||
'fotolia.com',
|
||||
'freepik.com',
|
||||
'gallerystock.com',
|
||||
'gettyimages.com',
|
||||
'goffphotos.com',
|
||||
'granger.com',
|
||||
'graphicstock.com',
|
||||
'imago-images.de',
|
||||
'istockphoto.com',
|
||||
'maryevans.com',
|
||||
'mindenpictures.com',
|
||||
'mintimages.com',
|
||||
'mostphotos.com',
|
||||
'naturepl.com',
|
||||
'newsroom.ap.org',
|
||||
'nhpa.co.uk',
|
||||
'offset.shutterstock.com',
|
||||
'pacificpressagency.com',
|
||||
'panthermedia.net',
|
||||
'photoshot.com',
|
||||
'pictures.reuters.com',
|
||||
'pixtastock.com',
|
||||
'plainpicture.com',
|
||||
'pond5.com',
|
||||
'robertharding.com',
|
||||
'sciencephoto.com',
|
||||
'shutterstock.com',
|
||||
'splashnews.com',
|
||||
'stock.adobe.com',
|
||||
'stocksy.com',
|
||||
'storyblocks.com',
|
||||
'trevillion.com',
|
||||
'vecteezy.com',
|
||||
'videoblocks.com',
|
||||
'wenn.com',
|
||||
'yayimages.com',
|
||||
'zumapress.com',
|
||||
);
|
||||
|
||||
/**
|
||||
* Default ignored hostnames seeded on first activation.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
private const DEFAULT_FILTER_EXCLUDE = array(
|
||||
'9gag.com',
|
||||
'artstation.com',
|
||||
'bandcamp.com',
|
||||
'behance.net',
|
||||
'blogger.com',
|
||||
'bsky.app',
|
||||
'dailymotion.com',
|
||||
'deviantart.com',
|
||||
'discord.com',
|
||||
'douyin.com',
|
||||
'dribbble.com',
|
||||
'facebook.com',
|
||||
'fb.com',
|
||||
'flickr.com',
|
||||
'gfycat.com',
|
||||
'giphy.com',
|
||||
'imgur.com',
|
||||
'instagram.com',
|
||||
'linkedin.com',
|
||||
'livejournal.com',
|
||||
'loom.com',
|
||||
'mastodon.social',
|
||||
'medium.com',
|
||||
'notion.site',
|
||||
'onlyfans.com',
|
||||
'patreon.com',
|
||||
'periscope.tv',
|
||||
'pinterest.com',
|
||||
'qq.com',
|
||||
'reddit.com',
|
||||
'snapchat.com',
|
||||
'soundcloud.com',
|
||||
'spotify.com',
|
||||
'squarespace.com',
|
||||
'streamable.com',
|
||||
'substack.com',
|
||||
'telegram.org',
|
||||
'tenor.com',
|
||||
'threads.net',
|
||||
'tiktok.com',
|
||||
'tumblr.com',
|
||||
'twitch.tv',
|
||||
'twitter.com',
|
||||
'vimeo.com',
|
||||
'vk.com',
|
||||
'weebly.com',
|
||||
'weibo.com',
|
||||
'whatsapp.com',
|
||||
'wix.com',
|
||||
'wordpress.com',
|
||||
'wordpress.org',
|
||||
'x.com',
|
||||
'xiaohongshu.com',
|
||||
'youtube.com',
|
||||
'zhihu.com',
|
||||
);
|
||||
|
||||
/**
|
||||
* Valid tab keys (order defines display order).
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
private const TAB_KEYS = array( 'general', 'api', 'filters', 'external' );
|
||||
|
||||
/**
|
||||
* Seeds the default filter lists into the option when they have not been set yet.
|
||||
*
|
||||
* Called once on plugin activation. Does nothing if the keys already exist,
|
||||
* so existing user customisations are never overwritten.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_seed_defaults(): void {
|
||||
$raw = get_option( self::OPTION_NAME, array() );
|
||||
$options = is_array( $raw ) ? $raw : array();
|
||||
$changed = false;
|
||||
|
||||
if ( ! array_key_exists( 'filter_include', $options ) ) {
|
||||
$options['filter_include'] = self::DEFAULT_FILTER_INCLUDE;
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'filter_exclude', $options ) ) {
|
||||
$options['filter_exclude'] = self::DEFAULT_FILTER_EXCLUDE;
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if ( $changed ) {
|
||||
update_option( self::OPTION_NAME, $options );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns translated tab labels keyed by tab ID.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function get_tabs(): array {
|
||||
return array(
|
||||
'general' => __( 'General', 'robotstxt-mediaaudit' ),
|
||||
'api' => __( 'API Credentials', 'robotstxt-mediaaudit' ),
|
||||
'filters' => __( 'Filters', 'robotstxt-mediaaudit' ),
|
||||
'external' => __( 'External Scanning', 'robotstxt-mediaaudit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active tab key derived from the current request.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_active_tab(): string {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$raw = isset( $_GET['tab'] ) && is_string( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'general';
|
||||
return in_array( $raw, self::TAB_KEYS, true ) ? $raw : 'general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all settings, sections, and fields via the WordPress Settings API.
|
||||
*
|
||||
|
|
@ -42,33 +236,35 @@ class Settings {
|
|||
)
|
||||
);
|
||||
|
||||
// --- General tab ---
|
||||
add_settings_section(
|
||||
'mra_general',
|
||||
__( 'General', 'robotstxt-mediaaudit' ),
|
||||
'__return_false',
|
||||
'robotstxt-mediaaudit-settings'
|
||||
'',
|
||||
array( $this, 'section_general_status' ),
|
||||
'mra-settings-general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'delete_on_uninstall',
|
||||
__( 'Delete data on uninstall', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_delete_on_uninstall' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-general',
|
||||
'mra_general'
|
||||
);
|
||||
|
||||
// --- API Credentials tab ---
|
||||
add_settings_section(
|
||||
'mra_api_credentials',
|
||||
__( 'API Credentials', 'robotstxt-mediaaudit' ),
|
||||
'',
|
||||
'__return_false',
|
||||
'robotstxt-mediaaudit-settings'
|
||||
'mra-settings-api'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'google_vision_api_key',
|
||||
__( 'Google Cloud Vision API Key', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_google_vision_api_key' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-api',
|
||||
'mra_api_credentials'
|
||||
);
|
||||
|
||||
|
|
@ -76,7 +272,7 @@ class Settings {
|
|||
'tineye_api_key',
|
||||
__( 'TinEye API Key', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_tineye_api_key' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-api',
|
||||
'mra_api_credentials'
|
||||
);
|
||||
|
||||
|
|
@ -84,7 +280,7 @@ class Settings {
|
|||
'picdefense_user_id',
|
||||
__( 'PicDefense User ID', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_picdefense_user_id' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-api',
|
||||
'mra_api_credentials'
|
||||
);
|
||||
|
||||
|
|
@ -92,22 +288,54 @@ class Settings {
|
|||
'picdefense_api_key',
|
||||
__( 'PicDefense API Key', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_picdefense_api_key' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-api',
|
||||
'mra_api_credentials'
|
||||
);
|
||||
|
||||
// --- Filters tab ---
|
||||
add_settings_section(
|
||||
'mra_filters_include',
|
||||
__( 'Alert Hostnames', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'section_filters_include' ),
|
||||
'mra-settings-filters'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'filter_include',
|
||||
__( 'Hostnames', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_filter_include' ),
|
||||
'mra-settings-filters',
|
||||
'mra_filters_include'
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'mra_filters_exclude',
|
||||
__( 'Ignored Hostnames', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'section_filters_exclude' ),
|
||||
'mra-settings-filters'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'filter_exclude',
|
||||
__( 'Hostnames', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_filter_exclude' ),
|
||||
'mra-settings-filters',
|
||||
'mra_filters_exclude'
|
||||
);
|
||||
|
||||
// --- External Scanning tab ---
|
||||
add_settings_section(
|
||||
'mra_external_scanning',
|
||||
__( 'External Scanning', 'robotstxt-mediaaudit' ),
|
||||
'',
|
||||
'__return_false',
|
||||
'robotstxt-mediaaudit-settings'
|
||||
'mra-settings-external'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'external_batch_size',
|
||||
__( 'Batch Size', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_external_batch_size' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-external',
|
||||
'mra_external_scanning'
|
||||
);
|
||||
|
||||
|
|
@ -115,7 +343,7 @@ class Settings {
|
|||
'rate_limit_per_minute',
|
||||
__( 'Rate Limit (requests/min)', 'robotstxt-mediaaudit' ),
|
||||
array( $this, 'field_rate_limit_per_minute' ),
|
||||
'robotstxt-mediaaudit-settings',
|
||||
'mra-settings-external',
|
||||
'mra_external_scanning'
|
||||
);
|
||||
}
|
||||
|
|
@ -123,42 +351,214 @@ class Settings {
|
|||
/**
|
||||
* Sanitises the settings array on save.
|
||||
*
|
||||
* Reads the submitted _tab sentinel to determine which fields are present
|
||||
* and merges them onto the existing stored values, leaving all other tabs
|
||||
* untouched.
|
||||
*
|
||||
* @param mixed $input Raw POST input.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function sanitize( $input ): array {
|
||||
$output = array();
|
||||
$raw = get_option( self::OPTION_NAME, array() );
|
||||
$current = is_array( $raw ) ? $raw : array();
|
||||
$output = $current;
|
||||
|
||||
if ( ! is_array( $input ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$output['delete_on_uninstall'] = ! empty( $input['delete_on_uninstall'] );
|
||||
$tab_raw = $input['_tab'] ?? '';
|
||||
$tab = is_string( $tab_raw ) ? sanitize_key( $tab_raw ) : '';
|
||||
|
||||
$key_val = $input['google_vision_api_key'] ?? null;
|
||||
$output['google_vision_api_key'] = is_string( $key_val ) ? sanitize_text_field( $key_val ) : '';
|
||||
if ( 'general' === $tab ) {
|
||||
$output['delete_on_uninstall'] = ! empty( $input['delete_on_uninstall'] );
|
||||
}
|
||||
|
||||
$tineye_val = $input['tineye_api_key'] ?? null;
|
||||
$output['tineye_api_key'] = is_string( $tineye_val ) ? sanitize_text_field( $tineye_val ) : '';
|
||||
if ( 'api' === $tab ) {
|
||||
$key_val = $input['google_vision_api_key'] ?? null;
|
||||
$output['google_vision_api_key'] = is_string( $key_val ) ? sanitize_text_field( $key_val ) : '';
|
||||
|
||||
$pd_uid_val = $input['picdefense_user_id'] ?? null;
|
||||
$output['picdefense_user_id'] = is_string( $pd_uid_val ) ? sanitize_text_field( $pd_uid_val ) : '';
|
||||
$tineye_val = $input['tineye_api_key'] ?? null;
|
||||
$output['tineye_api_key'] = is_string( $tineye_val ) ? sanitize_text_field( $tineye_val ) : '';
|
||||
|
||||
$pd_key_val = $input['picdefense_api_key'] ?? null;
|
||||
$output['picdefense_api_key'] = is_string( $pd_key_val ) ? sanitize_text_field( $pd_key_val ) : '';
|
||||
$pd_uid_val = $input['picdefense_user_id'] ?? null;
|
||||
$output['picdefense_user_id'] = is_string( $pd_uid_val ) ? sanitize_text_field( $pd_uid_val ) : '';
|
||||
|
||||
$batch_val = $input['external_batch_size'] ?? null;
|
||||
$batch = is_numeric( $batch_val ) ? (int) $batch_val : 10;
|
||||
$output['external_batch_size'] = max( 1, min( 100, $batch ) );
|
||||
$pd_key_val = $input['picdefense_api_key'] ?? null;
|
||||
$output['picdefense_api_key'] = is_string( $pd_key_val ) ? sanitize_text_field( $pd_key_val ) : '';
|
||||
}
|
||||
|
||||
$rl_val = $input['rate_limit_per_minute'] ?? null;
|
||||
$rl = is_numeric( $rl_val ) ? (int) $rl_val : 10;
|
||||
$output['rate_limit_per_minute'] = max( 1, min( 60, $rl ) );
|
||||
if ( 'filters' === $tab ) {
|
||||
$output['filter_include'] = $this->sanitize_hostname_list( $input['filter_include'] ?? '' );
|
||||
$output['filter_exclude'] = $this->sanitize_hostname_list( $input['filter_exclude'] ?? '' );
|
||||
|
||||
$overlap = array_intersect( $output['filter_include'], $output['filter_exclude'] );
|
||||
if ( ! empty( $overlap ) ) {
|
||||
$output['filter_exclude'] = array_values( array_diff( $output['filter_exclude'], $overlap ) );
|
||||
add_settings_error(
|
||||
self::OPTION_NAME,
|
||||
'mra_filter_overlap',
|
||||
sprintf(
|
||||
/* translators: %s: comma-separated list of hostnames */
|
||||
__( 'The following hostnames were removed from the exclusion list because they already appear in the alert list: %s', 'robotstxt-mediaaudit' ),
|
||||
esc_html( implode( ', ', $overlap ) )
|
||||
),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'external' === $tab ) {
|
||||
$batch_val = $input['external_batch_size'] ?? null;
|
||||
$batch = is_numeric( $batch_val ) ? (int) $batch_val : 10;
|
||||
$output['external_batch_size'] = max( 1, min( 100, $batch ) );
|
||||
|
||||
$rl_val = $input['rate_limit_per_minute'] ?? null;
|
||||
$rl = is_numeric( $rl_val ) ? (int) $rl_val : 10;
|
||||
$output['rate_limit_per_minute'] = max( 1, min( 60, $rl ) );
|
||||
}
|
||||
|
||||
unset( $output['_tab'] );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitises a newline-separated list of hostnames into a deduplicated array.
|
||||
*
|
||||
* Accepts plain hostnames (example.com) and explicit wildcard prefixes
|
||||
* (*.example.com). A plain hostname implicitly covers all its subdomains
|
||||
* at match time.
|
||||
*
|
||||
* @param mixed $raw Raw textarea value.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function sanitize_hostname_list( mixed $raw ): array {
|
||||
if ( ! is_string( $raw ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$split = preg_split( '/\r?\n/', $raw );
|
||||
$lines = is_array( $split ) ? $split : array();
|
||||
$result = array();
|
||||
|
||||
foreach ( $lines as $line ) {
|
||||
$h = strtolower( sanitize_text_field( trim( $line ) ) );
|
||||
if ( '' === $h ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Preserve wildcard prefix before stripping URL parts.
|
||||
$prefix = '';
|
||||
if ( str_starts_with( $h, '*.' ) ) {
|
||||
$prefix = '*.';
|
||||
$h = substr( $h, 2 );
|
||||
}
|
||||
|
||||
// Strip scheme (http://, https://, etc.).
|
||||
$stripped = preg_replace( '/^[a-z][a-z0-9+\-.]*:\/\//', '', $h );
|
||||
if ( is_string( $stripped ) ) {
|
||||
$h = $stripped;
|
||||
}
|
||||
|
||||
// Strip path, query string, and fragment — keep only host[:port].
|
||||
$h = substr( $h, 0, strcspn( $h, '/?#' ) );
|
||||
|
||||
// Strip port number.
|
||||
$no_port = preg_replace( '/:\d+$/', '', $h );
|
||||
if ( is_string( $no_port ) ) {
|
||||
$h = $no_port;
|
||||
}
|
||||
|
||||
$h = trim( $h, '. ' );
|
||||
|
||||
// Strip www. prefix — example.com already covers www.example.com at match time.
|
||||
if ( str_starts_with( $h, 'www.' ) ) {
|
||||
$h = substr( $h, 4 );
|
||||
}
|
||||
|
||||
$h = $prefix . $h;
|
||||
|
||||
if ( preg_match( '/^(\*\.)?[a-z0-9][a-z0-9\-]*(\.[a-z0-9][a-z0-9\-]*)+$/', $h ) ) {
|
||||
$result[] = $h;
|
||||
}
|
||||
}
|
||||
|
||||
$result = array_values( array_unique( $result ) );
|
||||
sort( $result );
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the API credentials status block shown at the top of the General tab.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function section_general_status(): void {
|
||||
$gv_ok = $this->is_google_vision_configured();
|
||||
$te_ok = $this->is_tineye_configured();
|
||||
$pd_ok = $this->is_picdefense_configured();
|
||||
?>
|
||||
<div class="mra-provider-status">
|
||||
<strong><?php esc_html_e( 'API Credentials Status', 'robotstxt-mediaaudit' ); ?></strong>
|
||||
<ul>
|
||||
<li>
|
||||
<?php if ( $gv_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
<?php esc_html_e( 'Google Cloud Vision', 'robotstxt-mediaaudit' ); ?>
|
||||
</li>
|
||||
<li>
|
||||
<?php if ( $te_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
<?php esc_html_e( 'TinEye', 'robotstxt-mediaaudit' ); ?>
|
||||
</li>
|
||||
<li>
|
||||
<?php if ( $pd_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
PicDefense
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the description for the "Alert Hostnames" filter section.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function section_filters_include(): void {
|
||||
?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Matches from these hostnames are flagged as critical copyright alerts. Entering example.com also covers all its subdomains.', 'robotstxt-mediaaudit' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the description for the "Ignored Hostnames" filter section.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function section_filters_exclude(): void {
|
||||
?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Matches from these hostnames are silently ignored and not counted as potential copyright issues. Entering example.com also covers all its subdomains. A hostname already present in the alert list cannot be added here.', 'robotstxt-mediaaudit' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the "Delete data on uninstall" checkbox field.
|
||||
*
|
||||
|
|
@ -339,6 +739,68 @@ class Settings {
|
|||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the alert hostname filter textarea.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function field_filter_include(): void {
|
||||
$options = (array) get_option( self::OPTION_NAME, array() );
|
||||
$raw = $options['filter_include'] ?? array();
|
||||
$strings = array();
|
||||
if ( is_array( $raw ) ) {
|
||||
foreach ( $raw as $item ) {
|
||||
if ( is_string( $item ) ) {
|
||||
$strings[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$value = implode( "\n", $strings );
|
||||
?>
|
||||
<textarea
|
||||
id="filter_include"
|
||||
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[filter_include]"
|
||||
rows="8"
|
||||
class="large-text code"
|
||||
placeholder="example.com"
|
||||
><?php echo esc_textarea( $value ); ?></textarea>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'One hostname per line. example.com covers all its subdomains. Explicit wildcard: *.example.com.', 'robotstxt-mediaaudit' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the ignored hostname filter textarea.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function field_filter_exclude(): void {
|
||||
$options = (array) get_option( self::OPTION_NAME, array() );
|
||||
$raw = $options['filter_exclude'] ?? array();
|
||||
$strings = array();
|
||||
if ( is_array( $raw ) ) {
|
||||
foreach ( $raw as $item ) {
|
||||
if ( is_string( $item ) ) {
|
||||
$strings[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$value = implode( "\n", $strings );
|
||||
?>
|
||||
<textarea
|
||||
id="filter_exclude"
|
||||
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[filter_exclude]"
|
||||
rows="8"
|
||||
class="large-text code"
|
||||
placeholder="cdn.example.com"
|
||||
><?php echo esc_textarea( $value ); ?></textarea>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'One hostname per line. example.com covers all its subdomains. Explicit wildcard: *.example.com.', 'robotstxt-mediaaudit' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the external batch size field.
|
||||
*
|
||||
|
|
@ -436,49 +898,31 @@ class Settings {
|
|||
wp_die( esc_html__( 'You do not have permission to access this page.', 'robotstxt-mediaaudit' ) );
|
||||
}
|
||||
|
||||
$gv_ok = $this->is_google_vision_configured();
|
||||
$te_ok = $this->is_tineye_configured();
|
||||
$pd_ok = $this->is_picdefense_configured();
|
||||
$tab = $this->get_active_tab();
|
||||
$tabs = $this->get_tabs();
|
||||
$page_url = admin_url( 'admin.php?page=robotstxt-mediaaudit-settings' );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
|
||||
<div class="mra-provider-status">
|
||||
<strong><?php esc_html_e( 'API Credentials Status', 'robotstxt-mediaaudit' ); ?></strong>
|
||||
<ul>
|
||||
<li>
|
||||
<?php if ( $gv_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
<?php esc_html_e( 'Google Cloud Vision', 'robotstxt-mediaaudit' ); ?>
|
||||
</li>
|
||||
<li>
|
||||
<?php if ( $te_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
<?php esc_html_e( 'TinEye', 'robotstxt-mediaaudit' ); ?>
|
||||
</li>
|
||||
<li>
|
||||
<?php if ( $pd_ok ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color:green;"></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-warning" style="color:orange;"></span>
|
||||
<?php endif; ?>
|
||||
PicDefense
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<nav class="nav-tab-wrapper">
|
||||
<?php foreach ( $tabs as $key => $label ) : ?>
|
||||
<a
|
||||
href="<?php echo esc_url( add_query_arg( 'tab', $key, $page_url ) ); ?>"
|
||||
class="nav-tab<?php echo ( $tab === $key ) ? ' nav-tab-active' : ''; ?>"
|
||||
><?php echo esc_html( $label ); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields( self::OPTION_GROUP );
|
||||
do_settings_sections( 'robotstxt-mediaaudit-settings' );
|
||||
submit_button();
|
||||
?>
|
||||
<?php settings_fields( self::OPTION_GROUP ); ?>
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[_tab]"
|
||||
value="<?php echo esc_attr( $tab ); ?>"
|
||||
/>
|
||||
<?php do_settings_sections( 'mra-settings-' . $tab ); ?>
|
||||
<?php submit_button(); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
|||
Loading…
Reference in a new issue