0,
'statuses' => array( 'wc-processing', 'wc-completed', 'wc-on-hold' ),
'deadline_days' => 14,
'custom_status' => 'no',
'text_return_instructions' => '',
'reason_field' => 'no',
'notify_emails' => get_option( 'admin_email' ),
'email_link_emails' => array( 'customer_processing_order', 'customer_completed_order' ),
'exclusion_mode' => 'soft',
'exclusion_meta_keys' => '',
'exclusion_label' => 'no',
'accent_color' => '#111111',
'radius' => '8',
'retention_years' => 5,
'delete_data' => 'no',
);
}
/**
* All defaults incl. translated texts (call after `init`).
*
* @return array
*/
public static function defaults() {
if ( null === self::$defaults ) {
self::$defaults = array_merge(
self::base_defaults(),
array(
'text_intro' => __( 'You can withdraw from a distance contract within 14 days without giving any reason. Find your order, choose the products and confirm – you will receive a confirmation by email straight away.', 'otkazpro' ),
'text_start_button' => __( 'Withdraw from the contract here', 'otkazpro' ),
'text_confirm_button' => __( 'Confirm withdrawal', 'otkazpro' ),
'text_statement' => __( 'I hereby give notice that I withdraw from the contract for the following goods: {items}, order No. {order_number}, submitted on {date} at {time}.', 'otkazpro' ),
)
);
}
return self::$defaults;
}
/**
* Drop cached (translated) defaults when the locale changes.
*/
public static function reset_defaults() {
self::$defaults = null;
}
/**
* Default for one key; translated texts are built only when needed.
*
* @param string $key Key.
* @return mixed
*/
private static function default_for( $key ) {
$base = self::base_defaults();
if ( array_key_exists( $key, $base ) ) {
return $base[ $key ];
}
$all = self::defaults();
return isset( $all[ $key ] ) ? $all[ $key ] : null;
}
/**
* Get a raw setting value (with default).
*
* @param string $key Setting key without prefix.
* @return mixed
*/
public static function get( $key ) {
$value = get_option( self::PREFIX . $key, null );
if ( null === $value ) {
$value = self::default_for( $key );
} elseif ( in_array( $key, self::REQUIRED_TEXTS, true ) && '' === trim( (string) $value ) ) {
$value = self::default_for( $key );
}
return apply_filters( 'py_otkaz_setting_' . $key, $value );
}
/**
* Whether a stored option exists (i.e. merchant saved it).
*
* @param string $key Key.
* @return bool
*/
public static function is_stored( $key ) {
return null !== get_option( self::PREFIX . $key, null );
}
/**
* Get a yes/no setting as bool.
*
* @param string $key Key.
* @return bool
*/
public static function enabled( $key ) {
return 'yes' === self::get( $key );
}
/**
* Get a translatable text setting, translated via WPML/Polylang when stored.
*
* @param string $key Key.
* @param string|null $lang Language code or null for current.
* @return string
*/
public static function text( $key, $lang = null ) {
$value = (string) self::get( $key );
if ( self::is_stored( $key ) && '' !== $value ) {
$value = I18n::translate( $key, $value, $lang );
}
return $value;
}
/**
* Settings tab URL.
*
* @return string
*/
public static function url() {
return admin_url( 'admin.php?page=wc-settings&tab=' . self::TAB );
}
/**
* Register the tab.
*
* @param array $tabs Tabs.
* @return array
*/
public static function add_tab( $tabs ) {
$tabs[ self::TAB ] = __( 'Withdrawal', 'otkazpro' );
return $tabs;
}
/**
* Render the tab.
*/
public static function output() {
// Defaults must be in the site language, not the admin user's profile language.
$switched = switch_to_locale( get_locale() );
woocommerce_admin_fields( self::fields() );
if ( $switched ) {
restore_previous_locale();
}
}
/**
* Save the tab.
*/
public static function save() {
$switched = switch_to_locale( get_locale() );
woocommerce_update_options( self::fields() );
if ( $switched ) {
restore_previous_locale();
}
$color = sanitize_hex_color( (string) get_option( self::PREFIX . 'accent_color' ) );
update_option( self::PREFIX . 'accent_color', $color ? $color : '#111111' );
$days = absint( get_option( self::PREFIX . 'deadline_days' ) );
update_option( self::PREFIX . 'deadline_days', $days > 0 ? $days : 14 );
$emails = array_filter( array_map( 'sanitize_email', array_map( 'trim', explode( ',', (string) get_option( self::PREFIX . 'notify_emails' ) ) ) ), 'is_email' );
update_option( self::PREFIX . 'notify_emails', implode( ', ', $emails ) );
self::$defaults = null;
I18n::register_strings();
Cache_Check::schedule_soon();
Install::backup_settings();
if ( class_exists( Order_Status::class ) ) {
// Custom status visibility may have changed.
wp_cache_delete( 'wc_order_statuses', 'woocommerce' );
}
}
/**
* Read-only info row with the form page URL.
*
* @param array $value Field definition.
*/
public static function field_info( $value ) {
$page_id = (int) self::get( 'page_id' );
$url = $page_id ? get_permalink( $page_id ) : '';
?>
|
|
'#00691c',
'recommended' => '#8a4b00',
'critical' => '#b32d2e',
'unknown' => '#50575e',
);
$labels = array(
'good' => __( 'OK', 'otkazpro' ),
'recommended' => __( 'Cached', 'otkazpro' ),
'critical' => __( 'Critical', 'otkazpro' ),
'unknown' => __( 'Not checked', 'otkazpro' ),
);
?>
|
|
__( 'Processing order', 'otkazpro' ),
'customer_completed_order' => __( 'Completed order', 'otkazpro' ),
'customer_on_hold_order' => __( 'Order on-hold', 'otkazpro' ),
'customer_invoice' => __( 'Order details / invoice', 'otkazpro' ),
);
$fields = array(
// General.
array(
'title' => __( 'General', 'otkazpro' ),
'type' => 'title',
'desc' => __( 'Online withdrawal function under Art. 52a of the Consumer Protection Act. The function stays available for eligible orders at all times – the 14-day deadline is shown only to you in the admin.', 'otkazpro' ),
'id' => 'py_otkaz_general',
),
array(
'title' => __( 'Form page', 'otkazpro' ),
'id' => self::PREFIX . 'page_id',
'type' => 'single_select_page',
'default' => '',
'class' => 'wc-enhanced-select-nostd',
'css' => 'min-width:300px;',
'desc_tip' => __( 'Page containing the [otkazpro] shortcode. Created automatically on activation.', 'otkazpro' ),
'autoload' => true,
),
array(
'title' => __( 'Form URL', 'otkazpro' ),
'type' => 'py_otkaz_info',
'id' => 'py_otkaz_info_url',
'is_option' => false,
),
array(
'title' => __( 'Page cache check', 'otkazpro' ),
'type' => 'py_otkaz_cache',
'id' => 'py_otkaz_cache_status',
'is_option' => false,
),
array(
'title' => __( 'Eligible order statuses', 'otkazpro' ),
'id' => self::PREFIX . 'statuses',
'type' => 'multiselect',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'options' => $statuses,
'default' => $d['statuses'],
'desc_tip' => __( 'Orders in these statuses can be withdrawn from. Do not add cancelled, refunded, failed or pending payment.', 'otkazpro' ),
),
array(
'title' => __( 'Withdrawal period (days)', 'otkazpro' ),
'id' => self::PREFIX . 'deadline_days',
'type' => 'number',
'default' => $d['deadline_days'],
'css' => 'width:80px;',
'custom_attributes' => array(
'min' => 1,
'step' => 1,
),
'desc' => __( 'Used only for the admin “in time / late” indicator (counted from the completed date). The form never closes automatically.', 'otkazpro' ),
),
array(
'title' => __( '“Withdrawal requested” status', 'otkazpro' ),
'id' => self::PREFIX . 'custom_status',
'type' => 'checkbox',
'default' => $d['custom_status'],
'desc' => __( 'Move the order to a custom “Withdrawal requested” status after each submission.', 'otkazpro' ),
'desc_tip' => __( 'Off by default: changing the status may interfere with invoicing, ERP or courier integrations that depend on order status.', 'otkazpro' ),
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_general',
),
// Texts.
array(
'title' => __( 'Texts', 'otkazpro' ),
'type' => 'title',
'desc' => __( 'With WPML / Polylang these texts can be translated in String Translation (domain “otkazpro”). Keep the legally required wording of the two buttons.', 'otkazpro' ),
'id' => 'py_otkaz_texts',
),
array(
'title' => __( 'Intro text', 'otkazpro' ),
'id' => self::PREFIX . 'text_intro',
'type' => 'textarea',
'css' => 'min-width:420px;height:70px;',
'default' => $d['text_intro'],
),
array(
'title' => __( 'Start button', 'otkazpro' ),
'id' => self::PREFIX . 'text_start_button',
'type' => 'text',
'css' => 'min-width:420px;',
'default' => $d['text_start_button'],
),
array(
'title' => __( 'Confirmation button', 'otkazpro' ),
'id' => self::PREFIX . 'text_confirm_button',
'type' => 'text',
'css' => 'min-width:420px;',
'default' => $d['text_confirm_button'],
),
array(
'title' => __( 'Statement template', 'otkazpro' ),
'id' => self::PREFIX . 'text_statement',
'type' => 'textarea',
'css' => 'min-width:420px;height:80px;',
'default' => $d['text_statement'],
'desc' => __( 'Placeholders: {items}, {order_number}, {order_date}, {date}, {time}, {name}, {email}, {site_title}.', 'otkazpro' ),
),
array(
'title' => __( 'Return instructions', 'otkazpro' ),
'id' => self::PREFIX . 'text_return_instructions',
'type' => 'textarea',
'css' => 'min-width:420px;height:100px;',
'default' => '',
'desc' => __( 'Where and how to return the goods. Shown on the success screen and in the confirmation email. Basic HTML allowed.', 'otkazpro' ),
),
array(
'title' => __( 'Optional “Reason” field', 'otkazpro' ),
'id' => self::PREFIX . 'reason_field',
'type' => 'checkbox',
'default' => $d['reason_field'],
'desc' => __( 'Show an optional free-text reason field (never required).', 'otkazpro' ),
'desc_tip' => __( 'Off by default: the law does not provide for asking a reason. Enable only after legal review.', 'otkazpro' ),
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_texts',
),
// Emails.
array(
'title' => __( 'Emails', 'otkazpro' ),
'type' => 'title',
'desc' => sprintf(
/* translators: %s: link to WooCommerce emails settings */
__( 'Subjects, headings and design of the two emails are edited in %s.', 'otkazpro' ),
'' . esc_html__( 'WooCommerce → Settings → Emails', 'otkazpro' ) . ''
),
'id' => 'py_otkaz_emails',
),
array(
'title' => __( 'Store notification email(s)', 'otkazpro' ),
'id' => self::PREFIX . 'notify_emails',
'type' => 'text',
'css' => 'min-width:420px;',
'default' => $d['notify_emails'],
'desc_tip' => __( 'Comma-separated. Receives the “New withdrawal” notification.', 'otkazpro' ),
),
array(
'title' => __( 'Link in order emails', 'otkazpro' ),
'id' => self::PREFIX . 'email_link_emails',
'type' => 'multiselect',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'options' => $order_emails,
'default' => $d['email_link_emails'],
'desc' => __( 'Adds a “Withdraw from the contract here” link (works without login) to these customer emails.', 'otkazpro' ),
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_emails',
),
// Exclusions.
array(
'title' => __( 'Products without right of withdrawal', 'otkazpro' ),
'type' => 'title',
'desc' => __( 'Mark products in the product / variation / category edit screens. Do not mark goods that lose the right only once unsealed (hygiene, cosmetics, sealed media).', 'otkazpro' ),
'id' => 'py_otkaz_exclusions',
),
array(
'title' => __( 'Mode', 'otkazpro' ),
'id' => self::PREFIX . 'exclusion_mode',
'type' => 'select',
'class' => 'wc-enhanced-select',
'options' => array(
'soft' => __( 'Soft – selectable with a warning, flagged for you', 'otkazpro' ),
'strict' => __( 'Strict – cannot be selected', 'otkazpro' ),
),
'default' => $d['exclusion_mode'],
),
array(
'title' => __( 'Personalisation meta keys', 'otkazpro' ),
'id' => self::PREFIX . 'exclusion_meta_keys',
'type' => 'textarea',
'css' => 'min-width:420px;height:70px;',
'default' => '',
'desc' => __( 'One per line. An order item with a non-empty value for any of these meta keys (e.g. engraving text from an add-ons plugin) is treated as personalised.', 'otkazpro' ),
),
array(
'title' => __( 'Storefront label', 'otkazpro' ),
'id' => self::PREFIX . 'exclusion_label',
'type' => 'checkbox',
'default' => $d['exclusion_label'],
'desc' => __( 'Show “No right of withdrawal” on the product page and next to the item in cart / checkout.', 'otkazpro' ),
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_exclusions',
),
// Appearance.
array(
'title' => __( 'Appearance', 'otkazpro' ),
'type' => 'title',
'desc' => __( 'The form inherits the theme font. The accent colour is used only for primary buttons, focus rings, checkboxes, links and the active step; button text colour is chosen automatically for contrast.', 'otkazpro' ),
'id' => 'py_otkaz_look',
),
array(
'title' => __( 'Accent colour', 'otkazpro' ),
'id' => self::PREFIX . 'accent_color',
'type' => 'color',
'css' => 'width:6em;',
'default' => $d['accent_color'],
),
array(
'title' => __( 'Corner radius', 'otkazpro' ),
'id' => self::PREFIX . 'radius',
'type' => 'select',
'options' => array(
'0' => __( 'Sharp (0)', 'otkazpro' ),
'4' => __( 'Slight (4px)', 'otkazpro' ),
'8' => __( 'Rounded (8px)', 'otkazpro' ),
'999' => __( 'Pill buttons', 'otkazpro' ),
),
'default' => $d['radius'],
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_look',
),
// Privacy.
array(
'title' => __( 'Data retention', 'otkazpro' ),
'type' => 'title',
'id' => 'py_otkaz_privacy',
),
array(
'title' => __( 'Keep records (years)', 'otkazpro' ),
'id' => self::PREFIX . 'retention_years',
'type' => 'number',
'css' => 'width:80px;',
'default' => $d['retention_years'],
'custom_attributes' => array(
'min' => 0,
'step' => 1,
),
'desc' => __( 'Withdrawal records (incl. IP and user agent as evidence) older than this are deleted automatically. 0 = keep forever.', 'otkazpro' ),
),
array(
'title' => __( 'Delete data on uninstall', 'otkazpro' ),
'id' => self::PREFIX . 'delete_data',
'type' => 'checkbox',
'default' => $d['delete_data'],
'desc' => __( 'Also delete all withdrawal records when the plugin is deleted. Off = records are kept as evidence.', 'otkazpro' ),
),
array(
'type' => 'sectionend',
'id' => 'py_otkaz_privacy',
),
);
return apply_filters( 'py_otkaz_settings_fields', $fields );
}
}