*
* @see http://wcpos.com
* @package WCPOS\WooCommercePOS
*/
namespace WCPOS\WooCommercePOS\Admin;
use WCPOS\WooCommercePOS\API\V1\Logs;
use WCPOS\WooCommercePOS\Services\Cloudflare_Detector;
use WCPOS\WooCommercePOS\Services\Extensions as ExtensionsService;
use WCPOS\WooCommercePOS\Services\Settings as SettingsService;
use const WCPOS\WooCommercePOS\PLUGIN_NAME;
use const WCPOS\WooCommercePOS\PLUGIN_URL;
use const WCPOS\WooCommercePOS\TRANSLATION_VERSION;
use const WCPOS\WooCommercePOS\VERSION;
/**
* Class Settings.
*/
class Settings {
/**
* Settings constructor.
*/
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_action( 'admin_head', array( $this, 'override_wpcontent_padding' ) );
add_action( 'in_admin_header', array( $this, 'remove_admin_notices' ) );
/*
* Initializes the settings for WCPOS in the admin panel.
*
* This action hook can be used to run additional initialization routines
* when the WCPOS settings are being set up in the admin panel.
*
* @since 1.0.0
*
* @param Settings $this Settings class instance for the current admin context.
*/
do_action( 'woocommerce_pos_admin_settings_init', $this );
}
/**
* Output the settings pages.
*/
public static function display_settings_page(): void {
printf(
'
',
/* translators: Short WCPOS UI label; keep concise. */
esc_html__( 'Error', 'woocommerce-pos' ),
esc_html__( 'Settings failed to load, please contact support', 'woocommerce-pos' )
);
}
/**
* Zero out #wpcontent padding so the settings page fills edge-to-edge.
*
* WordPress sets padding-left on #wpcontent, but the value varies across
* themes, plugins, and screen sizes. Rather than guessing the value with
* a negative margin hack, we remove it entirely on our settings page.
*
* @return void
*/
public function override_wpcontent_padding(): void {
echo '';
}
/**
* Remove all admin notices on our settings page.
*
* @return void
*/
public function remove_admin_notices(): void {
remove_all_actions( 'admin_notices' );
remove_all_actions( 'all_admin_notices' );
}
/**
* Enqueue assets.
*
* Note: SCRIPT_DEBUG should be set in the wp-config.php file for debugging
*
* @return void
*/
public function enqueue_assets(): void {
$is_development = isset( $_ENV['DEVELOPMENT'] )
&& wp_validate_boolean( sanitize_text_field( wp_unslash( $_ENV['DEVELOPMENT'] ) ) );
$dir = $is_development ? 'build' : 'assets';
wp_enqueue_style(
PLUGIN_NAME . '-settings-styles',
PLUGIN_URL . $dir . '/css/settings.css',
array(),
VERSION
);
wp_enqueue_script(
PLUGIN_NAME . '-settings',
PLUGIN_URL . $dir . '/js/settings.js',
array(
'react',
'react-dom',
'wp-api-fetch',
\WCPOS\WooCommercePOS\Admin::API_FETCH_METHOD_PARAM_HANDLE,
'wp-url',
'lodash',
),
VERSION,
true
);
wp_add_inline_script( PLUGIN_NAME . '-settings', Menu::get_posthog_inline_script(), 'before' );
// Add inline script.
wp_add_inline_script( PLUGIN_NAME . '-settings', $this->inline_script(), 'before' );
}
/**
* Generate the inline script for settings.
*
* @return string
*/
private function inline_script(): string {
$settings_service = SettingsService::instance();
$barcodes = array_values( $settings_service->get_barcodes() );
$order_statuses = $settings_service->get_order_statuses();
$update_ext_count = $this->get_update_available_count();
$unread_logs = Logs::get_unread_counts( get_current_user_id() );
$current_user_id = get_current_user_id();
$countries = function_exists( 'WC' )
? WC()->countries->get_countries()
: array();
$store_country = function_exists( 'WC' )
? WC()->countries->get_base_country()
: '';
$store_options = apply_filters( 'woocommerce_pos_cloud_print_store_options', array() );
$anon_id = ( new \WCPOS\WooCommercePOS\Services\Anon_ID() )->get();
$site_uuid = get_option( 'woocommerce_pos_uuid', '' );
return \sprintf(
'var wcpos = wcpos || {}; wcpos.settings = {
environment: %s,
barcodes: %s,
order_statuses: %s,
countries: %s,
storeCountry: %s,
cloudPrintStoreOptions: %s,
updateExtensionsCount: %s,
unreadLogCounts: %s,
currentUserId: %s,
anon_id: %s,
site_uuid: %s
}; wcpos.translationVersion = %s;',
json_encode( array( 'cloudflare' => ( new Cloudflare_Detector() )->detect() ) ),
json_encode( $barcodes ),
json_encode( $order_statuses ),
json_encode( $countries ),
json_encode( $store_country ),
json_encode( array_values( (array) $store_options ) ),
json_encode( $update_ext_count ),
json_encode( $unread_logs ),
json_encode( $current_user_id ),
json_encode( $anon_id ),
json_encode( $site_uuid ),
json_encode( TRANSLATION_VERSION )
);
}
/**
* Get the count of extensions that have updates available.
*
* Uses the cached catalog transient and local plugin versions to avoid
* remote fetches on every page load. Returns null if the catalog hasn't
* been fetched yet.
*
* @return int|null
*/
private function get_update_available_count(): ?int {
$cached = get_transient( ExtensionsService::TRANSIENT_KEY );
if ( false === $cached || ! \is_array( $cached ) ) {
return null;
}
if ( ! \function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$installed_plugins = get_plugins();
$installed_by_slug = array();
$count = 0;
foreach ( $installed_plugins as $plugin_file => $plugin_data ) {
$parts = explode( '/', $plugin_file, 2 );
if ( isset( $parts[1] ) ) {
$installed_by_slug[ $parts[0] ] = $plugin_data['Version'] ?? '';
}
}
foreach ( $cached as $entry ) {
if ( ! \is_array( $entry ) ) {
continue;
}
$slug = $entry['slug'] ?? '';
$remote_ver = $entry['latest_version'] ?? $entry['version'] ?? '';
if ( $slug && $remote_ver && isset( $installed_by_slug[ $slug ] ) ) {
if ( version_compare( $installed_by_slug[ $slug ], $remote_ver, '<' ) ) {
++$count;
}
}
}
return $count;
}
}