PluginProbe
Ebook Store / trunk
Ebook Store vtrunk
6.24 6.23 6.22 6.21 6.20 trunk
ebook-store / settings-Templates.php

settings-Templates.php in Ebook Store trunk, at settings-Templates.php

459 lines 17.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ebook Store - "Templates" settings tab.
4 *
5 * Pages the buyer is sent to, the delivery e-mail, and the HTML used for the
6 * thank-you page and the checkout form.
7 *
8 * PHP 7.4 compatible.
9 *
10 * @package EbookStore
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 settings_fields( 'ebook-settings-group-templates' );
18 do_settings_sections( 'ebook-settings-group-templates' );
19
20 // $op holds the shipped defaults; it is created by ebook_settings_page(). Guard
21 // so the tab still renders if this file is ever included on its own.
22 if ( ! isset( $op ) || ! is_object( $op ) ) {
23 $op = new QSWPOptions();
24 }
25
26 $ebook_general_tab_url = function_exists( 'ebook_store_get_settings_tab_url' )
27 ? ebook_store_get_settings_tab_url( 'General' )
28 : admin_url( 'options-general.php?page=ebook_options.php' );
29
30 $ebook_thankyou_page_id = (int) get_option( 'ebook_store_checkout_page', 0 );
31 $ebook_cancel_page_id = (int) get_option( 'ebook_store_cancel_page', 0 );
32 $ebook_thankyou_page = $ebook_thankyou_page_id > 0 ? get_post( $ebook_thankyou_page_id ) : null;
33 $ebook_cancel_page = $ebook_cancel_page_id > 0 ? get_post( $ebook_cancel_page_id ) : null;
34 $ebook_thankyou_has_code = false;
35
36 if ( $ebook_thankyou_page instanceof WP_Post ) {
37 $ebook_thankyou_has_code = has_shortcode(
38 (string) get_post_field( 'post_content', $ebook_thankyou_page_id ),
39 'ebook_thank_you'
40 );
41 }
42
43 /**
44 * Render one page picker with a real id, a real label and a description.
45 *
46 * A closure rather than a named function so re-including this file can never
47 * trigger a "cannot redeclare" fatal.
48 *
49 * @param string $field_id HTML id and option name.
50 * @param string $label Visible label.
51 * @param int $selected Currently selected page id.
52 * @param string $none_text Text for the "nothing chosen" entry.
53 * @param string $help Description below the control.
54 */
55 $ebook_render_page_picker = function ( $field_id, $label, $selected, $none_text, $help ) {
56 ?>
57 <div class="ebook-settings-field" data-ebook-search="<?php echo esc_attr( $label . ' ' . wp_strip_all_tags( $help ) . ' ' . $field_id ); ?>">
58 <label for="<?php echo esc_attr( $field_id ); ?>"><?php echo esc_html( $label ); ?></label>
59 <?php
60 wp_dropdown_pages(
61 array(
62 'depth' => 0,
63 'child_of' => 0,
64 'selected' => $selected,
65 'echo' => 1,
66 'name' => $field_id,
67 'id' => $field_id,
68 'show_option_none' => $none_text,
69 'show_option_no_change' => null,
70 'option_none_value' => '0',
71 'post_type' => 'page',
72 )
73 );
74 ?>
75 <p class="description"><?php echo wp_kses_post( $help ); ?></p>
76 </div>
77 <?php
78 };
79
80 /**
81 * Render the copyable placeholder reference.
82 *
83 * @param array $placeholders code => explanation.
84 */
85 $ebook_render_placeholders = function ( $placeholders ) {
86 $index = 0;
87 ?>
88 <dl class="ebook-settings-placeholders">
89 <?php
90 foreach ( $placeholders as $code => $explanation ) {
91 $index++;
92 $input_id = 'ebook-placeholder-' . $index;
93 ?>
94 <div class="ebook-settings-placeholders__row">
95 <dt>
96 <span class="ebook-code__row">
97 <input
98 id="<?php echo esc_attr( $input_id ); ?>"
99 class="ebook-code__input ebook-settings-monospace-input"
100 type="text"
101 readonly="readonly"
102 value="<?php echo esc_attr( $code ); ?>"
103 aria-label="<?php
104 /* translators: %s: a placeholder such as %%first_name%%. */
105 echo esc_attr( sprintf( __( 'Placeholder %s', 'ebook-store' ), $code ) );
106 ?>" />
107 <button type="button" class="button ebook-code__copy" data-copy-target="#<?php echo esc_attr( $input_id ); ?>">
108 <?php echo esc_html__( 'Copy', 'ebook-store' ); ?>
109 </button>
110 <span class="ebook-code__status" role="status" aria-live="polite"></span>
111 </span>
112 </dt>
113 <dd><?php echo esc_html( $explanation ); ?></dd>
114 </div>
115 <?php
116 }
117 ?>
118 </dl>
119 <?php
120 };
121
122 ebook_store_render_hero(
123 array(
124 'eyebrow' => __( 'Templates', 'ebook-store' ),
125 'title' => __( 'Pages, e-mails and page content', 'ebook-store' ),
126 'intro' => __( 'Everything a buyer reads after paying: the page they land on, the e-mail that carries their download link, and the wording of both. Change the text freely; keep the %%placeholders%% so the real order details are filled in automatically.', 'ebook-store' ),
127 )
128 );
129
130 /* ---------------------------------------------------------------------------
131 * 1. Pages
132 * ------------------------------------------------------------------------ */
133
134 ebook_store_section_open(
135 array(
136 'id' => 'ebook-templates-pages',
137 'eyebrow' => __( 'Step 1', 'ebook-store' ),
138 'title' => __( 'Pages', 'ebook-store' ),
139 'intro' => __( 'Which of your WordPress pages the buyer is sent to after paying, and where they land if they change their mind.', 'ebook-store' ),
140 )
141 );
142
143 ebook_store_grid_open( 2 );
144
145 $ebook_render_page_picker(
146 'ebook_store_checkout_page',
147 __( 'Thank-you page (buyers land here after paying)', 'ebook-store' ),
148 $ebook_thankyou_page_id,
149 __( '-- Please select a page --', 'ebook-store' ),
150 sprintf(
151 /* translators: %s: the [ebook_thank_you] shortcode, shown literally. */
152 __( 'Pick the page that shows the download link. That page must contain the shortcode %s, otherwise the buyer sees an empty page and never gets their file.', 'ebook-store' ),
153 '<code>[ebook_thank_you]</code>'
154 )
155 );
156
157 $ebook_render_page_picker(
158 'ebook_store_cancel_page',
159 __( 'Cancelled-order page (optional)', 'ebook-store' ),
160 $ebook_cancel_page_id,
161 __( '-- Optional, none selected --', 'ebook-store' ),
162 __( 'Shown when someone starts a payment and then cancels it at PayPal. Leave this empty and they are returned to your home page.', 'ebook-store' )
163 );
164
165 ebook_store_grid_close();
166
167 // The single most common setup mistake: a thank-you page is chosen, but it does
168 // not actually contain the shortcode. Say so plainly.
169 ?>
170 <ul class="ebook-settings-status-list">
171 <?php
172 if ( $ebook_thankyou_page_id < 1 ) {
173 ebook_store_status_row(
174 __( 'Thank-you page', 'ebook-store' ),
175 'error',
176 __( 'No page is selected yet, so buyers have nowhere to collect their download.', 'ebook-store' ),
177 array(
178 'label' => __( 'Create one for me', 'ebook-store' ),
179 'url' => wp_nonce_url( admin_url( 'admin-post.php?action=ebook_store_fix_thankyou' ), 'ebook_store_fix_thankyou' ),
180 )
181 );
182 } elseif ( ! ( $ebook_thankyou_page instanceof WP_Post ) ) {
183 ebook_store_status_row(
184 __( 'Thank-you page', 'ebook-store' ),
185 'error',
186 __( 'The page that was selected no longer exists. Choose another page above.', 'ebook-store' ),
187 array(
188 'label' => __( 'Create one for me', 'ebook-store' ),
189 'url' => wp_nonce_url( admin_url( 'admin-post.php?action=ebook_store_fix_thankyou' ), 'ebook_store_fix_thankyou' ),
190 )
191 );
192 } elseif ( ! $ebook_thankyou_has_code ) {
193 ebook_store_status_row(
194 __( 'Thank-you page', 'ebook-store' ),
195 'error',
196 sprintf(
197 /* translators: 1: page title, 2: the [ebook_thank_you] shortcode. */
198 __( '"%1$s" does not contain %2$s. Buyers will see an empty page instead of their download link. Edit the page and add the shortcode.', 'ebook-store' ),
199 $ebook_thankyou_page->post_title,
200 '[ebook_thank_you]'
201 ),
202 array(
203 'label' => __( 'Edit the page', 'ebook-store' ),
204 'url' => (string) get_edit_post_link( $ebook_thankyou_page_id, '' ),
205 )
206 );
207 } else {
208 ebook_store_status_row(
209 __( 'Thank-you page', 'ebook-store' ),
210 'ok',
211 sprintf(
212 /* translators: %s: page title. */
213 __( '"%s" is selected and contains the shortcode.', 'ebook-store' ),
214 $ebook_thankyou_page->post_title
215 ),
216 array(
217 'label' => __( 'View the page', 'ebook-store' ),
218 'url' => (string) get_permalink( $ebook_thankyou_page_id ),
219 )
220 );
221 }
222
223 if ( $ebook_cancel_page_id < 1 ) {
224 ebook_store_status_row(
225 __( 'Cancelled-order page', 'ebook-store' ),
226 'muted',
227 __( 'Not set. Buyers who cancel are returned to your home page, which is fine for most stores.', 'ebook-store' )
228 );
229 } elseif ( ! ( $ebook_cancel_page instanceof WP_Post ) ) {
230 ebook_store_status_row(
231 __( 'Cancelled-order page', 'ebook-store' ),
232 'warn',
233 __( 'The page that was selected no longer exists. Choose another page or clear this setting.', 'ebook-store' )
234 );
235 } else {
236 ebook_store_status_row(
237 __( 'Cancelled-order page', 'ebook-store' ),
238 'ok',
239 sprintf(
240 /* translators: %s: page title. */
241 __( '"%s" is selected.', 'ebook-store' ),
242 $ebook_cancel_page->post_title
243 ),
244 array(
245 'label' => __( 'View the page', 'ebook-store' ),
246 'url' => (string) get_permalink( $ebook_cancel_page_id ),
247 )
248 );
249 }
250 ?>
251 </ul>
252 <?php
253
254 ebook_store_section_close();
255
256 /* ---------------------------------------------------------------------------
257 * 2. Placeholder reference
258 * ------------------------------------------------------------------------ */
259
260 ebook_store_section_open(
261 array(
262 'id' => 'ebook-templates-placeholders',
263 'eyebrow' => __( 'Reference', 'ebook-store' ),
264 'title' => __( 'Placeholders you can use', 'ebook-store' ),
265 'intro' => __( 'Type any of these into the e-mail or the thank-you page below. When the message is sent, each one is swapped for the real detail from that order. Copy them exactly, including the two percent signs on each side.', 'ebook-store' ),
266 )
267 );
268
269 $ebook_render_placeholders(
270 array(
271 '%%first_name%%' => __( "The buyer's first name.", 'ebook-store' ),
272 '%%last_name%%' => __( "The buyer's surname.", 'ebook-store' ),
273 '%%payer_email%%' => __( 'The e-mail address the buyer paid with.', 'ebook-store' ),
274 '%%order_id%%' => __( 'Your order number, useful when a buyer writes in about a problem.', 'ebook-store' ),
275 '%%txn_id%%' => __( 'The transaction number from the payment provider.', 'ebook-store' ),
276 '%%downloadlink_html%%' => __( 'The ready-made download link (or links) for the book that was bought. Almost every store needs this one.', 'ebook-store' ),
277 '%%ebook_bonus%%' => __( 'Download links for any bonus books attached to that title. Shows "None" if there are none.', 'ebook-store' ),
278 '%%password%%' => __( 'The password that opens the PDF, when you protect your files with one.', 'ebook-store' ),
279 '%%mc_gross%%' => __( 'The amount paid for the book, before tax.', 'ebook-store' ),
280 '%%tax%%' => __( 'The tax or VAT charged on the order.', 'ebook-store' ),
281 '%%total%%' => __( 'The full amount charged, tax included.', 'ebook-store' ),
282 '%%mc_currency%%' => __( 'The currency code of the payment, for example USD or EUR.', 'ebook-store' ),
283 '%%residence_country%%' => __( "The two-letter code of the buyer's country.", 'ebook-store' ),
284 )
285 );
286
287 ebook_store_callout(
288 sprintf(
289 /* translators: 1: example placeholder, 2: example placeholder. */
290 __( 'Any other detail the payment provider sends with the order can be used the same way, for example %1$s or %2$s. If you type a name the order does not have, the placeholder is simply left on the page as you typed it, so always send yourself a test order after editing.', 'ebook-store' ),
291 '<code>%%payment_date%%</code>',
292 '<code>%%item_name%%</code>'
293 ),
294 'soft',
295 __( 'Anything else?', 'ebook-store' )
296 );
297
298 ebook_store_section_close();
299
300 /* ---------------------------------------------------------------------------
301 * 3. Order e-mail, thank-you content and checkout form.
302 *
303 * These are rich-text (TinyMCE) editors. They are the whole point of this tab,
304 * and — importantly — a TinyMCE editor must be visible when it initialises, so
305 * they are NOT placed inside a collapsed <details> (doing so left them with a
306 * broken width and overlapping toolbar). The tab itself already lives behind the
307 * "Pages & emails" group, so users only arrive here when they mean to.
308 * ------------------------------------------------------------------------ */
309
310 ebook_store_section_open(
311 array(
312 'id' => 'ebook-templates-email',
313 'eyebrow' => __( 'Step 2', 'ebook-store' ),
314 'title' => __( 'Order e-mail', 'ebook-store' ),
315 'intro' => __( 'The message sent to the buyer once payment clears. This is the copy most buyers keep, so make sure the download link is easy to find.', 'ebook-store' ),
316 )
317 );
318
319 ebook_store_callout(
320 sprintf(
321 /* translators: %s: link to the General tab. */
322 __( 'This e-mail is only sent while <strong>Email delivery</strong> is switched on in the <a href="%s">General tab</a>. If it is off, buyers can only download from the thank-you page.', 'ebook-store' ),
323 esc_url( $ebook_general_tab_url )
324 ),
325 'info'
326 );
327
328 ebook_store_field(
329 array(
330 'type' => 'text',
331 'name' => 'email_delivery_subject',
332 'id' => 'email_delivery_subject',
333 'label' => __( 'Subject line', 'ebook-store' ),
334 'value' => get_option( 'email_delivery_subject', $op->email_delivery_subject ),
335 'placeholder' => $op->email_delivery_subject,
336 'wide' => true,
337 'help' => __( 'What the buyer sees in their inbox. Keep it short and mention your shop or the book title so it is not mistaken for spam.', 'ebook-store' ),
338 )
339 );
340 ?>
341
342 <div class="ebook-settings-field ebook-settings-field--wide" data-ebook-search="<?php echo esc_attr( __( 'E-mail message', 'ebook-store' ) . ' email_delivery_text' ); ?>">
343 <label for="email_delivery_text"><?php echo esc_html__( 'E-mail message', 'ebook-store' ); ?></label>
344 <?php
345 wp_editor(
346 get_option( 'email_delivery_text', $op->email_delivery_text ),
347 'email_delivery_text',
348 array(
349 'textarea_name' => 'email_delivery_text',
350 'textarea_rows' => 14,
351 )
352 );
353 ?>
354 <p class="description">
355 <?php
356 echo wp_kses_post(
357 sprintf(
358 /* translators: %s: the %%downloadlink_html%% placeholder. */
359 __( 'Always keep %s somewhere in this message. Without it the e-mail arrives with no way to download the book.', 'ebook-store' ),
360 '<code>%%downloadlink_html%%</code>'
361 )
362 );
363 ?>
364 </p>
365 </div>
366
367 <?php
368 ebook_store_section_close();
369
370 /* ---------------------------------------------------------------------------
371 * 4. Thank-you page content
372 * ------------------------------------------------------------------------ */
373
374 ebook_store_section_open(
375 array(
376 'id' => 'ebook-templates-thankyou',
377 'eyebrow' => __( 'Step 3', 'ebook-store' ),
378 'title' => __( 'Thank-you page content', 'ebook-store' ),
379 'intro' => __( 'What appears on the page the buyer lands on after paying. This text replaces the [ebook_thank_you] shortcode on the page you picked above.', 'ebook-store' ),
380 )
381 );
382 ?>
383
384 <div class="ebook-settings-field ebook-settings-field--wide" data-ebook-search="<?php echo esc_attr( __( 'Thank-you page content', 'ebook-store' ) . ' thankyou_page' ); ?>">
385 <label for="thankyou_page"><?php echo esc_html__( 'Page content', 'ebook-store' ); ?></label>
386 <?php
387 wp_editor(
388 get_option( 'thankyou_page', $op->thankyou_page ),
389 'thankyou_page',
390 array(
391 'textarea_name' => 'thankyou_page',
392 'textarea_rows' => 16,
393 )
394 );
395 ?>
396 <p class="description">
397 <?php
398 echo wp_kses_post(
399 sprintf(
400 /* translators: 1: the %%downloadlink_html%% placeholder, 2: the [ebook_thank_you] shortcode. */
401 __( 'Keep %1$s here too, and remember this text only shows up if the selected page contains %2$s.', 'ebook-store' ),
402 '<code>%%downloadlink_html%%</code>',
403 '<code>[ebook_thank_you]</code>'
404 )
405 );
406 ?>
407 </p>
408 </div>
409
410 <?php
411 ebook_store_section_close();
412
413 /* ---------------------------------------------------------------------------
414 * 5. Checkout form markup
415 * ------------------------------------------------------------------------ */
416
417 ebook_store_section_open(
418 array(
419 'id' => 'ebook-templates-form',
420 'eyebrow' => __( 'Advanced', 'ebook-store' ),
421 'title' => __( 'Checkout form markup', 'ebook-store' ),
422 'intro' => __( 'The short form that asks the buyer for their name and e-mail address before payment starts. Most stores never need to touch this.', 'ebook-store' ),
423 )
424 );
425
426 ebook_store_callout(
427 sprintf(
428 /* translators: 1: first_name field name, 2: last_name field name, 3: payer_email field name, 4: the CSS class "required". */
429 __( 'This box holds raw HTML. The fields named %1$s, %2$s and %3$s are the ones the plugin reads, so do not rename or remove them, and keep the class %4$s on any field a buyer must fill in. If you break the form, clear the box and save to fall back to the built-in version.', 'ebook-store' ),
430 '<code>first_name</code>',
431 '<code>last_name</code>',
432 '<code>payer_email</code>',
433 '<code>required</code>'
434 ),
435 'warning',
436 __( 'Editing this can stop checkout working.', 'ebook-store' )
437 );
438 ?>
439
440 <div class="ebook-settings-field ebook-settings-field--wide" data-ebook-search="<?php echo esc_attr( __( 'Checkout form markup', 'ebook-store' ) . ' formContent' ); ?>">
441 <label for="formContent"><?php echo esc_html__( 'Form HTML', 'ebook-store' ); ?></label>
442 <?php
443 wp_editor(
444 get_option( 'formContent', $op->formContent ),
445 'formContent',
446 array(
447 'textarea_name' => 'formContent',
448 'textarea_rows' => 12,
449 )
450 );
451 ?>
452 <p class="description"><?php echo esc_html__( 'Use the "Text" tab of the editor when you edit this, so the HTML is not reformatted.', 'ebook-store' ); ?></p>
453 </div>
454
455 <?php
456 ebook_store_section_close();
457
458 do_action( 'ebook_settings_page_extend' );
459