| 1 |
<?php |
| 2 |
/** |
| 3 |
* Ebook Store - "ePub protection" settings tab. |
| 4 |
* |
| 5 |
* Rendered inside <div class="ebook-settings-panel"> by ebook_options.php, so this |
| 6 |
* file emits sections only: no table rows, no cells. |
| 7 |
* |
| 8 |
* EPUB cannot be password-encrypted and stay readable in e-readers, so protection |
| 9 |
* here is social DRM: the buyer's identity is watermarked into the book and stamped |
| 10 |
* into its metadata, while the file stays a valid ePub. Reading order: Mark (what |
| 11 |
* the watermark says) -> Place (where it appears) -> Stamp (buyer in the metadata). |
| 12 |
* |
| 13 |
* PHP 7.4 compatible. |
| 14 |
* |
| 15 |
* @package EbookStore |
| 16 |
*/ |
| 17 |
|
| 18 |
if ( ! defined( 'ABSPATH' ) ) { |
| 19 |
exit; |
| 20 |
} |
| 21 |
|
| 22 |
settings_fields( 'ebook-settings-group-epub-protection' ); |
| 23 |
do_settings_sections( 'ebook-settings-group-epub-protection' ); |
| 24 |
|
| 25 |
$wm_on = ( '1' === (string) get_option( 'ebook_store_epub_watermark', 0 ) ); |
| 26 |
$wm_text = (string) get_option( 'ebook_store_epub_watermark_text', 'Licensed to %%first_name%% %%last_name%% - %%payer_email%%. Please do not share.' ); |
| 27 |
$wm_scope = (string) get_option( 'ebook_store_epub_watermark_scope', 'first' ); |
| 28 |
$wm_position = (string) get_option( 'ebook_store_epub_watermark_position', 'bottom' ); |
| 29 |
$wm_style = (string) get_option( 'ebook_store_epub_watermark_style', 'subtle' ); |
| 30 |
$meta_on = ( '1' === (string) get_option( 'ebook_store_epub_stamp_metadata', 0 ) ); |
| 31 |
$qr_on = ( '1' === (string) get_option( 'ebook_store_epub_qr', 0 ) ); |
| 32 |
$qr_scope = (string) get_option( 'ebook_store_epub_qr_scope', 'first' ); |
| 33 |
$qr_position = (string) get_option( 'ebook_store_epub_qr_position', 'bottom' ); |
| 34 |
|
| 35 |
$scope_options = array( |
| 36 |
'first' => __( 'The opening page only', 'ebook-store' ), |
| 37 |
'every' => __( 'The start of every chapter', 'ebook-store' ), |
| 38 |
'colophon' => __( 'A dedicated licence page at the end', 'ebook-store' ), |
| 39 |
); |
| 40 |
|
| 41 |
$position_options = array( |
| 42 |
'top' => __( 'Top of the page', 'ebook-store' ), |
| 43 |
'bottom' => __( 'Bottom of the page', 'ebook-store' ), |
| 44 |
); |
| 45 |
|
| 46 |
$style_options = array( |
| 47 |
'subtle' => __( 'Subtle — a small, muted line', 'ebook-store' ), |
| 48 |
'box' => __( 'Boxed — a bordered licence notice', 'ebook-store' ), |
| 49 |
); |
| 50 |
|
| 51 |
$scope_where = array( |
| 52 |
'first' => __( 'the opening page', 'ebook-store' ), |
| 53 |
'every' => __( 'the start of every chapter', 'ebook-store' ), |
| 54 |
'colophon' => __( 'a licence page at the end', 'ebook-store' ), |
| 55 |
); |
| 56 |
$where_phrase = isset( $scope_where[ $wm_scope ] ) ? $scope_where[ $wm_scope ] : $scope_where['first']; |
| 57 |
$qr_where_phrase = isset( $scope_where[ $qr_scope ] ) ? $scope_where[ $qr_scope ] : $scope_where['first']; |
| 58 |
|
| 59 |
$pdf_tab_url = function_exists( 'ebook_store_get_settings_tab_url' ) |
| 60 |
? ebook_store_get_settings_tab_url( 'PDF-Protection' ) |
| 61 |
: admin_url( 'options-general.php?page=ebook_options.php&tab=PDF-Protection' ); |
| 62 |
|
| 63 |
/* |
| 64 |
* --------------------------------------------------------------------------- |
| 65 |
* Hero + "what gets stamped" status card. |
| 66 |
* --------------------------------------------------------------------------- |
| 67 |
*/ |
| 68 |
ebook_store_render_hero( |
| 69 |
array( |
| 70 |
'eyebrow' => __( 'ePub protection', 'ebook-store' ), |
| 71 |
'title' => __( 'Trace the ePubs you sell back to their buyer.', 'ebook-store' ), |
| 72 |
'intro' => __( 'An ePub is a website in a box, and unlike a PDF it cannot be locked with a password without breaking e-readers. So instead of locking it, Ebook Store stamps each buyer\'s name into the book and its metadata. The copy stays a perfectly valid ePub that opens everywhere — but if it turns up on a sharing site, it points straight back to the order it came from.', 'ebook-store' ), |
| 73 |
'aside' => function () use ( $wm_on, $where_phrase, $wm_style, $meta_on, $qr_on, $qr_where_phrase ) { |
| 74 |
?> |
| 75 |
<aside class="ebook-settings-status-card"> |
| 76 |
<h4><?php echo esc_html__( 'What gets stamped', 'ebook-store' ); ?></h4> |
| 77 |
<ul class="ebook-settings-status-list"> |
| 78 |
<?php |
| 79 |
ebook_store_status_row( |
| 80 |
__( 'Visible watermark', 'ebook-store' ), |
| 81 |
$wm_on ? 'ok' : 'muted', |
| 82 |
$wm_on |
| 83 |
? ( $wm_style === 'box' |
| 84 |
? __( 'A bordered licence notice with the buyer\'s name.', 'ebook-store' ) |
| 85 |
: __( 'A small, muted line with the buyer\'s name.', 'ebook-store' ) ) |
| 86 |
: __( 'Off. ePubs are delivered exactly as uploaded.', 'ebook-store' ) |
| 87 |
); |
| 88 |
|
| 89 |
if ( $wm_on ) { |
| 90 |
ebook_store_status_row( |
| 91 |
__( 'Where it lands', 'ebook-store' ), |
| 92 |
'ok', |
| 93 |
sprintf( |
| 94 |
/* translators: %s: a place in the book, e.g. the opening page. */ |
| 95 |
__( 'Printed on %s.', 'ebook-store' ), |
| 96 |
$where_phrase |
| 97 |
) |
| 98 |
); |
| 99 |
} |
| 100 |
|
| 101 |
ebook_store_status_row( |
| 102 |
__( 'Metadata stamp', 'ebook-store' ), |
| 103 |
$meta_on ? 'ok' : 'muted', |
| 104 |
$meta_on |
| 105 |
? __( 'The buyer is recorded in the file\'s hidden metadata too.', 'ebook-store' ) |
| 106 |
: __( 'Off. Only the visible watermark is added.', 'ebook-store' ) |
| 107 |
); |
| 108 |
|
| 109 |
ebook_store_status_row( |
| 110 |
__( 'QR verification code', 'ebook-store' ), |
| 111 |
$qr_on ? 'ok' : 'muted', |
| 112 |
$qr_on |
| 113 |
? sprintf( |
| 114 |
/* translators: %s: a place in the book, e.g. the opening page. */ |
| 115 |
__( 'A scannable code that links back to the order, on %s.', 'ebook-store' ), |
| 116 |
$qr_where_phrase |
| 117 |
) |
| 118 |
: __( 'Off. No QR code is added.', 'ebook-store' ) |
| 119 |
); |
| 120 |
|
| 121 |
ebook_store_status_row( |
| 122 |
__( 'Still a valid ebook', 'ebook-store' ), |
| 123 |
'ok', |
| 124 |
__( 'The file opens normally in Apple Books, Kindle, Kobo and others.', 'ebook-store' ) |
| 125 |
); |
| 126 |
|
| 127 |
ebook_store_status_row( |
| 128 |
__( 'Any language', 'ebook-store' ), |
| 129 |
'ok', |
| 130 |
__( 'Buyer names in Cyrillic, Hebrew or Arabic are stamped correctly.', 'ebook-store' ) |
| 131 |
); |
| 132 |
?> |
| 133 |
</ul> |
| 134 |
</aside> |
| 135 |
<?php |
| 136 |
}, |
| 137 |
) |
| 138 |
); |
| 139 |
|
| 140 |
ebook_store_pro_banner( __( 'ePub protection', 'ebook-store' ) ); |
| 141 |
|
| 142 |
ebook_store_callout( |
| 143 |
sprintf( |
| 144 |
/* translators: %s: link to the PDF protection tab. */ |
| 145 |
__( 'This tab is for ePub files. ePubs are watermarked and traceable, not locked — encryption would stop e-readers opening them. To encrypt and password-protect PDFs instead, use the %s.', 'ebook-store' ), |
| 146 |
'<a href="' . esc_url( $pdf_tab_url ) . '">' . esc_html__( 'PDF protection tab', 'ebook-store' ) . '</a>' |
| 147 |
), |
| 148 |
'info', |
| 149 |
__( 'How ePub protection works:', 'ebook-store' ) |
| 150 |
); |
| 151 |
|
| 152 |
/* |
| 153 |
* --------------------------------------------------------------------------- |
| 154 |
* Section 1 · Mark — turn it on and choose what the watermark says. |
| 155 |
* --------------------------------------------------------------------------- |
| 156 |
*/ |
| 157 |
ebook_store_section_open( |
| 158 |
array( |
| 159 |
'eyebrow' => __( 'Mark', 'ebook-store' ), |
| 160 |
'title' => __( 'Watermark each ePub', 'ebook-store' ), |
| 161 |
'intro' => __( 'Print the buyer\'s identity into the book so every copy is traceable to the order it came from.', 'ebook-store' ), |
| 162 |
'id' => 'ebook-epub-watermark', |
| 163 |
'pro' => true, |
| 164 |
) |
| 165 |
); |
| 166 |
|
| 167 |
ebook_store_toggle_grid_open(); |
| 168 |
ebook_store_toggle( |
| 169 |
array( |
| 170 |
'name' => 'ebook_store_epub_watermark', |
| 171 |
'id' => 'ebook_store_epub_watermark', |
| 172 |
'title' => __( 'Watermark every ePub I sell', 'ebook-store' ), |
| 173 |
'description' => __( 'When a buyer downloads an ePub, a per-order copy is generated with their details stamped in. The original upload is never changed.', 'ebook-store' ), |
| 174 |
'checked' => $wm_on ? '1' : '', |
| 175 |
'pro' => true, |
| 176 |
) |
| 177 |
); |
| 178 |
ebook_store_toggle_grid_close(); |
| 179 |
|
| 180 |
if ( ! $wm_on ) { |
| 181 |
ebook_store_callout( |
| 182 |
__( 'The wording and placement below are saved, but no ePub is watermarked until you switch this on.', 'ebook-store' ), |
| 183 |
'soft', |
| 184 |
__( 'Heads up:', 'ebook-store' ) |
| 185 |
); |
| 186 |
} |
| 187 |
|
| 188 |
ebook_store_field( |
| 189 |
array( |
| 190 |
'type' => 'textarea', |
| 191 |
'name' => 'ebook_store_epub_watermark_text', |
| 192 |
'id' => 'ebook_store_epub_watermark_text', |
| 193 |
'label' => __( 'What the watermark should say', 'ebook-store' ), |
| 194 |
'value' => $wm_text, |
| 195 |
'wide' => true, |
| 196 |
'pro' => true, |
| 197 |
'help' => sprintf( |
| 198 |
/* translators: 1-4: merge codes shown in code style. */ |
| 199 |
__( 'Use these merge codes and they are replaced with the buyer\'s real details: %1$s, %2$s, %3$s, %4$s. Example: "Licensed to %1$s %2$s (%3$s)".', 'ebook-store' ), |
| 200 |
'<code>%%first_name%%</code>', |
| 201 |
'<code>%%last_name%%</code>', |
| 202 |
'<code>%%payer_email%%</code>', |
| 203 |
'<code>%%residence_country%%</code>' |
| 204 |
), |
| 205 |
'hint' => __( 'The buyer\'s name is escaped before it is inserted, so a name containing < > & or quotes cannot break the book, and any script (Latin, Cyrillic, Hebrew, Arabic) is stamped correctly. Leaving this empty turns watermarking off for that download.', 'ebook-store' ), |
| 206 |
) |
| 207 |
); |
| 208 |
|
| 209 |
ebook_store_section_close(); |
| 210 |
|
| 211 |
/* |
| 212 |
* --------------------------------------------------------------------------- |
| 213 |
* Section 2 · Place — where and how the watermark appears. |
| 214 |
* --------------------------------------------------------------------------- |
| 215 |
*/ |
| 216 |
ebook_store_section_open( |
| 217 |
array( |
| 218 |
'eyebrow' => __( 'Place', 'ebook-store' ), |
| 219 |
'title' => __( 'Where the watermark appears', 'ebook-store' ), |
| 220 |
'intro' => __( 'Balance how visible the watermark is against how little it gets in the reader\'s way.', 'ebook-store' ), |
| 221 |
'id' => 'ebook-epub-placement', |
| 222 |
'pro' => true, |
| 223 |
) |
| 224 |
); |
| 225 |
|
| 226 |
ebook_store_grid_open( 2 ); |
| 227 |
|
| 228 |
ebook_store_field( |
| 229 |
array( |
| 230 |
'type' => 'select', |
| 231 |
'name' => 'ebook_store_epub_watermark_scope', |
| 232 |
'id' => 'ebook_store_epub_watermark_scope', |
| 233 |
'label' => __( 'Where to place it', 'ebook-store' ), |
| 234 |
'value' => $wm_scope, |
| 235 |
'options' => $scope_options, |
| 236 |
'pro' => true, |
| 237 |
'help' => __( 'The opening page is a balanced default. Every chapter is the hardest to remove. A licence page at the end is the least intrusive for the reader.', 'ebook-store' ), |
| 238 |
) |
| 239 |
); |
| 240 |
|
| 241 |
ebook_store_field( |
| 242 |
array( |
| 243 |
'type' => 'select', |
| 244 |
'name' => 'ebook_store_epub_watermark_position', |
| 245 |
'id' => 'ebook_store_epub_watermark_position', |
| 246 |
'label' => __( 'Position on the page', 'ebook-store' ), |
| 247 |
'value' => $wm_position, |
| 248 |
'options' => $position_options, |
| 249 |
'pro' => true, |
| 250 |
'help' => __( 'Top or bottom of the chosen page. Ignored for the dedicated licence page, which is a page of its own.', 'ebook-store' ), |
| 251 |
) |
| 252 |
); |
| 253 |
|
| 254 |
ebook_store_field( |
| 255 |
array( |
| 256 |
'type' => 'select', |
| 257 |
'name' => 'ebook_store_epub_watermark_style', |
| 258 |
'id' => 'ebook_store_epub_watermark_style', |
| 259 |
'label' => __( 'How it looks', 'ebook-store' ), |
| 260 |
'value' => $wm_style, |
| 261 |
'options' => $style_options, |
| 262 |
'wide' => true, |
| 263 |
'pro' => true, |
| 264 |
'help' => __( 'Subtle is a faint single line. Boxed draws a bordered notice that is harder to overlook.', 'ebook-store' ), |
| 265 |
) |
| 266 |
); |
| 267 |
|
| 268 |
ebook_store_grid_close(); |
| 269 |
|
| 270 |
ebook_store_section_close(); |
| 271 |
|
| 272 |
/* |
| 273 |
* --------------------------------------------------------------------------- |
| 274 |
* Section · Verify — a QR code, and where it goes. |
| 275 |
* --------------------------------------------------------------------------- |
| 276 |
*/ |
| 277 |
ebook_store_section_open( |
| 278 |
array( |
| 279 |
'eyebrow' => __( 'Verify', 'ebook-store' ), |
| 280 |
'title' => __( 'QR verification code', 'ebook-store' ), |
| 281 |
'intro' => __( 'Add a scannable QR code that opens this order\'s verification page, so any copy can be traced back to the sale it came from. Choose where it goes.', 'ebook-store' ), |
| 282 |
'id' => 'ebook-epub-qr', |
| 283 |
'pro' => true, |
| 284 |
) |
| 285 |
); |
| 286 |
|
| 287 |
ebook_store_toggle_grid_open(); |
| 288 |
ebook_store_toggle( |
| 289 |
array( |
| 290 |
'name' => 'ebook_store_epub_qr', |
| 291 |
'id' => 'ebook_store_epub_qr', |
| 292 |
'title' => __( 'Add a QR verification code', 'ebook-store' ), |
| 293 |
'description' => __( 'Embeds a scannable code (as a real image inside the book) that links to this order\'s verification page.', 'ebook-store' ), |
| 294 |
'checked' => $qr_on ? '1' : '', |
| 295 |
'pro' => true, |
| 296 |
) |
| 297 |
); |
| 298 |
ebook_store_toggle_grid_close(); |
| 299 |
|
| 300 |
ebook_store_grid_open( 2 ); |
| 301 |
|
| 302 |
ebook_store_field( |
| 303 |
array( |
| 304 |
'type' => 'select', |
| 305 |
'name' => 'ebook_store_epub_qr_scope', |
| 306 |
'id' => 'ebook_store_epub_qr_scope', |
| 307 |
'label' => __( 'Where to place the QR code', 'ebook-store' ), |
| 308 |
'value' => $qr_scope, |
| 309 |
'options' => $scope_options, |
| 310 |
'pro' => true, |
| 311 |
'help' => __( 'By default it sits on the opening page. Put it on every chapter for the strongest trace, or on the licence page at the end to keep it out of the way.', 'ebook-store' ), |
| 312 |
) |
| 313 |
); |
| 314 |
|
| 315 |
ebook_store_field( |
| 316 |
array( |
| 317 |
'type' => 'select', |
| 318 |
'name' => 'ebook_store_epub_qr_position', |
| 319 |
'id' => 'ebook_store_epub_qr_position', |
| 320 |
'label' => __( 'Position on the page', 'ebook-store' ), |
| 321 |
'value' => $qr_position, |
| 322 |
'options' => $position_options, |
| 323 |
'pro' => true, |
| 324 |
'help' => __( 'Top or bottom of the chosen page. Ignored for the licence page, which is a page of its own.', 'ebook-store' ), |
| 325 |
) |
| 326 |
); |
| 327 |
|
| 328 |
ebook_store_grid_close(); |
| 329 |
|
| 330 |
ebook_store_callout( |
| 331 |
__( 'The QR code is drawn on your own server and embedded as an image inside the book, so it scans on any reader background and needs no outside service to generate. Scanning it opens the verification page, which needs internet.', 'ebook-store' ), |
| 332 |
'soft', |
| 333 |
__( 'Good to know:', 'ebook-store' ) |
| 334 |
); |
| 335 |
|
| 336 |
ebook_store_section_close(); |
| 337 |
|
| 338 |
/* |
| 339 |
* --------------------------------------------------------------------------- |
| 340 |
* Section · Stamp — record the buyer in the file metadata too. |
| 341 |
* --------------------------------------------------------------------------- |
| 342 |
*/ |
| 343 |
ebook_store_section_open( |
| 344 |
array( |
| 345 |
'eyebrow' => __( 'Stamp', 'ebook-store' ), |
| 346 |
'title' => __( 'Stamp the buyer into the metadata', 'ebook-store' ), |
| 347 |
'intro' => __( 'Add the buyer to the book\'s hidden metadata as well as the visible watermark, so the trace survives even if the visible line is edited out.', 'ebook-store' ), |
| 348 |
'id' => 'ebook-epub-metadata', |
| 349 |
'pro' => true, |
| 350 |
) |
| 351 |
); |
| 352 |
|
| 353 |
ebook_store_toggle_grid_open(); |
| 354 |
ebook_store_toggle( |
| 355 |
array( |
| 356 |
'name' => 'ebook_store_epub_stamp_metadata', |
| 357 |
'id' => 'ebook_store_epub_stamp_metadata', |
| 358 |
'title' => __( 'Record the buyer in the file\'s metadata', 'ebook-store' ), |
| 359 |
'description' => __( 'Adds the buyer as a contributor and rights holder inside the book\'s package file. Invisible to the reader, visible to library and forensic tools.', 'ebook-store' ), |
| 360 |
'checked' => $meta_on ? '1' : '', |
| 361 |
'pro' => true, |
| 362 |
) |
| 363 |
); |
| 364 |
ebook_store_toggle_grid_close(); |
| 365 |
|
| 366 |
ebook_store_callout( |
| 367 |
__( 'Whatever you choose here, the delivered file stays a valid ePub: its mimetype marker stays first and uncompressed, every file stays listed in the book\'s manifest, and the pages stay well-formed. Files that carry their own DRM or a digital signature are passed through untouched, and a paid download is never blocked if watermarking cannot run.', 'ebook-store' ), |
| 368 |
'soft', |
| 369 |
__( 'Stays a valid ebook:', 'ebook-store' ) |
| 370 |
); |
| 371 |
|
| 372 |
ebook_store_section_close(); |
| 373 |
|