PluginProbe
Ebook Store / 6.25
Ebook Store v6.25
6.25 6.24 6.23 6.22 6.21 6.20 trunk
ebook-store / settings-General.php

settings-General.php in Ebook Store 6.25, at settings-General.php

603 lines 24.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ebook Store - "General" settings tab.
4 *
5 * Rendered inside the settings <form> of ebook_options.php, within a plain
6 * <div class="ebook-settings-panel">. This file must therefore never emit table
7 * markup; it prints <section> blocks through the shared settings-UI helpers.
8 *
9 * PHP 7.4 compatible.
10 *
11 * @package EbookStore
12 */
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 settings_fields( 'ebook-settings-group-general' );
19 do_settings_sections( 'ebook-settings-group-general' );
20
21 /*
22 * ---------------------------------------------------------------------------
23 * Saved values.
24 * ---------------------------------------------------------------------------
25 * $op is the QSWPOptions defaults object created by ebook_options.php. It is
26 * read defensively so this tab still renders if it is ever included on its own.
27 */
28 $default_locale = isset( $op->ebook_store_locale ) ? (string) $op->ebook_store_locale : '';
29 $default_expiration = isset( $op->link_expiration ) ? (string) $op->link_expiration : '1 year';
30 $default_email_flag = isset( $op->email_delivery ) ? (string) $op->email_delivery : '';
31 $default_download_cap = isset( $op->downloads_limit ) ? (string) $op->downloads_limit : '5';
32 $default_vat_percent = isset( $op->vat_percent ) ? (string) $op->vat_percent : '0';
33 $default_form_scale = isset( $op->form_scale ) ? (string) $op->form_scale : '1';
34
35 $license_key = function_exists( 'ebook_store_get_license_key' )
36 ? ebook_store_get_license_key()
37 : trim( (string) get_option( 'ebook_store_license_key', '' ) );
38
39 $license_summary = function_exists( 'ebook_store_get_license_summary' )
40 ? ebook_store_get_license_summary()
41 : array();
42
43 $license_summary = wp_parse_args(
44 is_array( $license_summary ) ? $license_summary : array(),
45 array(
46 'label' => __( 'Free version', 'ebook-store' ),
47 'tone' => 'muted',
48 'message' => __( 'Enter your license key to unlock the Pro features.', 'ebook-store' ),
49 )
50 );
51
52 $selected_locale = (string) get_option( 'ebook_store_locale', $default_locale );
53 $link_expiration = trim( (string) get_option( 'link_expiration', $default_expiration ) );
54 $link_expiration = $link_expiration !== '' ? $link_expiration : '1 year';
55 $downloads_limit = function_exists( 'ebook_store_get_download_limit' )
56 ? (string) ebook_store_get_download_limit()
57 : (string) get_option( 'downloads_limit', $default_download_cap );
58 $email_delivery_enabled = (string) get_option( 'email_delivery', $default_email_flag );
59 $attach_files_enabled = (string) get_option( 'attach_files', '' );
60 $vat_percent = function_exists( 'ebook_store_get_vat_percent' )
61 ? (string) ebook_store_get_vat_percent()
62 : (string) get_option( 'vat_percent', $default_vat_percent );
63 $current_currency = function_exists( 'ebook_store_get_store_currency' )
64 ? ebook_store_get_store_currency()
65 : (string) get_option( 'paypal_currency', 'USD' );
66 $form_scale = trim( (string) get_option( 'form_scale', $default_form_scale ) );
67 $form_scale = $form_scale !== '' ? $form_scale : '1';
68 $default_form_template = function_exists( 'ebook_store_resolve_form_template' )
69 ? ebook_store_resolve_form_template( (string) get_option( 'ebook_store_form_template', '2026' ) )
70 : (string) get_option( 'ebook_store_form_template', '2026' );
71
72 /*
73 * ---------------------------------------------------------------------------
74 * Option lists.
75 * ---------------------------------------------------------------------------
76 */
77 $form_templates = function_exists( 'ebook_store_get_form_templates' ) ? ebook_store_get_form_templates() : array();
78 $template_choices = array();
79 foreach ( (array) $form_templates as $template_key => $template_config ) {
80 $template_choices[ $template_key ] = is_array( $template_config ) && isset( $template_config['label'] )
81 ? $template_config['label']
82 : (string) $template_key;
83 }
84 if ( empty( $template_choices ) ) {
85 $template_choices = array( '2026' => __( '2026', 'ebook-store' ) );
86 }
87
88 // The full label list is used here (not only installed language packs) so an
89 // already-saved language never disappears from the list after an update.
90 if ( ! isset( $languages ) || ! is_array( $languages ) || empty( $languages ) ) {
91 $languages = function_exists( 'ebook_store_get_active_languages' )
92 ? ebook_store_get_active_languages()
93 : array( 0 => __( 'Default', 'ebook-store' ) );
94 }
95
96 $common_currencies = array(
97 'USD' => __( 'US Dollar', 'ebook-store' ),
98 'EUR' => __( 'Euro', 'ebook-store' ),
99 'GBP' => __( 'British Pound', 'ebook-store' ),
100 'AUD' => __( 'Australian Dollar', 'ebook-store' ),
101 'CAD' => __( 'Canadian Dollar', 'ebook-store' ),
102 'JPY' => __( 'Japanese Yen', 'ebook-store' ),
103 'NZD' => __( 'New Zealand Dollar', 'ebook-store' ),
104 'CHF' => __( 'Swiss Franc', 'ebook-store' ),
105 'HKD' => __( 'Hong Kong Dollar', 'ebook-store' ),
106 'SGD' => __( 'Singapore Dollar', 'ebook-store' ),
107 'SEK' => __( 'Swedish Krona', 'ebook-store' ),
108 'DKK' => __( 'Danish Krone', 'ebook-store' ),
109 'NOK' => __( 'Norwegian Krone', 'ebook-store' ),
110 'MXN' => __( 'Mexican Peso', 'ebook-store' ),
111 'PLN' => __( 'Polish Zloty', 'ebook-store' ),
112 'BRL' => __( 'Brazilian Real', 'ebook-store' ),
113 );
114
115 $additional_currencies = array(
116 'CZK' => __( 'Czech Koruna', 'ebook-store' ),
117 'ILS' => __( 'Israeli New Sheqel', 'ebook-store' ),
118 'HUF' => __( 'Hungarian Forint', 'ebook-store' ),
119 'MYR' => __( 'Malaysian Ringgit', 'ebook-store' ),
120 'PHP' => __( 'Philippine Peso', 'ebook-store' ),
121 'TWD' => __( 'Taiwan New Dollar', 'ebook-store' ),
122 'THB' => __( 'Thai Baht', 'ebook-store' ),
123 'INR' => __( 'Indian Rupee', 'ebook-store' ),
124 'TRY' => __( 'Turkish Lira', 'ebook-store' ),
125 'ZAR' => __( 'South African Rand', 'ebook-store' ),
126 'NGN' => __( 'Nigerian Naira', 'ebook-store' ),
127 'RUB' => __( 'Russian Ruble', 'ebook-store' ),
128 'RON' => __( 'Romanian Leu', 'ebook-store' ),
129 );
130
131 if ( $current_currency !== '' && ! isset( $common_currencies[ $current_currency ] ) && ! isset( $additional_currencies[ $current_currency ] ) ) {
132 $additional_currencies[ $current_currency ] = $current_currency;
133 }
134
135 $currency_groups = array();
136 foreach ( $common_currencies as $code => $label ) {
137 /* translators: 1: currency name, 2: three-letter currency code. */
138 $currency_groups[ __( 'Common currencies', 'ebook-store' ) ][ $code ] = sprintf( __( '%1$s (%2$s)', 'ebook-store' ), $label, $code );
139 }
140 foreach ( $additional_currencies as $code => $label ) {
141 /* translators: 1: currency name, 2: three-letter currency code. */
142 $currency_groups[ __( 'Additional currencies', 'ebook-store' ) ][ $code ] = sprintf( __( '%1$s (%2$s)', 'ebook-store' ), $label, $code );
143 }
144
145 /*
146 * ---------------------------------------------------------------------------
147 * Live preview of the download-link expiry.
148 * ---------------------------------------------------------------------------
149 * The sanitiser accepts any non-empty text, and functions.php feeds the value
150 * to strtotime( '+' . $value ). If that fails the comparison is made against
151 * false, so every download link is treated as already expired. Showing the
152 * resulting date here is the only way a shop owner can spot a typo.
153 */
154 $expiration_expression = ltrim( $link_expiration, '+ ' );
155 $expiration_timestamp = $expiration_expression !== '' ? strtotime( '+' . $expiration_expression, time() ) : false;
156 $expiration_is_valid = ( is_int( $expiration_timestamp ) && $expiration_timestamp > time() );
157
158 if ( $expiration_is_valid ) {
159 $expiration_note = sprintf(
160 /* translators: %s: formatted date, e.g. 19 July 2027. */
161 __( 'A link bought today would keep working until %s.', 'ebook-store' ),
162 '<strong>' . esc_html( date_i18n( get_option( 'date_format' ), $expiration_timestamp ) ) . '</strong>'
163 );
164 $expiration_note_tone = 'success';
165 } else {
166 $expiration_note = sprintf(
167 /* translators: %s: the value the shop owner typed, shown in code style. */
168 __( 'WordPress cannot read %s as a length of time, so every download link would be treated as already expired and buyers would see an error instead of their file. Use wording such as 1 year, 6 months, 30 days or 48 hours.', 'ebook-store' ),
169 '<code>' . esc_html( $link_expiration ) . '</code>'
170 );
171 $expiration_note_tone = 'error';
172 }
173
174 /*
175 * ---------------------------------------------------------------------------
176 * Cross-tab links.
177 * ---------------------------------------------------------------------------
178 */
179 if ( function_exists( 'ebook_store_get_settings_tab_url' ) ) {
180 $templates_tab_url = ebook_store_get_settings_tab_url( 'Templates' );
181 $paypal_tab_url = ebook_store_get_settings_tab_url( 'PayPal' );
182 } else {
183 $templates_tab_url = admin_url( 'options-general.php?page=ebook_options.php&tab=Templates' );
184 $paypal_tab_url = admin_url( 'options-general.php?page=ebook_options.php&tab=PayPal' );
185 }
186
187 $license_recheck_url = wp_nonce_url(
188 admin_url( 'admin-post.php?action=ebook_store_license_recheck' ),
189 'ebook_store_license_recheck'
190 );
191
192 /*
193 * ---------------------------------------------------------------------------
194 * Hero.
195 * ---------------------------------------------------------------------------
196 */
197 $currency_label = isset( $common_currencies[ $current_currency ] )
198 ? $common_currencies[ $current_currency ]
199 : ( isset( $additional_currencies[ $current_currency ] ) ? $additional_currencies[ $current_currency ] : $current_currency );
200
201 $hero_aside = function () use ( $license_summary, $email_delivery_enabled, $attach_files_enabled, $expiration_is_valid, $link_expiration, $current_currency, $currency_label, $vat_percent ) {
202 $vat_number = (float) $vat_percent;
203 ?>
204 <aside class="ebook-settings-status-card">
205 <h4><?php echo esc_html__( 'How your store behaves right now', 'ebook-store' ); ?></h4>
206 <ul class="ebook-settings-status-list">
207 <?php
208 ebook_store_status_row(
209 __( 'License', 'ebook-store' ),
210 $license_summary['tone'],
211 $license_summary['message']
212 );
213
214 ebook_store_status_row(
215 __( 'Order confirmation emails', 'ebook-store' ),
216 $email_delivery_enabled === '1' ? 'ok' : 'warn',
217 $email_delivery_enabled === '1'
218 ? ( $attach_files_enabled === '1'
219 ? __( 'Buyers are emailed their download links, with the ebook attached.', 'ebook-store' )
220 : __( 'Buyers are emailed their download links after paying.', 'ebook-store' ) )
221 : __( 'Turned off. Buyers only get their download links on the thank-you page.', 'ebook-store' )
222 );
223
224 ebook_store_status_row(
225 __( 'Download links', 'ebook-store' ),
226 $expiration_is_valid ? 'ok' : 'error',
227 $expiration_is_valid
228 ? sprintf(
229 /* translators: %s: a length of time, e.g. 1 year. */
230 __( 'Each link stays usable for %s after the order.', 'ebook-store' ),
231 $link_expiration
232 )
233 : __( 'The expiry wording below cannot be understood, so links stop working immediately.', 'ebook-store' )
234 );
235
236 ebook_store_status_row(
237 __( 'Pricing', 'ebook-store' ),
238 'ok',
239 $vat_number > 0
240 ? sprintf(
241 /* translators: 1: currency name, 2: three-letter code, 3: tax percentage. */
242 __( 'Charged in %1$s (%2$s), plus %3$s%% tax at checkout.', 'ebook-store' ),
243 $currency_label,
244 $current_currency,
245 rtrim( rtrim( number_format( $vat_number, 2, '.', '' ), '0' ), '.' )
246 )
247 : sprintf(
248 /* translators: 1: currency name, 2: three-letter code. */
249 __( 'Charged in %1$s (%2$s), with no tax added.', 'ebook-store' ),
250 $currency_label,
251 $current_currency
252 )
253 );
254 ?>
255 </ul>
256 </aside>
257 <?php
258 };
259
260 ebook_store_render_hero(
261 array(
262 'eyebrow' => __( 'General settings', 'ebook-store' ),
263 'title' => __( 'The basics every store needs', 'ebook-store' ),
264 'intro' => __( 'Your license, the language buyers see, how long download links last, what happens by email after a sale, and the currency and tax you charge.', 'ebook-store' ),
265 'aside' => $hero_aside,
266 )
267 );
268
269 ebook_store_pro_banner( __( 'The store language selector', 'ebook-store' ) );
270
271 /*
272 * ---------------------------------------------------------------------------
273 * Section: license.
274 * ---------------------------------------------------------------------------
275 */
276 ebook_store_section_open(
277 array(
278 'eyebrow' => __( 'Setup', 'ebook-store' ),
279 'title' => __( 'License & Pro features', 'ebook-store' ),
280 'intro' => __( 'A license unlocks the Pro features and keeps them working after updates. The free features on this page work either way.', 'ebook-store' ),
281 'id' => 'ebook-settings-license',
282 )
283 );
284
285 ebook_store_split_open();
286 ?>
287
288 <div class="ebook-settings-field"
289 data-ebook-search="<?php echo esc_attr( __( 'License key or purchase email', 'ebook-store' ) . ' ' . __( 'Pro license', 'ebook-store' ) . ' ebook_store_license_key' ); ?>">
290 <label for="ebook_store_license_key"><?php echo esc_html__( 'License key or purchase email', 'ebook-store' ); ?></label>
291 <input
292 id="ebook_store_license_key"
293 class="ebook-settings-monospace-input"
294 type="text"
295 name="ebook_store_license_key"
296 value="<?php echo esc_attr( $license_key ); ?>"
297 placeholder="<?php echo esc_attr__( 'Paste your license key, or the email you paid with', 'ebook-store' ); ?>"
298 autocomplete="off"
299 spellcheck="false"
300 />
301 <p class="description">
302 <?php echo esc_html__( 'Paste the key from your receipt, or leave empty to keep running the free version.', 'ebook-store' ); ?>
303 </p>
304 <details class="ebook-settings-hint">
305 <summary><?php echo esc_html__( 'Details', 'ebook-store' ); ?></summary>
306 <div class="ebook-settings-hint__body">
307 <?php echo esc_html__( 'For older purchases there is no key: use the PayPal email address the order was paid with instead. Both are accepted here, so this box does not force an email format.', 'ebook-store' ); ?>
308 </div>
309 </details>
310 </div>
311
312 <?php
313 ebook_store_split_aside();
314 ?>
315
316 <ul class="ebook-settings-status-list">
317 <?php
318 ebook_store_status_row(
319 $license_summary['label'],
320 $license_summary['tone'],
321 $license_summary['message']
322 );
323 ?>
324 </ul>
325
326 <p class="ebook-settings-field__actions">
327 <a class="button" href="<?php echo esc_url( $license_recheck_url ); ?>">
328 <?php echo esc_html__( 'Check my license again', 'ebook-store' ); ?>
329 </a>
330 </p>
331 <p class="description">
332 <?php echo esc_html__( 'Rechecks daily on its own. Save this page before checking, or unsaved edits are lost.', 'ebook-store' ); ?>
333 </p>
334 <p class="description">
335 <?php echo esc_html__( 'If the licensing server is unreachable your Pro features are never switched off — only when it explicitly rejects the key.', 'ebook-store' ); ?>
336 </p>
337
338 <?php
339 if ( isset( $_GET['ebook_license_checked'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only redirect flag.
340 $checked_state = sanitize_key( wp_unslash( $_GET['ebook_license_checked'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
341 if ( $checked_state === 'valid' ) {
342 $checked_tone = 'is-success';
343 $checked_text = __( 'Checked just now: confirmed by the licensing server. Pro features are unlocked.', 'ebook-store' );
344 } elseif ( $checked_state === 'invalid' ) {
345 $checked_tone = 'is-warning';
346 $checked_text = __( 'Checked just now: not recognised. Check for stray spaces, or try the email you paid with, then contact support.', 'ebook-store' );
347 } elseif ( $checked_state === 'empty' ) {
348 $checked_tone = 'is-info';
349 $checked_text = __( 'Nothing to check yet. Enter your license and save the page first.', 'ebook-store' );
350 } else {
351 $checked_tone = 'is-info';
352 $checked_text = __( 'Checked just now: the licensing server could not be reached. Nothing is switched off and the plugin retries on its own.', 'ebook-store' );
353 }
354 printf(
355 '<div class="ebook-settings-inline-note %1$s">%2$s</div>',
356 esc_attr( $checked_tone ),
357 esc_html( $checked_text )
358 );
359 }
360
361 ebook_store_split_close();
362 ebook_store_section_close();
363
364 /*
365 * ---------------------------------------------------------------------------
366 * Card 2: Storefront & delivery.
367 * ---------------------------------------------------------------------------
368 * Three former sections (language / download access / order emails) merged into
369 * one dense card, kept navigable by three labelled bands so no row is half-empty
370 * and the wayfinding a shop owner scans for ("how long do links last?") survives.
371 */
372 ebook_store_section_open(
373 array(
374 'eyebrow' => __( 'Storefront', 'ebook-store' ),
375 'title' => __( 'Storefront & delivery', 'ebook-store' ),
376 'intro' => __( 'The language buyers see, how their download links behave, and what they receive by email after paying.', 'ebook-store' ),
377 'id' => 'ebook-settings-storefront',
378 )
379 );
380
381 ebook_store_subhead( __( 'Storefront', 'ebook-store' ) );
382 ebook_store_grid_open( 2 );
383
384 ebook_store_field(
385 array(
386 'type' => 'select',
387 'name' => 'ebook_store_locale',
388 'id' => 'ebook_store_locale',
389 'label' => __( 'Store display language', 'ebook-store' ),
390 'value' => $selected_locale,
391 'options' => $languages,
392 'pro' => true,
393 'help' => __( 'Leave on Default so the store follows your WordPress language. Pick a specific language only to pin the store while the rest of the site differs.', 'ebook-store' ),
394 'hint' => __( 'Default means the store speaks whatever language WordPress itself is set to, which is what almost every shop wants. This does not translate your own ebook titles or descriptions.', 'ebook-store' ),
395 )
396 );
397
398 ebook_store_field(
399 array(
400 'type' => 'select',
401 'name' => 'ebook_store_form_template',
402 'id' => 'ebook_store_form_template',
403 'label' => __( 'Default order form design', 'ebook-store' ),
404 'value' => $default_form_template,
405 'options' => $template_choices,
406 'help' => sprintf(
407 /* translators: %s: link to the Templates settings tab. */
408 __( 'Used by new ebooks and by shortcodes with no design chosen. 2026 is recommended. Wording lives in the %s.', 'ebook-store' ),
409 '<a href="' . esc_url( $templates_tab_url ) . '">' . esc_html__( 'Templates tab', 'ebook-store' ) . '</a>'
410 ),
411 'hint' => __( 'Existing ebooks keep whatever design you already picked for them; this only sets the starting point for new titles.', 'ebook-store' ),
412 )
413 );
414
415 ebook_store_grid_close();
416
417 ebook_store_subhead( __( 'Downloads', 'ebook-store' ) );
418 ebook_store_grid_open( 2 );
419
420 ebook_store_field(
421 array(
422 'type' => 'text',
423 'name' => 'link_expiration',
424 'id' => 'link_expiration',
425 'label' => __( 'Download links stop working after', 'ebook-store' ),
426 'value' => $link_expiration,
427 'placeholder' => __( '1 year', 'ebook-store' ),
428 'help' => __( 'Counted from the order. Write a plain length of time: 1 year, 6 months, 30 days, 48 hours. Empty saves 1 year.', 'ebook-store' ),
429 'note' => $expiration_note,
430 'note_tone' => $expiration_note_tone,
431 )
432 );
433
434 ebook_store_field(
435 array(
436 'type' => 'number',
437 'name' => 'downloads_limit',
438 'id' => 'downloads_limit',
439 'label' => __( 'Downloads allowed per order', 'ebook-store' ),
440 'value' => $downloads_limit,
441 'placeholder' => '5',
442 'min' => 1,
443 'step' => 1,
444 'help' => __( 'Retries for an interrupted download or a second device. Five is a sensible start; once used up the link stops working even before it expires. Empty saves 5.', 'ebook-store' ),
445 )
446 );
447
448 ebook_store_grid_close();
449
450 ebook_store_subhead( __( 'After payment', 'ebook-store' ) );
451 ebook_store_toggle_grid_open();
452
453 ebook_store_toggle(
454 array(
455 'name' => 'email_delivery',
456 'id' => 'email_delivery',
457 'title' => __( 'Email the order confirmation and download links', 'ebook-store' ),
458 'description' => __( 'Strongly recommended. Without it, a buyer who closes the thank-you page has no way back to their files.', 'ebook-store' ),
459 'checked' => $email_delivery_enabled,
460 )
461 );
462
463 ebook_store_toggle(
464 array(
465 'name' => 'attach_files',
466 'id' => 'attach_files',
467 'title' => __( 'Also attach the ebook file to that email', 'ebook-store' ),
468 'description' => __( 'Convenient for small files. Large attachments make emails slow, and many mail providers reject anything over a few megabytes.', 'ebook-store' ),
469 'checked' => $attach_files_enabled,
470 )
471 );
472
473 ebook_store_toggle_grid_close();
474
475 ebook_store_callout(
476 __( 'WordPress sends mail through your web server by default, and shop emails sent that way often land in spam. Installing an SMTP plugin and sending through a real mailbox is the single best way to make sure buyers actually receive their downloads.', 'ebook-store' ),
477 'warning',
478 __( 'Deliverability:', 'ebook-store' )
479 );
480
481 ebook_store_callout(
482 sprintf(
483 /* translators: %s: link to the Templates settings tab. */
484 __( 'The subject line and wording of this email are edited in the %s. Attachments only go out when the confirmation email above is switched on.', 'ebook-store' ),
485 '<a href="' . esc_url( $templates_tab_url ) . '">' . esc_html__( 'Templates tab', 'ebook-store' ) . '</a>'
486 ),
487 'info',
488 __( 'Where the wording lives:', 'ebook-store' )
489 );
490
491 ebook_store_advanced_open();
492
493 ebook_store_field(
494 array(
495 'type' => 'number',
496 'name' => 'form_scale',
497 'id' => 'form_scale',
498 'label' => __( 'Order form size', 'ebook-store' ),
499 'value' => $form_scale,
500 'placeholder' => '1',
501 'min' => '0.1',
502 'step' => '0.05',
503 'help' => __( 'Scales the whole order form on your site. 1 is normal, 0.8 smaller, 1.2 larger.', 'ebook-store' ),
504 'hint' => __( 'Useful when the form does not quite fit your theme\'s column width. Leave it at 1 unless you have a sizing problem.', 'ebook-store' ),
505 )
506 );
507
508 ebook_store_advanced_close();
509
510 ebook_store_section_close();
511
512 /*
513 * ---------------------------------------------------------------------------
514 * Section: tax and currency.
515 * ---------------------------------------------------------------------------
516 */
517 ebook_store_section_open(
518 array(
519 'eyebrow' => __( 'Pricing', 'ebook-store' ),
520 'title' => __( 'Tax & currency', 'ebook-store' ),
521 'intro' => __( 'The money side: which currency buyers are charged in, and whether tax is added on top of your prices.', 'ebook-store' ),
522 'id' => 'ebook-settings-pricing',
523 )
524 );
525
526 ebook_store_grid_open( 2 );
527
528 ebook_store_field(
529 array(
530 'type' => 'number',
531 'name' => 'vat_percent',
532 'id' => 'vat_percent',
533 'label' => __( 'Tax added at checkout (%)', 'ebook-store' ),
534 'value' => $vat_percent,
535 'placeholder' => '0',
536 'min' => 0,
537 'step' => '0.01',
538 'help' => __( 'Enter 0 if prices already include tax or you charge none. Enter 20 to add twenty percent at checkout. One rate only — no per-country rates. Negative values save as 0.', 'ebook-store' ),
539 )
540 );
541
542 ebook_store_field(
543 array(
544 'type' => 'select',
545 'name' => 'paypal_currency',
546 'id' => 'paypal_currency',
547 'label' => __( 'Store currency', 'ebook-store' ),
548 'value' => $current_currency,
549 'optgroups' => $currency_groups,
550 'help' => __( 'Every price is charged in this currency, whichever payment method the buyer uses. A currency your provider rejects makes checkout fail, so check before switching.', 'ebook-store' ),
551 'hint' => sprintf(
552 /* translators: 1: legacy option name in code style, 2: link to the PayPal settings tab. */
553 __( 'For historical reasons this is stored under the name %1$s, but it is not PayPal-only. The payment methods themselves are set up in %2$s and the other gateway tabs.', 'ebook-store' ),
554 '<code>paypal_currency</code>',
555 '<a href="' . esc_url( $paypal_tab_url ) . '">' . esc_html__( 'the PayPal tab', 'ebook-store' ) . '</a>'
556 ),
557 )
558 );
559
560 ebook_store_grid_close();
561
562 $sample_net = 10.0;
563 $vat_number = max( 0, (float) $vat_percent );
564 $sample_gross = $sample_net * ( 1 + $vat_number / 100 );
565 if ( $vat_number > 0 ) {
566 $pricing_preview = sprintf(
567 /* translators: 1: currency code, 2: net price, 3: gross price, 4: tax percentage. */
568 __( 'Example: a %1$s %2$s book is charged as %1$s %3$s at checkout (%2$s + %4$s%% tax).', 'ebook-store' ),
569 esc_html( $current_currency ),
570 esc_html( number_format( $sample_net, 2 ) ),
571 esc_html( number_format( $sample_gross, 2 ) ),
572 esc_html( rtrim( rtrim( number_format( $vat_number, 2, '.', '' ), '0' ), '.' ) )
573 );
574 } else {
575 $pricing_preview = sprintf(
576 /* translators: 1: currency code, 2: price. */
577 __( 'Example: a %1$s %2$s book is charged as %1$s %2$s at checkout, with no tax added.', 'ebook-store' ),
578 esc_html( $current_currency ),
579 esc_html( number_format( $sample_net, 2 ) )
580 );
581 }
582 printf(
583 '<div class="ebook-settings-inline-note">%s</div>',
584 $pricing_preview // Already escaped piecewise above.
585 );
586
587 ebook_store_section_close();
588
589 /*
590 * ---------------------------------------------------------------------------
591 * Internal flags carried through the save.
592 * ---------------------------------------------------------------------------
593 * ebookstorewpsc is not a shop setting: it records that the WP Super Cache
594 * notice was dismissed. It is registered in this option group, and
595 * wp-admin/options.php writes null over every registered option of the group
596 * that the submitted form does not contain, so the dismissal would come back
597 * on every save. Its current value is resubmitted unchanged instead.
598 */
599 printf(
600 '<input type="hidden" name="ebookstorewpsc" value="%s" />',
601 esc_attr( (string) get_option( 'ebookstorewpsc', '' ) )
602 );
603