Ctrl K
ComunidadGuías…

plugins/woocommerce-beta-tester/includes/class-wc-beta-tester-admin-menus.php

Rama trunk · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php
/**
 * Beta Tester Admin Menus class
 *
 * @package WC_Beta_Tester
 */

defined( 'ABSPATH' ) || exit;

/**
 * WC_Beta_Tester_Admin_Menus Main Class.
 */
class WC_Beta_Tester_Admin_Menus {

	/**
	 * Environment values the WooCommerce Status page marks private and leaves out of its copyable report.
	 *
	 * Nested keys use dot notation.
	 */
	private const PRIVATE_KEYS = array(
		'log_directory',
		'action_scheduler.source_path', // Added to the environment section by WooCommerce PR #69882.
	);

	/**
	 * Constructor
	 */
	public function __construct() {
		if ( class_exists( 'WC_Admin_Status' ) ) {
			add_action( 'admin_bar_menu', array( $this, 'admin_bar_menus' ), 50 );
		}
		add_action( 'admin_footer', array( $this, 'version_information_template' ) );
		add_action( 'admin_head', array( $this, 'hide_from_menus' ) );
	}

	/**
	 * Get issue template from GitHub.
	 *
	 * @return string
	 */
	protected function get_ticket_template() {
		ob_start();
		?>
**Describe the bug**
A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Isolating the problem (mark completed items with an [x]):**
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
- [ ] I can reproduce this bug consistently using the steps above.

**WordPress Environment**
<details>
```
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin.
```
</details>
		<?php

		$bug_tpl = ob_get_clean();
		return $bug_tpl;
	}

	/**
	 * Get info about the current theme for SSR.
	 *
	 * @param string $theme Current theme.
	 * @return string
	 */
	private function get_theme_ssr_info( $theme ) {
		$theme_info = __( "\n\n### Theme ###\n\n", 'woocommerce-beta-tester' );

		$version = $theme['version'];
		if ( version_compare( $theme['version'], $theme['version_latest'], '<' ) ) {
			/* translators: 1: latest version */
			$version .= sprintf( __( ' - %s is available', 'woocommerce-beta-tester' ), esc_html( $theme['version_latest'] ) );
		}

		$child_theme = $theme['is_child_theme'] ? __( 'Yes', 'woocommerce-beta-tester' ) : __( 'No - If you are modifying WooCommerce on a parent theme that you did not build personally we recommend using a child theme.', 'woocommerce-beta-tester' );

		if ( $theme['is_child_theme'] ) {
			$parent_version = $theme['parent_version'];
			if ( version_compare( $theme['parent_version'], $theme['parent_version_latest'], '<' ) ) {
				/* translators: 1: latest version */
				$parent_version .= sprintf( __( ' - %s is available', 'woocommerce-beta-tester' ), esc_html( $theme['parent_version_latest'] ) );
			}

			$child_theme .= sprintf(
				__( "Parent theme name: %1\$s\nParent theme version: %2\$s\nParent theme author URL: %3\$s\n", 'woocommerce-beta-tester' ),
				$theme['parent_name'],
				$parent_version,
				$theme['parent_author_url']
			);
		}

		$theme_info .= sprintf(
			__( "Name: %1\$s\nVersion: %2\$s\nAuthor URL: %3\$s\nChild Theme: %4\$s\nWooCommerce Support: %5\$s", 'woocommerce-beta-tester' ),
			$theme['name'],
			$version,
			esc_html( $theme['author_url'] ),
			$child_theme,
			! $theme['has_woocommerce_support'] ? __( 'Not declared', 'woocommerce-beta-tester' ) : __( 'Yes', 'woocommerce-beta-tester' )
		);

		return $theme_info;
	}

