Current File : /home/honehdyv/readbtooom.com/wp-content/plugins/autodescription/inc/classes/admin-pages.class.php
<?php
/**
 * @package The_SEO_Framework\Classes\Facade\Admin_Pages
 * @subpackage The_SEO_Framework\Admin\Settings
 */

namespace The_SEO_Framework;

\defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;

/**
 * The SEO Framework plugin
 * Copyright (C) 2015 - 2023 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Class The_SEO_Framework\Site_Options
 *
 * Renders admin pages content for this plugin.
 *
 * @since 2.8.0
 */
class Admin_Pages extends Generate_Ldjson {

	/**
	 * @since 2.7.0
	 * @var string $seo_settings_page_hook The page hook_suffix added via WP add_menu_page()
	 */
	public $seo_settings_page_hook;

	/**
	 * Adds menu links under "settings" in the wp-admin dashboard
	 *
	 * @since 2.2.2
	 * @since 2.9.2 Added static cache so the method can only run once.
	 *
	 * @return void Early if method is already called.
	 */
	public function add_menu_link() {

		if ( has_run( __METHOD__ ) ) return;

		$issue_count = $this->get_admin_issue_count();

		if ( $issue_count )
			$issue_badge = $this->get_admin_menu_issue_badge( $issue_count );

		/**
		 * @since 4.2.8
		 * @param array $args The menu arguments. All indexes must be maintained.
		 */
		$menu = \apply_filters(
			'the_seo_framework_top_menu_args',
			[
				'page_title' => \esc_html__( 'SEO Settings', 'autodescription' ),
				'menu_title' => \esc_html__( 'SEO', 'autodescription' ) . ( $issue_badge ?? '' ),
				'capability' => $this->get_settings_capability(),
				'menu_slug'  => $this->seo_settings_page_slug,
				'callback'   => [ $this, '_output_settings_wrap' ],
				'icon'       => 'dashicons-search',
				'position'   => '90.9001',
			]
		);

		$this->seo_settings_page_hook = \add_menu_page(
			$menu['page_title'],
			$menu['menu_title'],
			$menu['capability'],
			$menu['menu_slug'],
			$menu['callback'],
			$menu['icon'],
			$menu['position']
		);

		/**
		 * Simply copy the previous, but rename the submenu entry.
		 * The function add_submenu_page() takes care of the duplications.
		 */
		\add_submenu_page(
			$menu['menu_slug'],
			$menu['page_title'],
			$menu['page_title'],
			$menu['capability'],
			$menu['menu_slug'],
			$menu['callback']
		);

		// Enqueue scripts
		\add_action( "admin_print_scripts-{$this->seo_settings_page_hook}", [ $this, '_init_admin_scripts' ], 11 );
		\add_action( "load-{$this->seo_settings_page_hook}", [ $this, '_register_seo_settings_meta_boxes' ] );
	}

	/**
	 * Returns the number of issues registered.
	 * Always returns 0 when the settings are headless.
	 *
	 * @since 4.2.8
	 *
	 * @return int The registered issue count.
	 */
	public function get_admin_issue_count() {

		if ( $this->is_headless['settings'] ) return 0;

		/**
		 * @since 4.2.8
		 * @param int The issue count. Don't overwrite, but increment it!
		 */
		return memo() ?? memo( \absint( \apply_filters( 'the_seo_framework_top_menu_issue_count', 0 ) ) );
	}

	/**
	 * Returns formatted text for the notice count to be displayed in the admin menu as a number.
	 *
	 * @since 4.2.8
	 *
	 * @param int $issue_count The issue count.
	 * @return string The issue count badge.
	 */
	public function get_admin_menu_issue_badge( $issue_count ) {

		$notice_i18n = \number_format_i18n( $issue_count );

		return ' ' . sprintf(
			'<span class="tsf-menu-issue menu-counter count-%d"><span class=tsf-menu-issue-text aria-hidden=true>%s</span><span class=screen-reader-text>%s</span></span>',
			$issue_count,
			$notice_i18n,
			sprintf(
				/* translators: %s: number of issues waiting */
				\_n( '%s issue waiting', '%s issues waiting', $issue_count, 'autodescription' ),
				$notice_i18n
			)
		);
	}

	/**
	 * Registers the meta boxes early, so WordPress recognizes them for user-settings.
	 *
	 * @since 4.0.0
	 * @see $this->_output_settings_wrap()
	 * @access private
	 */
	public function _register_seo_settings_meta_boxes() {
		Bridges\SeoSettings::_register_seo_settings_meta_boxes();
	}

	/**
	 * Outputs the SEO Settings page wrap.
	 *
	 * @since 4.0.0
	 * @access private
	 */
	public function _output_settings_wrap() {

		\add_action(
			"{$this->seo_settings_page_hook}_settings_page_boxes",
			[ Bridges\SeoSettings::class, '_output_columns' ]
		);

		Bridges\SeoSettings::_output_wrap();
	}

