false,
'view' => '',
'errors' => array(),
'fields' => array(),
'values' => array(),
'submission' => null,
'notice' => '',
);
/**
* Hooks.
*/
public static function init() {
foreach ( self::shortcodes() as $tag ) {
add_shortcode( $tag, array( __CLASS__, 'shortcode' ) );
}
add_action( 'init', array( __CLASS__, 'map_legacy_request' ), 1 );
add_action( 'template_redirect', array( __CLASS__, 'template_redirect' ), 5 );
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
add_filter( 'wp_robots', array( __CLASS__, 'robots' ) );
}
/**
* Earlier editions used other URL / form field names (e.g. ?py_order=…&py_key=…).
* Links in emails already sent and cached copies of the old form must keep working.
*/
public static function map_legacy_request() {
// Earlier editions used py_* (≤ 1.2) and wpotkaz_* (WPOtkaz 1.3) names.
$map = array(
'otkazpro' => array( 'wpotkaz' ),
'otkazpro_order' => array( 'py_order', 'wpotkaz_order' ),
'otkazpro_key' => array( 'py_key', 'wpotkaz_key' ),
'otkazpro_done' => array( 'py_done', 'wpotkaz_done' ),
'otkazpro_src' => array( 'py_src', 'wpotkaz_src' ),
'otkazpro_action' => array( 'py_otkaz_action', 'wpotkaz_action' ),
'_otkazpro_nonce' => array( '_py_otkaz_nonce', '_wpotkaz_nonce' ),
'otkazpro_order_number' => array( 'py_order_number', 'wpotkaz_order_number' ),
'otkazpro_email' => array( 'py_email', 'wpotkaz_email' ),
'otkazpro_name' => array( 'py_name', 'wpotkaz_name' ),
'otkazpro_token' => array( 'py_token', 'wpotkaz_token' ),
'otkazpro_items' => array( 'py_items', 'wpotkaz_items' ),
'otkazpro_reason' => array( 'py_reason', 'wpotkaz_reason' ),
'otkazpro_website' => array( 'py_website', 'wpotkaz_website' ),
);
// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
foreach ( $map as $new => $olds ) {
foreach ( $olds as $old ) {
if ( isset( $_GET[ $old ] ) && ! isset( $_GET[ $new ] ) ) {
$_GET[ $new ] = $_GET[ $old ];
$_REQUEST[ $new ] = $_GET[ $old ];
}
if ( isset( $_POST[ $old ] ) && ! isset( $_POST[ $new ] ) ) {
$_POST[ $new ] = $_POST[ $old ];
$_REQUEST[ $new ] = $_POST[ $old ];
}
}
}
// phpcs:enable
}
/**
* Current + legacy shortcode tags.
*
* @return string[]
*/
public static function shortcodes() {
return array_merge( array( self::SHORTCODE ), self::LEGACY_SHORTCODES );
}
/**
* Does the content contain the form (any shortcode name)?
*
* @param string $content Content.
* @return bool
*/
public static function has_form_shortcode( $content ) {
foreach ( self::shortcodes() as $tag ) {
if ( has_shortcode( $content, $tag ) ) {
return true;
}
}
return false;
}
/* ---------------------------------------------------------------------
* URLs
* ------------------------------------------------------------------ */
/**
* Form page URL (translated page when WPML/Polylang is active).
*
* @param string $lang Language code.
* @return string
*/
public static function page_url( $lang = '' ) {
$page_id = (int) Settings::get( 'page_id' );
if ( $page_id && $lang && I18n::is_multilingual() ) {
$translated = (int) apply_filters( 'wpml_object_id', $page_id, 'page', true, $lang ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- third-party / core hook.
if ( ( ! $translated || $translated === $page_id ) && function_exists( 'pll_get_post' ) ) {
$translated = (int) pll_get_post( $page_id, $lang );
}
// Use the translation only if it really is a published page with the form.
if ( $translated && $translated !== $page_id && 'publish' === get_post_status( $translated ) && self::page_has_form( $translated ) ) {
$page_id = $translated;
}
}
$url = $page_id && self::is_valid_page( $page_id ) ? get_permalink( $page_id ) : '';
return $url ? $url : self::fallback_url();
}
/**
* Is the configured page usable: published and actually showing the form?
*
* @param int $page_id Page ID.
* @return bool
*/
public static function is_valid_page( $page_id ) {
static $cache = array();
$page_id = (int) $page_id;
if ( ! isset( $cache[ $page_id ] ) ) {
$cache[ $page_id ] = $page_id
&& 'publish' === get_post_status( $page_id )
&& ( self::page_has_form( $page_id ) || (int) get_option( 'py_otkaz_seen_page' ) === $page_id );
}
return $cache[ $page_id ];
}
/**
* Fallback address that always shows the form (theme header/footer), used when
* the form page is missing, unpublished or no longer contains the form.
*
* @return string
*/
public static function fallback_url() {
return add_query_arg( 'otkazpro', '1', home_url( '/' ) );
}
/**
* Is the current request the fallback page?
*
* @return bool
*/
public static function is_fallback_request() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return ! is_admin() && isset( $_GET['otkazpro'] );
}
/**
* Does the page contain the form (post content or Elementor data)?
*
* @param int $page_id Page ID.
* @return bool
*/
public static function page_has_form( $page_id ) {
$content = (string) get_post_field( 'post_content', $page_id ) . ' ' . (string) get_post_meta( $page_id, '_elementor_data', true );
foreach ( self::shortcodes() as $tag ) {
if ( false !== strpos( $content, '[' . $tag ) ) {
return true;
}
}
return false;
}
/**
* Direct (token) URL to the form for an order – works without login.
*
* @param \WC_Order $order Order.
* @param string $lang Language.
* @return string
*/
public static function order_url( $order, $lang = '' ) {
return add_query_arg(
array(
'otkazpro_order' => $order->get_id(),
'otkazpro_key' => $order->get_order_key(),
),
self::page_url( $lang )
);
}
/**
* URL of the current page (form action).
*
* @return string
*/
private static function current_page_url() {
if ( self::is_fallback_request() ) {
return self::fallback_url();
}
$id = get_queried_object_id();
return $id ? get_permalink( $id ) : self::page_url();
}
/* ---------------------------------------------------------------------
* Request lifecycle
* ------------------------------------------------------------------ */
/**
* Is the current request the form page?
*
* @return bool
*/
public static function is_form_page() {
if ( ! is_singular() ) {
return false;
}
$page_id = (int) Settings::get( 'page_id' );
if ( $page_id && is_page( $page_id ) ) {
return true;
}
if ( $page_id && I18n::is_multilingual() ) {
$translated = (int) apply_filters( 'wpml_object_id', $page_id, 'page', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- third-party / core hook.
if ( $translated && is_page( $translated ) ) {
return true;
}
}
$post = get_post();
return $post && self::has_form_shortcode( (string) $post->post_content );
}
/**
* Has the request any order / result parameters?
*
* @return bool
*/
private static function has_private_params() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return isset( $_GET['otkazpro_order'] ) || isset( $_GET['otkazpro_key'] ) || isset( $_GET['otkazpro_done'] ) || isset( $_GET['otkazpro'] );
}
/**
* No caching / no indexing / no referrer on the form page; handle POST before output.
*/
public static function template_redirect() {
if ( self::is_fallback_request() ) {
self::no_cache();
self::maybe_handle_post( true );
self::render_fallback_page();
exit;
}
if ( ! self::is_form_page() ) {
return;
}
self::no_cache();
self::maybe_handle_post( true );
}
/**
* Output the fallback page with the theme's header and footer.
*/
private static function render_fallback_page() {
status_header( 200 );
$title = __( 'Withdrawal from contract', 'otkazpro' );
add_filter(
'pre_get_document_title',
static function () use ( $title ) {
return $title . ' – ' . wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
}
);
$main = static function () use ( $title ) {
echo '';
echo '' . esc_html( $title ) . '
';
echo self::shortcode(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- form markup from templates/, escaped there.
echo '';
};
if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
// Block theme: render the theme's header/footer template parts ourselves.
$header = do_blocks( '' );
$footer = do_blocks( '' );
if ( function_exists( '_block_template_render_title_tag' ) && ! has_action( 'wp_head', '_block_template_render_title_tag' ) ) {
add_action( 'wp_head', '_block_template_render_title_tag', 1 );
}
?>
>
>
$number,
'email' => self::post( 'otkazpro_email' ),
);
if ( Verifier::is_rate_limited() ) {
self::error( __( 'Too many unsuccessful attempts. Please try again in 15 minutes or contact us.', 'otkazpro' ) );
return;
}
if ( self::is_bot() ) {
Verifier::register_failure();
self::error( self::generic_not_found() );
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$nonce = isset( $_POST['_otkazpro_nonce'] ) ? sanitize_key( wp_unslash( $_POST['_otkazpro_nonce'] ) ) : '';
// Guests: no nonce. Page caches that ignore DONOTCACHEPAGE would otherwise serve a
// stale nonce and block every lookup (= obstructing the withdrawal). A cross-site
// lookup changes nothing; rate limiting + honeypot protect against enumeration.
if ( is_user_logged_in() && ! wp_verify_nonce( $nonce, 'py_otkaz_lookup' ) ) {
self::error( __( 'The page was open for too long. Please submit the form again.', 'otkazpro' ) );
return;
}
if ( '' === $number ) {
self::$state['fields']['otkazpro_order_number'] = __( 'Enter the order number.', 'otkazpro' );
}
if ( '' === $email || ! is_email( $email ) ) {
self::$state['fields']['otkazpro_email'] = __( 'Enter a valid email address, e.g. name@example.com.', 'otkazpro' );
}
/**
* Extra validation for the lookup step (e.g. a CAPTCHA). Return a WP_Error to block.
*
* @param null|\WP_Error $result Result.
* @param array $values Submitted values.
*/
$extra = apply_filters( 'py_otkaz_validate_lookup', null, self::$state['values'] );
if ( is_wp_error( $extra ) ) {
self::error( $extra->get_error_message() );
return;
}
if ( self::$state['fields'] ) {
return;
}
$order = Verifier::lookup( $number, $email );
if ( ! $order ) {
Verifier::register_failure();
self::error( self::generic_not_found() );
return;
}
$url = add_query_arg( 'otkazpro_src', 'lookup', self::order_url( $order, I18n::current_language() ) );
self::redirect( $url, $can_redirect );
}
/**
* Step 2: confirm withdrawal.
*
* @param bool $can_redirect Can redirect.
*/
private static function handle_submit( $can_redirect ) {
$order_id = absint( self::post( 'otkazpro_order' ) );
$key = self::post( 'otkazpro_key' );
$order = Verifier::get_order( $order_id );
self::$state['view'] = 'order';
if ( ! Verifier::can_access( $order, $key ) ) {
Verifier::register_failure();
self::$state['view'] = 'lookup';
self::error( __( 'This link is invalid or has expired. Please find your order using its number and email.', 'otkazpro' ) );
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Missing
$raw_items = isset( $_POST['otkazpro_items'] ) && is_array( $_POST['otkazpro_items'] ) ? wp_unslash( $_POST['otkazpro_items'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitised below.
$items = array();
$posted = array();
foreach ( $raw_items as $item_id => $item ) {
if ( ! is_array( $item ) ) {
continue;
}
$item_id = absint( $item_id );
$qty = isset( $item['qty'] ) ? (int) $item['qty'] : 1;
$on = ! empty( $item['selected'] );
$posted[ $item_id ] = array(
'selected' => $on,
'qty' => $qty,
);
if ( $item_id && $on ) {
$items[ $item_id ] = $qty;
}
}
$reason = Settings::enabled( 'reason_field' ) && isset( $_POST['otkazpro_reason'] ) ? sanitize_textarea_field( wp_unslash( $_POST['otkazpro_reason'] ) ) : '';
$token = self::post( 'otkazpro_token' );
$nonce = isset( $_POST['_otkazpro_nonce'] ) ? sanitize_key( wp_unslash( $_POST['_otkazpro_nonce'] ) ) : '';
// phpcs:enable
$name = self::post( 'otkazpro_name' );
$email = strtolower( sanitize_email( self::post( 'otkazpro_email' ) ) );
self::$state['values'] = array(
'name' => $name,
'email' => self::post( 'otkazpro_email' ),
'items' => $posted,
'reason' => $reason,
);
// Double submit (refresh / double click) → show the existing result.
if ( $token && preg_match( '/^[a-zA-Z0-9]{16,64}$/', $token ) ) {
$done = get_transient( 'py_otkaz_idem_' . $token );
if ( $done ) {
self::redirect( add_query_arg( 'otkazpro_done', $done, self::current_page_url() ), $can_redirect, (string) $done );
return;
}
}
if ( self::is_bot() ) {
self::error( __( 'Your withdrawal could not be submitted. Please try again.', 'otkazpro' ) );
return;
}
// The secret order key already proves the request comes from the order email / page,
// so it doubles as CSRF protection; the nonce is required only for owner access.
$has_key = '' !== $key && hash_equals( (string) $order->get_order_key(), $key );
if ( ! $has_key && ! wp_verify_nonce( $nonce, 'py_otkaz_submit_' . $order->get_id() ) ) {
self::error( __( 'The page was open for too long. Please review the details and confirm again.', 'otkazpro' ) );
return;
}
if ( '' === $name ) {
self::$state['fields']['otkazpro_name'] = __( 'Enter your name.', 'otkazpro' );
} elseif ( mb_strlen( $name ) > 200 ) {
self::$state['fields']['otkazpro_name'] = __( 'The name is too long.', 'otkazpro' );
}
if ( '' === $email || ! is_email( $email ) ) {
self::$state['fields']['otkazpro_email'] = __( 'Enter a valid email address, e.g. name@example.com.', 'otkazpro' );
}
if ( mb_strlen( $reason ) > 2000 ) {
$reason = mb_substr( $reason, 0, 2000 );
}
if ( self::$state['fields'] ) {
return;
}
$source = Verifier::is_owner( $order ) ? 'account' : 'link';
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['otkazpro_src'] ) && 'lookup' === $_POST['otkazpro_src'] ) {
$source = 'lookup';
}
$result = Submission::create(
$order,
array(
'name' => $name,
'email' => $email,
'items' => $items,
'reason' => $reason,
'source' => $source,
)
);
if ( is_wp_error( $result ) ) {
$data = $result->get_error_data();
if ( is_array( $data ) && ! empty( $data['fields'] ) ) {
self::$state['fields'] = array_merge( self::$state['fields'], $data['fields'] );
} else {
self::error( $result->get_error_message() );
}
return;
}
$done = wp_generate_password( 32, false );
set_transient( 'py_otkaz_done_' . $done, $result->id, DAY_IN_SECONDS );
if ( $token && preg_match( '/^[a-zA-Z0-9]{16,64}$/', $token ) ) {
set_transient( 'py_otkaz_idem_' . $token, $done, DAY_IN_SECONDS );
}
self::$state['submission'] = $result;
self::redirect( add_query_arg( 'otkazpro_done', $done, self::current_page_url() ), $can_redirect, $done );
}
/**
* PRG redirect, or render the success view in place when headers are gone.
*
* @param string $url URL.
* @param bool $can_redirect Can redirect.
* @param string $done Success token (fallback render).
*/
private static function redirect( $url, $can_redirect, $done = '' ) {
if ( $can_redirect && ! headers_sent() ) {
wp_safe_redirect( $url, 303 );
exit;
}
if ( $done ) {
self::$state['view'] = 'success';
self::$state['submission'] = self::submission_from_token( $done );
} else {
// Fallback: render the target in place.
$query = wp_parse_url( $url, PHP_URL_QUERY );
parse_str( (string) $query, $args );
$_GET = array_merge( $_GET, $args ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
self::$state['view'] = '';
}
}
/**
* Load the submission behind a success token.
*
* @param string $token Token.
* @return Submission|null
*/
private static function submission_from_token( $token ) {
if ( ! preg_match( '/^[a-zA-Z0-9]{16,64}$/', (string) $token ) ) {
return null;
}
$id = (int) get_transient( 'py_otkaz_done_' . $token );
return $id ? Submission::get( $id ) : null;
}
/* ---------------------------------------------------------------------
* Rendering
* ------------------------------------------------------------------ */
/**
* Shortcode callback.
*
* @param array $atts Attributes.
* @return string
*/
public static function shortcode( $atts = array() ) {
if ( is_admin() || wp_is_json_request() ) {
return '';
}
self::enqueue_assets();
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- standard page-cache constant.
}
if ( ! self::is_fallback_request() && is_page() && (int) get_option( 'py_otkaz_seen_page' ) !== (int) get_queried_object_id() ) {
update_option( 'py_otkaz_seen_page', (int) get_queried_object_id(), false );
}
self::maybe_handle_post( false );
$view = self::$state['view'];
$args = array();
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! $view && isset( $_GET['otkazpro_done'] ) ) {
$submission = self::submission_from_token( sanitize_text_field( wp_unslash( $_GET['otkazpro_done'] ) ) );
if ( $submission ) {
$view = 'success';
self::$state['submission'] = $submission;
} else {
self::$state['notice'] = __( 'Your withdrawal has already been submitted. The confirmation was sent to your email.', 'otkazpro' );
$view = 'lookup';
}
}
if ( 'success' === $view && ! self::$state['submission'] ) {
$view = 'lookup';
}
$order = null;
if ( ! $view || 'order' === $view ) {
$order_id = isset( $_POST['otkazpro_order'] ) ? absint( $_POST['otkazpro_order'] ) : ( isset( $_GET['otkazpro_order'] ) ? absint( $_GET['otkazpro_order'] ) : 0 ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
$key = isset( $_POST['otkazpro_key'] ) ? sanitize_text_field( wp_unslash( $_POST['otkazpro_key'] ) ) : ( isset( $_GET['otkazpro_key'] ) ? sanitize_text_field( wp_unslash( $_GET['otkazpro_key'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( $order_id ) {
$order = Verifier::get_order( $order_id );
if ( Verifier::can_access( $order, $key ) ) {
$view = 'order';
} else {
$order = null;
$view = 'lookup';
if ( ! self::$state['errors'] ) {
self::error( __( 'This link is invalid or has expired. Please find your order using its number and email.', 'otkazpro' ) );
}
}
} else {
$view = 'lookup';
}
}
// phpcs:enable
$common = array(
'form_action' => self::current_page_url(),
'errors' => self::$state['errors'],
'fields' => self::$state['fields'],
'values' => self::$state['values'],
'notice' => self::$state['notice'],
'honeypot' => self::HONEYPOT,
);
switch ( $view ) {
case 'success':
$args = self::success_args( self::$state['submission'] );
break;
case 'order':
$args = self::order_args( $order );
break;
default:
$view = 'lookup';
$args = self::lookup_args();
}
$args = array_merge( $common, $args, array( 'view' => $view ) );
/**
* Filter template args for the form views.
*
* @param array $args Args.
* @param string $view lookup|order|success.
*/
$args = apply_filters( 'py_otkaz_form_args', $args, $view );
return Cache_Check::marker() . self::template( 'form-' . $view . '.php', $args );
}
/**
* Args for the lookup view.
*
* @return array
*/
private static function lookup_args() {
$orders = array();
if ( is_user_logged_in() ) {
$candidates = wc_get_orders(
array(
'customer_id' => get_current_user_id(),
'status' => array_map(
static function ( $s ) {
return 'wc-' . $s;
},
Order_Data::eligible_statuses()
),
'limit' => 10,
'orderby' => 'date',
'order' => 'DESC',
)
);
foreach ( $candidates as $candidate ) {
if ( $candidate instanceof \WC_Order && Order_Data::has_selectable_items( $candidate ) ) {
$orders[] = $candidate;
}
}
}
return array(
'intro' => Settings::text( 'text_intro' ),
'start_label' => Settings::text( 'text_start_button' ),
'account_orders' => $orders,
// Only in the POST response: a cached GET page must never carry one visitor's block.
'rate_limited' => self::is_post() && Verifier::is_rate_limited(),
);
}
/**
* Args for the order view.
*
* @param \WC_Order $order Order.
* @return array
*/
private static function order_args( $order ) {
$items = Order_Data::get_items( $order );
$eligible = Order_Data::is_eligible_status( $order );
$values = self::$state['values'];
$has_posted = isset( $values['items'] );
$groups = array(
'selectable' => array(),
'excluded' => array(),
'done' => array(),
);
foreach ( $items as $id => $item ) {
if ( $has_posted ) {
$item['checked'] = ! empty( $values['items'][ $id ]['selected'] );
$item['qty'] = isset( $values['items'][ $id ]['qty'] ) ? max( 1, min( $item['remaining'], (int) $values['items'][ $id ]['qty'] ) ) : $item['remaining'];
} else {
$item['checked'] = $item['preselect'];
$item['qty'] = $item['remaining'];
}
if ( $item['remaining'] < 1 ) {
$groups['done'][ $id ] = $item;
} elseif ( $item['excluded'] ) {
$groups['excluded'][ $id ] = $item;
} else {
$groups['selectable'][ $id ] = $item;
}
}
$name = trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() );
// phpcs:ignore WordPress.Security.NonceVerification
$src = isset( $_REQUEST['otkazpro_src'] ) && 'lookup' === $_REQUEST['otkazpro_src'] ? 'lookup' : '';
return array(
'order' => $order,
'order_key' => $order->get_order_key(),
'eligible' => $eligible,
'groups' => $groups,
'any_selectable' => (bool) array_filter( wp_list_pluck( $items, 'selectable' ) ),
'strict' => 'strict' === Settings::get( 'exclusion_mode' ),
'reason_enabled' => Settings::enabled( 'reason_field' ),
'confirm_label' => Settings::text( 'text_confirm_button' ),
'name' => isset( $values['name'] ) ? $values['name'] : $name,
'email' => isset( $values['email'] ) ? $values['email'] : $order->get_billing_email(),
'reason' => isset( $values['reason'] ) ? $values['reason'] : '',
'token' => wp_generate_password( 32, false ),
'src' => $src,
'store_email' => get_option( 'woocommerce_email_from_address', get_option( 'admin_email' ) ),
);
}
/**
* Args for the success view.
*
* @param Submission $submission Submission.
* @return array
*/
private static function success_args( $submission ) {
return array(
'submission' => $submission,
'order' => Verifier::get_order( $submission->order_id ),
'return_instructions' => Settings::text( 'text_return_instructions' ),
);
}
/**
* Render a template (overridable in theme/woocommerce/otkazpro/).
*
* @param string $name Template file.
* @param array $args Args.
* @return string
*/
public static function template( $name, $args ) {
return wc_get_template_html( $name, $args, 'woocommerce/otkazpro/', PY_OTKAZ_DIR . 'templates/' );
}
/* ---------------------------------------------------------------------
* Assets
* ------------------------------------------------------------------ */
/**
* Enqueue on the form page and in My Account.
*/
public static function enqueue() {
if ( self::is_fallback_request() || self::is_form_page() || ( function_exists( 'is_account_page' ) && is_account_page() ) ) {
self::enqueue_assets();
}
}
/**
* Register + enqueue CSS/JS with the accent variables.
*/
public static function enqueue_assets() {
if ( wp_style_is( 'otkazpro', 'enqueued' ) ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$css = file_exists( PY_OTKAZ_DIR . 'assets/css/otkazpro' . $suffix . '.css' ) ? $suffix : '';
$js = file_exists( PY_OTKAZ_DIR . 'assets/js/otkazpro' . $suffix . '.js' ) ? $suffix : '';
wp_enqueue_style( 'otkazpro', PY_OTKAZ_URL . 'assets/css/otkazpro' . $css . '.css', array(), PY_OTKAZ_VERSION );
wp_add_inline_style( 'otkazpro', self::inline_css() );
wp_enqueue_script(
'otkazpro',
PY_OTKAZ_URL . 'assets/js/otkazpro' . $js . '.js',
array(),
PY_OTKAZ_VERSION,
array(
'in_footer' => true,
'strategy' => 'defer',
)
);
wp_localize_script(
'otkazpro',
'otkazproL10n',
array(
'sending' => __( 'Sending…', 'otkazpro' ),
'selectOne' => __( 'Select at least one product to continue.', 'otkazpro' ),
/* translators: 1: number of products, 2: number of pieces */
'selected' => __( 'Selected: %1$s products, %2$s pcs.', 'otkazpro' ),
)
);
}
/**
* CSS custom properties from the settings.
*
* @return string
*/
private static function inline_css() {
$accent = sanitize_hex_color( (string) Settings::get( 'accent_color' ) );
$accent = $accent ? $accent : '#111111';
$radius = (int) Settings::get( 'radius' );
$radius = in_array( $radius, array( 0, 4, 8, 999 ), true ) ? $radius : 8;
return sprintf(
'.otkazpro,.otkazpro-account{--otkazpro-accent:%1$s;--otkazpro-accent-contrast:%2$s;--otkazpro-ink:%3$s;--otkazpro-radius:%4$dpx;--otkazpro-btn-radius:%5$dpx}',
$accent,
Util::text_on( $accent ),
Util::ink_on_white( $accent ),
min( 8, $radius ),
$radius
);
}
}