	/**
	 * Get info about the current plugins for SSR.
	 *
	 * @param array $active_plugins List of active plugins.
	 * @param array $untested_plugins List of untested plugins.
	 * @return string
	 */
	private function get_plugins_ssr_info( $active_plugins, $untested_plugins ) {
		$plugins_info = __( "\n\n### Plugins ###\n\n", 'woocommerce-beta-tester' );

		foreach ( $active_plugins as $plugin ) {
			if ( ! empty( $plugin['name'] ) ) {
				// Link the plugin name to the plugin url if available.
				$plugin_name = esc_html( $plugin['name'] );

				$version_string = '';
				$network_string = '';
				if ( strstr( $plugin['url'], 'woothemes.com' ) || strstr( $plugin['url'], 'woo.com' ) ) {
					if ( ! empty( $plugin['version_latest'] ) && version_compare( $plugin['version_latest'], $plugin['version'], '>' ) ) {
						/* translators: %s: plugin latest version */
						$version_string = sprintf( esc_html__( '%s is available', 'woocommerce-beta-tester' ), $plugin['version_latest'] );
					}

					if ( false !== (bool) $plugin['network_activated'] ) {
						$network_string = __( 'Network enabled', 'woocommerce-beta-tester' );
					}
				}
				$untested_string = '';
				if ( array_key_exists( $plugin['plugin'], $untested_plugins ) ) {
					$untested_string = __( 'Not tested with the active version of WooCommerce', 'woocommerce-beta-tester' );
				}

				$plugins_info .= sprintf( __( "%1\$s: by %2\$s - %3\$s - %4\$s\n", 'woocommerce-beta-tester' ), $plugin_name, $plugin['author_name'], $plugin['version'], $version_string . $untested_string . $network_string );
			}
		}

		return $plugins_info;
	}

	/**
	 * Get the environment section of the SSR, one "label: value" line per field.
	 *
	 * Object and array values are printed as JSON. Private values (see PRIVATE_KEYS) are left out.
	 *
	 * @param array|object $environment Environment values, already cast to their schema types.
	 * @param array        $properties  The environment properties of the system status schema.
	 * @return string
	 */
	private function get_environment_ssr_info( $environment, $properties ) {
		$ssr = '';

		foreach ( $environment as $key => $value ) {
			if ( in_array( $key, self::PRIVATE_KEYS, true ) ) {
				continue;
			}

			if ( 'external_object_cache' === $key ) {
				$ssr .= sprintf( "%s: %s\n", 'External object cache', wc_bool_to_string( $value ) );
				continue;
			}

			if ( is_bool( $value ) ) {
				$value = wc_bool_to_string( $value );
			} elseif ( is_array( $value ) || is_object( $value ) ) {
				$value = wp_json_encode( $this->remove_private_keys( $value, $key ) );
			}

			$label = $properties[ $key ]['description'] ?? $key;
			$ssr  .= sprintf( "%s: %s\n", $label, $value );
		}

		return $ssr;
	}

	/**
	 * Remove the private keys nested under an environment field before it is printed as JSON.
	 *
	 * @param array|object $value Field value.
	 * @param string       $key   Field key, the first segment of each dotted private key.
	 * @return array|object
	 */
	private function remove_private_keys( $value, $key ) {
		foreach ( self::PRIVATE_KEYS as $private_key ) {
			$segments = explode( '.', $private_key );

			if ( array_shift( $segments ) === $key && ! empty( $segments ) ) {
				$value = $this->remove_key_path( $value, $segments );
			}
		}

		return $value;
	}

	/**
	 * Remove one key path from a nested array or object. The value comes back as an array.
	 *
	 * @param array|object $value    Value to remove the key from.
	 * @param string[]     $segments Key path, outermost segment first.
	 * @return array
	 */
	private function remove_key_path( $value, array $segments ) {
		$value   = (array) $value;
		$segment = array_shift( $segments );

		if ( empty( $segments ) ) {
			unset( $value[ $segment ] );
		} elseif ( isset( $value[ $segment ] ) && ( is_array( $value[ $segment ] ) || is_object( $value[ $segment ] ) ) ) {
			$value[ $segment ] = $this->remove_key_path( $value[ $segment ], $segments );
		}

		return $value;
	}