	/**
	 * Prepares post edit view, like outputting the fields.
	 *
	 * @since 4.0.0
	 *
	 * @param string   $post_type The current post type.
	 * @param \WP_Post $post      The Post object. Unused.
	 */
	public function _init_post_edit_view( $post_type, $post ) {

		if ( ! $this->is_post_edit() || ! $this->is_post_type_supported( $post_type ) ) return;

		/**
		 * @since 2.0.0
		 * @param bool $show_seobox Whether to show the SEO meta box.
		 */
		$show_seobox = (bool) \apply_filters( 'the_seo_framework_seobox_output', true );

		if ( $show_seobox )
			\add_action(
				'add_meta_boxes',
				[ Bridges\PostSettings::class, '_prepare_meta_box' ]
			);
	}

	/**
	 * Prepares term edit view, like outputting the fields.
	 *
	 * @since 4.0.0
	 */
	public function _init_term_edit_view() {

		if ( ! $this->is_term_edit() ) return;

		$taxonomy = $this->get_current_taxonomy();

		if ( ! $this->is_taxonomy_supported( $taxonomy ) ) return;

		/**
		 * @since 2.6.0
		 * @param int $priority The meta box term priority.
		 *                      Defaults to a high priority, this box is seen soon below the default edit inputs.
		 */
		$priority = (int) \apply_filters( 'the_seo_framework_term_metabox_priority', 0 );

		\add_action(
			"{$taxonomy}_edit_form",
			[ Bridges\TermSettings::class, '_prepare_setting_fields' ],
			$priority,
			2
		);
	}

	/**
	 * Prepares profile/user edit view, like outputting the SEO fields.
	 *
	 * @since 4.1.4
	 * @access private
	 */
	public function _init_user_edit_view() {

		if ( ! $this->is_profile_edit() ) return;

		// WordPress made a mess of this. We can't reliably get a user future-proof. Load class for all users; check there.
		// if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;

		\add_action( 'show_user_profile', [ Bridges\UserSettings::class, '_prepare_setting_fields' ], 0, 1 );
		\add_action( 'edit_user_profile', [ Bridges\UserSettings::class, '_prepare_setting_fields' ], 0, 1 );
	}

	/**
	 * Outputs notices on SEO setting changes.
	 *
	 * @since 4.0.0
	 * @since 4.0.5 This is no longer a static function.
	 * @access private
	 */
	public function _do_settings_page_notices() {

		$notice = $this->get_static_cache( 'settings_notice' );

		if ( ! $notice ) return;

		$message = '';
		$type    = '';

		switch ( $notice ) {
			case 'updated':
				$message = \__( 'SEO settings are saved, and the caches have been flushed.', 'autodescription' );
				$type    = 'updated';
				break;

			case 'unchanged':
				$message = \__( 'No SEO settings were changed, but the caches have been flushed.', 'autodescription' );
				$type    = 'info';
				break;

			case 'reset':
				$message = \__( 'SEO settings are reset, and the caches have been flushed.', 'autodescription' );
				$type    = 'warning';
				break;

			case 'error':
				$message = \__( 'An unknown error occurred saving SEO settings.', 'autodescription' );
				$type    = 'error';
				break;
		}

		$this->update_static_cache( 'settings_notice', '' );

		$message and $this->do_dismissible_notice( $message, $type ?: 'updated' );
	}

	/**
	 * Initializes and outputs various notices.
	 *
	 * @since 4.1.0
	 * @access private
	 */
	public function _output_notices() {

		if ( $this->get_static_cache( 'check_seo_plugin_conflicts' ) && \current_user_can( 'activate_plugins' ) ) {
			$this->detect_seo_plugins()
				and $this->do_dismissible_notice(
					\__( 'Multiple SEO tools have been detected. You should only use one.', 'autodescription' ),
					'warning'
				);
			$this->update_static_cache( 'check_seo_plugin_conflicts', 0 );
		}

		$this->output_dismissible_persistent_notices();
	}