	/**
	 * Constructs a WC System Status Report.
	 *
	 * The report is embedded in the admin bar markup of every wp-admin page, so it is only built for
	 * users who may view the System Status page. Everyone else gets an empty string.
	 *
	 * @return string
	 */
	protected function construct_ssr() {
		if ( ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown -- WooCommerce capability, the same gate core uses for the status page.
			return '';
		}

		// This function depends on the WC core global being available. Sometimes, such as when we deactivate
		// WC to install a live branches version, WC will not be available and cause a crash if we don't exit early
		// here.
		if ( ! function_exists( 'WC' ) ) {
			return '';
		}

		if ( version_compare( WC()->version, '3.6', '<' ) ) {
			return '';
		}

		$transient_name = 'wc-beta-tester-ssr';
		$ssr            = get_transient( $transient_name );

		if ( false === $ssr ) {
			if ( ! class_exists( 'WC_REST_System_Status_Controller' ) ) {
				return '';
			}

			// Get SSR.
			$api      = new WC_REST_System_Status_Controller();
			$schema   = $api->get_item_schema();
			$mappings = $api->get_item_mappings_per_fields( array( 'environment' ) );
			$response = array();

			foreach ( $mappings as $section => $values ) {
				foreach ( $values as $key => $value ) {
					if ( isset( $schema['properties'][ $section ]['properties'][ $key ]['type'] ) ) {
						settype( $values[ $key ], $schema['properties'][ $section ]['properties'][ $key ]['type'] );
					}
				}
				settype( $values, $schema['properties'][ $section ]['type'] );
				$response[ $section ] = $values;
			}

			$ssr = $this->get_environment_ssr_info( $response['environment'], $schema['properties']['environment']['properties'] );

			if ( ! class_exists( 'WC_Plugin_Updates' ) ) {
				include_once WC_ABSPATH . 'includes/admin/plugin-updates/class-wc-plugin-updates.php';
			}

			$active_plugins   = $api->get_active_plugins();
			$plugin_updates   = new WC_Plugin_Updates();
			$untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'minor' );
			$ssr             .= $this->get_plugins_ssr_info( $active_plugins, $untested_plugins );
			$theme            = $api->get_theme_info();
			$ssr             .= $this->get_theme_ssr_info( $theme );

			set_transient( $transient_name, $ssr, DAY_IN_SECONDS );
		}

		return $ssr;
	}

	/**
	 * Get URL for creating a GitHub ticket.
	 *
	 * @return string
	 */
	protected function get_github_ticket_url() {
		$bug_tpl = $this->get_ticket_template();
		$ssr     = $this->construct_ssr();
		$body    = str_replace( 'Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin.', $ssr, $bug_tpl );
		$body    = str_replace( '```', '', $body ); // Remove since this break how is displayed.

		$wc_plugin_data = get_plugin_data( WC_PLUGIN_FILE );
		if ( isset( $wc_plugin_data['Version'] ) ) {
			$version = $wc_plugin_data['Version'];
		} else {
			$version = '-';
		}

		return add_query_arg(
			array(
				'body'  => rawurlencode( $body ),
				/* translators: %s: woocommerce version */
				'title' => rawurlencode( sprintf( __( '[WC Beta Tester] Bug report for version "%s"', 'woocommerce-beta-tester' ), $version ) ),
			),
			'https://github.com/woocommerce/woocommerce/issues/new'
		);
	}

	/**
	 * Add the "Visit Store" link in admin bar main menu.
	 *
	 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance.
	 */
	public function admin_bar_menus( $wp_admin_bar ) {
		if ( ! is_admin() || ! is_user_logged_in() ) {
			return;
		}

		// Show only when the user is a member of this site, or they're a super admin.
		if ( ! is_user_member_of_blog() && ! is_super_admin() ) {
			return;
		}

		// Add the beta tester root node.
		$wp_admin_bar->add_node(
			array(
				'parent' => 0,
				'id'     => 'wc-beta-tester',
				'title'  => __( 'WC Beta Tester', 'woocommerce-beta-tester' ),
			)
		);

		$settings = WC_Beta_Tester::get_settings();
		switch ( $settings->channel ) {
			case 'beta':
				$current_channel = __( 'Beta', 'woocommerce-beta-tester' );
				break;
			case 'rc':
				$current_channel = __( 'Release Candidate', 'woocommerce-beta-tester' );
				break;
			default:
				$current_channel = __( 'Stable', 'woocommerce-beta-tester' );
				break;
		}

		$nodes = array(
			array(
				'parent' => 'wc-beta-tester',
				'id'     => 'update-channel',
				/* translators: %s: current channel */
				'title'  => sprintf( __( 'Channel: %s', 'woocommerce-beta-tester' ), $current_channel ),
				'href'   => admin_url( 'plugins.php?page=wc-beta-tester' ),
			),
			array(
				'parent' => 'wc-beta-tester',
				'id'     => 'import-export-settings',
				'title'  => __( 'Import/Export Settings', 'woocommerce-beta-tester' ),
				'href'   => admin_url( 'admin.php?page=wc-beta-tester-settings' ),
			),
			array(
				'parent' => 'wc-beta-tester',
				'id'     => 'show-version-info',
				/* translators: %s: current version */
				'title'  => sprintf( __( 'Release %s information', 'woocommerce-beta-tester' ), WC_VERSION ),
				'href'   => '#',
			),
			array(
				'parent' => 'wc-beta-tester',
				'id'     => 'switch-version',
				'title'  => __( 'Switch versions', 'woocommerce-beta-tester' ),
				'href'   => admin_url( 'plugins.php?page=wc-beta-tester-version-picker' ),
			),
			array(
				'parent' => 'wc-beta-tester',
				'id'     => 'submit-gh-ticket',
				'title'  => __( 'Submit bug report', 'woocommerce-beta-tester' ),
				'href'   => '#',
				'meta'   => array(
					// We can't simply use the href here since WP core calls esc_url on it which strips some parts.
					'onclick' => 'javascript:window.open( "' . esc_js( $this->get_github_ticket_url() ) . '" );',
				),
			),
		);

		foreach ( $nodes as $node ) {
			$wp_admin_bar->add_node( $node );
		}
	}

	/**
	 * Hide menu items from view so the pages exist, but the menu items do not.
	 */
	public function hide_from_menus() {
		global $submenu;

		$items_to_remove = array( 'wc-beta-tester-settings', 'wc-beta-tester-version-picker', 'wc-beta-tester' );
		if ( isset( $submenu['plugins.php'] ) ) {
			foreach ( $submenu['plugins.php'] as $key => $menu ) {
				if ( in_array( $menu[2], $items_to_remove, true ) ) {
					unset( $submenu['plugins.php'][ $key ] );
				}
			}
		}
	}

	/**
	 * Template for version information.
	 */
	public function version_information_template() {
		?>
		<script type="text/template" id="tmpl-wc-beta-tester-version-info">
			<div class="wc-backbone-modal wc-backbone-modal-beta-tester-version-info">
				<div class="wc-backbone-modal-content">
					<section class="wc-backbone-modal-main" role="main">
						<header class="wc-backbone-modal-header">
							<h1>
							<?php
								/* translators: %s: version number */
								echo esc_html( sprintf( __( 'Release %s information', 'woocommerce-beta-tester' ), '{{ data.version }}' ) );
							?>
							</h1>
							<button class="modal-close modal-close-link dashicons dashicons-no-alt">
								<span class="screen-reader-text"><?php esc_html_e( 'Close modal panel', 'woocommerce-beta-tester' ); ?></span>
							</button>
						</header>
						<article>
							<?php do_action( 'woocommerce_admin_version_information_start' ); ?>
							{{ data.description }}
							<?php do_action( 'woocommerce_admin_version_information_end' ); ?>
						</article>
						<footer>
							<a target="_blank" href="https://github.com/woocommerce/woocommerce/releases/tag/{{ data.version }}"><?php esc_html_e( 'Read more on GitHub', 'woocommerce-beta-tester' ); ?></a>
						</footer>
					</section>
				</div>
			</div>
			<div class="wc-backbone-modal-backdrop modal-close"></div>
		</script>
		<?php
	}
}

return new WC_Beta_Tester_Admin_Menus();