	/**
	 * Generates dismissible notice.
	 * Also loads scripts and styles if out of The SEO Framework's context.
	 *
	 * @since 2.6.0
	 * @since 3.0.6 The messages are no longer auto-styled to "strong".
	 * @since 4.0.0 Added a tabindex, so keyboard navigation is possible on the "empty" dashicon.
	 * @since 4.0.3 1. Keyboard navigation is now supported on the dismiss icon.
	 *              2. The info notice type is now supported.
	 * @since 4.1.0 Now semantically wraps the content with HTML.
	 * @since 4.1.2 1. No longer invokes the script loader during AJAX-requests.
	 *              2. Now accepts empty messages, so that AJAX-invoked generators can grab a notice wrapper.
	 *              3. Added the inline parameter.
	 *              4. Now enqueues scripts in the footer, so templates won't spam the header.
	 * @TODO deprecate -- Use the more reliable and secure persistent notices registry instead...
	 *                    Then again, this allows for AJAX-generated notices.
	 * @see register_dismissible_persistent_notice()
	 *
	 * @param string $message The notice message. Expected to be escaped if $escape is false.
	 *                        When the message contains HTML, it must start with a <p> tag,
	 *                        or it will be added for you--regardless of proper semantics.
	 * @param string $type   The notice type : 'updated', 'error', 'warning', 'info'. Expected to be escaped.
	 * @param bool   $icon   Whether to add an accessibility icon.
	 * @param bool   $escape Whether to escape the whole output.
	 * @param bool   $inline Whether WordPress should be allowed to move it.
	 * @return string The dismissible error notice.
	 */
	public function generate_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true, $inline = false ) {

		if ( ! \wp_doing_ajax() ) {
			// Make sure the scripts are loaded.
			$this->init_admin_scripts();
			\The_SEO_Framework\Builders\Scripts::footer_enqueue();
		}

		switch ( $type ) {
			case 'warning':
			case 'info':
				$type = "notice-$type";
				break;
		}

		return vsprintf(
			'<div class="notice %s tsf-notice %s %s">%s%s</div>',
			[
				\esc_attr( $type ),
				( $icon ? 'tsf-show-icon' : '' ),
				( $inline ? 'inline' : '' ),
				sprintf(
					( ! $escape && 0 === strpos( $message, '<p' ) ? '%s' : '<p>%s</p>' ),
					( $escape ? \esc_html( $message ) : $message )
				),
				sprintf(
					'<a class="hide-if-no-tsf-js tsf-dismiss" href="javascript:;" title="%s"></a>',
					\esc_attr__( 'Dismiss this notice', 'default' )
				),
			]
		);
	}

	/**
	 * Echos generated dismissible notice.
	 *
	 * @since 2.7.0
	 * @since 4.1.2 Added the $inline parameter.
	 * @TODO deprecate -- Use the more reliable and secure persistent notices registry instead...
	 *                    Then again, this allows for AJAX-generated notices.
	 * @see register_dismissible_persistent_notice()
	 *
	 * @param string $message The notice message. Expected to be escaped if $escape is false.
	 * @param string $type    The notice type : 'updated', 'error', 'warning', 'info'. Expected to be escaped.
	 * @param bool   $icon    Whether to add an accessibility icon.
	 * @param bool   $escape  Whether to escape the whole output.
	 * @param bool   $inline Whether WordPress should be allowed to move it.
	 */
	public function do_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true, $inline = false ) {
		// phpcs:ignore, WordPress.Security.EscapeOutput -- use $escape
		echo $this->generate_dismissible_notice( $message, $type, $icon, $escape, $inline );
	}

	/**
	 * Echos dismissible persistent notice to screen.
	 *
	 * @since 4.1.0
	 *
	 * @param string $message    The notice message. Expected to be escaped if $escape is false.
	 * @param string $key        The unique notice key used to dismiss notices.
	 * @param array  $args       : {
	 *    'type'   => string Optional. The notification type. Default 'updated'.
	 *    'icon'   => bool   Optional. Whether to enable accessibility. Default true.
	 *    'escape' => bool   Optional. Whether to escape the $message. Default true.
	 * }
	 */
	protected function output_dismissible_persistent_notice( $message, $key, $args ) { // phpcs:ignore,VariableAnalysis.CodeAnalysis
		$this->get_view( 'notice/persistent', get_defined_vars() );
	}

	/**
	 * Outputs registered dismissible persistent notice.
	 *
	 * @since 4.1.0
	 * @since 4.1.2 Now only ignores timeout values of -1 to test against.
	 * @uses $this->output_dismissible_persistent_notice()
	 * @uses $this->count_down_persistent_notice()
	 * @global string $page_hook
	 */
	protected function output_dismissible_persistent_notices() {

		$notices    = $this->get_static_cache( 'persistent_notices', [] );
		$screenbase = \get_current_screen()->base ?? '';

		// Ideally, we don't want to output more than one on no-js. Alas, we can't anticipate the importance and order of the notices.
		foreach ( $notices as $key => $notice ) {
			$cond = $notice['conditions'];

			if (
				   ! \current_user_can( $cond['capability'] )
				|| ( $cond['user'] && $cond['user'] !== $this->get_user_id() )
				|| ( $cond['screens'] && ! \in_array( $screenbase, $cond['screens'], true ) )
				|| ( $cond['excl_screens'] && \in_array( $screenbase, $cond['excl_screens'], true ) )
			) continue;

			if ( -1 !== $cond['timeout'] && $cond['timeout'] < time() ) {
				$this->clear_persistent_notice( $key );
				continue;
			}

			// phpcs:ignore, WordPress.Security.EscapeOutput -- use $notice['args']['escape']
			$this->output_dismissible_persistent_notice( $notice['message'], $key, $notice['args'] );

			$this->count_down_persistent_notice( $key, $cond['count'] );
		}
	}

	/**
	 * Returns the SEO Bar.
	 *
	 * @since 4.0.0
	 * @uses \The_SEO_Framework\Interpreters\SEOBar::generate_bar();
	 *
	 * @param array $query : {
	 *   int    $id        : Required. The current post or term ID.
	 *   string $taxonomy  : Optional. If not set, this will interpret it as a post.
	 *   string $post_type : Optional. If not set, this will be automatically filled.
	 *                                 This parameter is ignored for taxonomies.
	 * }
	 * @return string The generated SEO bar, in HTML.
	 */
	public function get_generated_seo_bar( $query ) {
		return Interpreters\SEOBar::generate_bar( $query );
	}

	/**
	 * Outputs floating and reference title HTML elements for JavaScript.
	 *
	 * Do not use. Legacy item output for backward compatibility.
	 *
	 * @since 3.0.4
	 * @since 4.1.0 Now only outputs the legacy reference and noadditions reference.
	 * @since 4.1.2 Now prevents wp-emoji.js parsing the reference.
	 * @ignore
	 * @todo deprecate
	 */
	public function output_js_title_elements() {
		echo '<span data-ignore-me=legacy id=tsf-title-reference class="tsf-title-reference wp-exclude-emoji hidden" data-do-not-use=legacy></span>';
	}

	/**
	 * Outputs reference description HTML elements for JavaScript for a specific ID.
	 *
	 * @since 4.1.0
	 * @since 4.1.2 Now prevents wp-emoji.js parsing the references and data.
	 *
	 * @param string $id The input ID.
	 * @param array  $data The input data.
	 */
	public function output_js_title_data( $id, $data ) {
		vprintf(
			implode(
				'',
				[
					'<span id="tsf-title-reference_%1$s" class="tsf-title-reference wp-exclude-emoji hidden" data-for="%1$s"></span>',
					'<span id="tsf-title-noadditions-reference_%1$s" class="tsf-title-noadditions-reference wp-exclude-emoji hidden" data-for="%1$s"></span>',
					'<span id="tsf-title-offset_%1$s" class="tsf-title-offset wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
					'<span id="tsf-title-placeholder-additions_%1$s" class="tsf-title-placeholder-additions wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
					'<span id="tsf-title-placeholder-prefix_%1$s" class="tsf-title-placeholder-prefix wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
					'<span id="tsf-title-data_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" %2$s></span>',
				]
			),
			[
				\esc_attr( $id ),
				// phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
				Interpreters\HTML::make_data_attributes( $data ),
			]
		);
	}

	/**
	 * Outputs reference social HTML elements for JavaScript for a specific ID.
	 *
	 * @since 4.2.0
	 *
	 * @param string       $group    The social input group ID.
	 * @param array[og,tw] $settings The input settings data.
	 */
	public function output_js_social_data( $group, $settings ) {
		vprintf(
			'<span id="tsf-social-data_%1$s" class="hidden wp-exclude-emoji" data-group="%1$s" %2$s></span>',
			[
				\esc_attr( $group ),
				// phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
				Interpreters\HTML::make_data_attributes( [ 'settings' => $settings ] ),
			]
		);
	}

	/**
	 * Outputs reference description HTML elements for JavaScript.
	 *
	 * Do not use. Legacy item output for backward compatibility.
	 *
	 * @since 3.0.4
	 * @since 4.1.2 Now prevents wp-emoji.js parsing the reference.
	 * @ignore
	 * @todo deprecate
	 */
	public function output_js_description_elements() {
		echo '<span data-ignore-me=legacy id=tsf-description-reference class="tsf-description-reference wp-exclude-emoji hidden" data-do-not-use=legacy></span>';
	}

	/**
	 * Outputs reference description HTML elements for JavaScript for a specific ID.
	 *
	 * @since 4.1.0
	 * @since 4.1.2 Now prevents wp-emoji.js parsing the references and data.
	 *
	 * @param string $id   The description input ID.
	 * @param array  $data The input data.
	 */
	public function output_js_description_data( $id, $data ) {
		vprintf(
			implode(
				'',
				[
					'<span id="tsf-description-reference_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" ></span>',
					'<span id="tsf-description-data_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" %2$s ></span>',
				]
			),
			[
				\esc_attr( $id ),
				// phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
				Interpreters\HTML::make_data_attributes( $data ),
			]
		);
	}
}