| 1 |
<?php |
| 2 |
|
| 3 |
// error_reporting(E_ALL); |
| 4 |
|
| 5 |
include_once('EbookStoreEbook.class.php'); |
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
function ebook_activate() { |
| 10 |
// register taxonomies/post types here |
| 11 |
flush_rewrite_rules(); |
| 12 |
} |
| 13 |
|
| 14 |
function ebook_deactivate() { |
| 15 |
flush_rewrite_rules(); |
| 16 |
} |
| 17 |
|
| 18 |
function ebook_store_activate() { |
| 19 |
ebook_create_post_type(); |
| 20 |
ebook_store_ensure_protected_dirs(); |
| 21 |
flush_rewrite_rules(); |
| 22 |
} |
| 23 |
|
| 24 |
function ebook_create_post_type() { |
| 25 |
$labels = array( |
| 26 |
'name' => _x('Ebook Store', 'post type general name', 'ebook-store'), |
| 27 |
'singular_name' => _x('Ebook', 'post type singular name', 'ebook-store'), |
| 28 |
'add_new' => _x('Add New Ebook', 'ebook', 'ebook-store'), |
| 29 |
'add_new_item' => __('Add New Ebook Item', 'ebook-store'), |
| 30 |
'edit_item' => __('Edit Item', 'ebook-store'), |
| 31 |
'all_items' => __('Ebooks', 'ebook-store'), |
| 32 |
'new_item' => __('New Ebook Item', 'ebook-store'), |
| 33 |
'view_item' => __('View Ebook Item', 'ebook-store'), |
| 34 |
'search_items' => __('Search Ebook', 'ebook-store'), |
| 35 |
'not_found' => __('Nothing found', 'ebook-store'), |
| 36 |
'not_found_in_trash' => __('Nothing found in Trash', 'ebook-store'), |
| 37 |
'parent_item_colon' => '' |
| 38 |
); |
| 39 |
|
| 40 |
$args = array( |
| 41 |
'labels' => $labels, |
| 42 |
'public' => true, |
| 43 |
'publicly_queryable' => true, |
| 44 |
'show_ui' => true, |
| 45 |
'show_in_menu' => true, |
| 46 |
'show_in_nav_menus' => true, |
| 47 |
'query_var' => true, |
| 48 |
'rewrite' => array('slug','ebook'), |
| 49 |
'capability_type' => 'page', |
| 50 |
'hierarchical' => true, |
| 51 |
//'menu_position' => 5, |
| 52 |
'has_archive' => true, |
| 53 |
//'taxonomies' => array('category'), |
| 54 |
'supports' => array('title','editor'), |
| 55 |
'menu_icon' => 'dashicons-cart', |
| 56 |
); |
| 57 |
|
| 58 |
register_post_type( 'ebook' , $args ); |
| 59 |
|
| 60 |
$labels = array( |
| 61 |
'name' => __('Authors', 'ebook-store'), |
| 62 |
'singular_name' => __('Author', 'ebook-store'), |
| 63 |
'add_new' => __('Add New', 'ebook-store'), |
| 64 |
'add_new_item' => __('Add New Author', 'ebook-store'), |
| 65 |
'edit_item' => __('Edit Author', 'ebook-store'), |
| 66 |
'new_item' => __('New Author', 'ebook-store'), |
| 67 |
'all_items' => __('Authors', 'ebook-store'), |
| 68 |
'view_item' => __('View Author', 'ebook-store'), |
| 69 |
'search_items' => __('Search Author', 'ebook-store'), |
| 70 |
'not_found' => __('No authors found', 'ebook-store'), |
| 71 |
'not_found_in_trash' => __('No authors found in Trash', 'ebook-store'), |
| 72 |
'parent_item_colon' => '', |
| 73 |
'menu_name' => __('Authors', 'ebook-store'), |
| 74 |
|
| 75 |
); |
| 76 |
|
| 77 |
$args = array( |
| 78 |
'labels' => $labels, |
| 79 |
'public' => true, |
| 80 |
'publicly_queryable' => true, |
| 81 |
'show_ui' => true, |
| 82 |
'show_in_menu' => 'edit.php?post_type=ebook', |
| 83 |
'query_var' => true, |
| 84 |
'rewrite' => array( 'slug' => 'ebook_author' ), |
| 85 |
'capability_type' => 'post', |
| 86 |
'has_archive' => true, |
| 87 |
'hierarchical' => false, |
| 88 |
//'menu_position' => 5, |
| 89 |
'supports' => array( 'title', 'thumbnail','comments' ) |
| 90 |
); |
| 91 |
|
| 92 |
register_post_type( 'ebook_author', $args ); |
| 93 |
///// |
| 94 |
$labels = array( |
| 95 |
'name' => __('Publishers', 'ebook-store'), |
| 96 |
'singular_name' => __('Publisher', 'ebook-store'), |
| 97 |
'add_new' => __('Add New', 'ebook-store'), |
| 98 |
'add_new_item' => __('Add New Publisher', 'ebook-store'), |
| 99 |
'edit_item' => __('Edit Publisher', 'ebook-store'), |
| 100 |
'new_item' => __('New Publisher', 'ebook-store'), |
| 101 |
'all_items' => __('Publishers', 'ebook-store'), |
| 102 |
'view_item' => __('View Publisher', 'ebook-store'), |
| 103 |
'search_items' => __('Search Publisher', 'ebook-store'), |
| 104 |
'not_found' => __('No Publishers found', 'ebook-store'), |
| 105 |
'not_found_in_trash' => __('No Publishers found in Trash', 'ebook-store'), |
| 106 |
'parent_item_colon' => '', |
| 107 |
'menu_name' => __('Publishers', 'ebook-store')); |
| 108 |
|
| 109 |
$args = array( |
| 110 |
'labels' => $labels, |
| 111 |
'public' => true, |
| 112 |
'publicly_queryable' => true, |
| 113 |
'show_ui' => true, |
| 114 |
'show_in_menu' => 'edit.php?post_type=ebook', |
| 115 |
'query_var' => true, |
| 116 |
'rewrite' => array( 'slug' => 'publisher' ), |
| 117 |
'capability_type' => 'post', |
| 118 |
'has_archive' => true, |
| 119 |
'hierarchical' => false, |
| 120 |
//'menu_position' => 5, |
| 121 |
'supports' => array( 'title', 'thumbnail','comments' ) |
| 122 |
); |
| 123 |
|
| 124 |
register_post_type( 'ebook_publisher', $args ); |
| 125 |
/////// |
| 126 |
$labels = array( |
| 127 |
'name' => __('Orders', 'ebook-store'), |
| 128 |
'singular_name' => __('Order', 'ebook-store'), |
| 129 |
'add_new' => __('Add New', 'ebook-store'), |
| 130 |
//'add_new_item' => 'Add New Order', |
| 131 |
'edit_item' => __('Edit Order', 'ebook-store'), |
| 132 |
'new_item' =>__( 'New Order', 'ebook-store'), |
| 133 |
'all_items' => __('Orders', 'ebook-store'), |
| 134 |
'view_item' => __('View Order Details', 'ebook-store'), |
| 135 |
'search_items' => __('Search Order', 'ebook-store'), |
| 136 |
'not_found' => __('No orders found', 'ebook-store'), |
| 137 |
'not_found_in_trash' => __('No orders found in Trash', 'ebook-store'), |
| 138 |
'parent_item_colon' => '', |
| 139 |
'menu_name' => __('Orders', 'ebook-store'), |
| 140 |
); |
| 141 |
|
| 142 |
$args = array( |
| 143 |
'labels' => $labels, |
| 144 |
'public' => true, |
| 145 |
'publicly_queryable' => false, |
| 146 |
'show_ui' => true, |
| 147 |
'show_in_menu' => 'edit.php?post_type=ebook', |
| 148 |
'query_var' => true, |
| 149 |
'rewrite' => array( 'slug' => 'order' ), |
| 150 |
'capability_type' => 'post', |
| 151 |
'has_archive' => false, |
| 152 |
'hierarchical' => false, |
| 153 |
//'menu_position' => 5, |
| 154 |
'supports' => array('title','excerpt'), |
| 155 |
// 'capabilities' => array( |
| 156 |
// 'order' => true, |
| 157 |
// 'read_ebook_order' => true, |
| 158 |
// 'delete_ebook_order' => true, |
| 159 |
// // 'delete_posts' => true, |
| 160 |
// // 'edit_posts' => true, |
| 161 |
// // 'edit_others_posts' => true, |
| 162 |
// // 'publish_posts' => true, |
| 163 |
// // 'read_private_posts' => false, |
| 164 |
// // 'create_posts' => false, |
| 165 |
// ), |
| 166 |
); |
| 167 |
|
| 168 |
register_post_type( 'ebook_order', $args ); |
| 169 |
|
| 170 |
$labels = array( |
| 171 |
'name' => __('Email Log', 'ebook-store'), |
| 172 |
'singular_name' => __('Email Entry', 'ebook-store'), |
| 173 |
'add_new' => __('Add New', 'ebook-store'), |
| 174 |
'edit_item' => __('View Email Entry', 'ebook-store'), |
| 175 |
'new_item' => __('New Email Entry', 'ebook-store'), |
| 176 |
'all_items' => __('Email Log', 'ebook-store'), |
| 177 |
'view_item' => __('View Email Entry', 'ebook-store'), |
| 178 |
'search_items' => __('Search Email Log', 'ebook-store'), |
| 179 |
'not_found' => __('No email log entries found', 'ebook-store'), |
| 180 |
'not_found_in_trash' => __('No email log entries found in Trash', 'ebook-store'), |
| 181 |
'parent_item_colon' => '', |
| 182 |
'menu_name' => __('Email Log', 'ebook-store'), |
| 183 |
); |
| 184 |
|
| 185 |
$args = array( |
| 186 |
'labels' => $labels, |
| 187 |
'public' => false, |
| 188 |
'publicly_queryable' => false, |
| 189 |
'show_ui' => true, |
| 190 |
'show_in_menu' => 'edit.php?post_type=ebook', |
| 191 |
'query_var' => false, |
| 192 |
'rewrite' => false, |
| 193 |
'capability_type' => 'page', |
| 194 |
'has_archive' => false, |
| 195 |
'hierarchical' => false, |
| 196 |
'supports' => array('title'), |
| 197 |
); |
| 198 |
|
| 199 |
register_post_type( 'ebook_email_log', $args ); |
| 200 |
//wp_die(print_r($GLOBALS['wp_post_types'],true)); |
| 201 |
} |
| 202 |
|
| 203 |
function ebook_add_custom_meta_boxes() { |
| 204 |
|
| 205 |
// Define the custom attachment for posts |
| 206 |
add_meta_box( |
| 207 |
'ebook_wp_custom_attachment', |
| 208 |
__('Ebook Details', 'ebook-store'), |
| 209 |
'ebook_wp_custom_attachment', |
| 210 |
'ebook', |
| 211 |
'advanced' |
| 212 |
); |
| 213 |
add_meta_box( |
| 214 |
'ebook_wp_embed_ebook', |
| 215 |
__('Ebook Store - Click on Ebook to embed it', 'ebook-store'), |
| 216 |
'ebook_wp_embed_ebook', |
| 217 |
'post', |
| 218 |
'advanced' |
| 219 |
); |
| 220 |
add_meta_box( |
| 221 |
'ebook_wp_embed_ebook', |
| 222 |
__('Ebook Store - Click Ebook to Embed', 'ebook-store'), |
| 223 |
'ebook_wp_embed_ebook', |
| 224 |
'page', |
| 225 |
'advanced' |
| 226 |
); |
| 227 |
// Define the custom attachment for posts |
| 228 |
// add_meta_box( |
| 229 |
// 'ebook_code_box', |
| 230 |
// 'Ebook Embed Code Box', |
| 231 |
// 'ebook_code_box', |
| 232 |
// 'ebook', |
| 233 |
// 'side' |
| 234 |
// ); |
| 235 |
add_meta_box( |
| 236 |
'ebook_order_box', |
| 237 |
'Order details', |
| 238 |
'ebook_order_box', |
| 239 |
'ebook_order', |
| 240 |
'advanced' |
| 241 |
); |
| 242 |
|
| 243 |
add_meta_box( |
| 244 |
'ebook_email_log_box', |
| 245 |
__('Email details', 'ebook-store'), |
| 246 |
'ebook_store_render_email_log_box', |
| 247 |
'ebook_email_log', |
| 248 |
'advanced' |
| 249 |
); |
| 250 |
} |
| 251 |
|
| 252 |
function ebook_store_get_order_field_sections() { |
| 253 |
static $sections = null; |
| 254 |
if ($sections !== null) { |
| 255 |
return $sections; |
| 256 |
} |
| 257 |
|
| 258 |
$sections = array( |
| 259 |
'summary' => array( |
| 260 |
'title' => __('Order summary', 'ebook-store'), |
| 261 |
'description' => __('Key totals and product level identifiers that are most commonly updated.', 'ebook-store'), |
| 262 |
'open' => true, |
| 263 |
'fields' => array( |
| 264 |
'item_name' => array( |
| 265 |
'label' => __('Item name', 'ebook-store'), |
| 266 |
'width' => 'full', |
| 267 |
), |
| 268 |
'item_number' => array( |
| 269 |
'label' => __('Item number / SKU', 'ebook-store'), |
| 270 |
'width' => 'half', |
| 271 |
), |
| 272 |
'quantity' => array( |
| 273 |
'label' => __('Quantity', 'ebook-store'), |
| 274 |
'width' => 'third', |
| 275 |
), |
| 276 |
'mc_gross' => array( |
| 277 |
'label' => __('Gross total', 'ebook-store'), |
| 278 |
'width' => 'third', |
| 279 |
), |
| 280 |
'payment_gross' => array( |
| 281 |
'label' => __('Payment gross', 'ebook-store'), |
| 282 |
'width' => 'third', |
| 283 |
), |
| 284 |
'tax' => array( |
| 285 |
'label' => __('Tax', 'ebook-store'), |
| 286 |
'width' => 'third', |
| 287 |
), |
| 288 |
'mc_currency' => array( |
| 289 |
'label' => __('Currency', 'ebook-store'), |
| 290 |
'width' => 'third', |
| 291 |
), |
| 292 |
'payment_status' => array( |
| 293 |
'label' => __('Payment status', 'ebook-store'), |
| 294 |
'width' => 'third', |
| 295 |
), |
| 296 |
'payment_date' => array( |
| 297 |
'label' => __('Payment date', 'ebook-store'), |
| 298 |
'width' => 'third', |
| 299 |
), |
| 300 |
), |
| 301 |
), |
| 302 |
'customer' => array( |
| 303 |
'title' => __('Customer', 'ebook-store'), |
| 304 |
'description' => __('Buyer contact information, used for receipts and encryption.', 'ebook-store'), |
| 305 |
'open' => true, |
| 306 |
'fields' => array( |
| 307 |
'first_name' => array( |
| 308 |
'label' => __('First name', 'ebook-store'), |
| 309 |
'width' => 'half', |
| 310 |
), |
| 311 |
'last_name' => array( |
| 312 |
'label' => __('Last name', 'ebook-store'), |
| 313 |
'width' => 'half', |
| 314 |
), |
| 315 |
'payer_email' => array( |
| 316 |
'label' => __('Buyer email', 'ebook-store'), |
| 317 |
'width' => 'full', |
| 318 |
), |
| 319 |
'payer_id' => array( |
| 320 |
'label' => __('Payer ID', 'ebook-store'), |
| 321 |
'width' => 'half', |
| 322 |
), |
| 323 |
'payer_status' => array( |
| 324 |
'label' => __('Payer status', 'ebook-store'), |
| 325 |
'width' => 'half', |
| 326 |
), |
| 327 |
'residence_country' => array( |
| 328 |
'label' => __('Country', 'ebook-store'), |
| 329 |
'width' => 'third', |
| 330 |
), |
| 331 |
'shipping' => array( |
| 332 |
'label' => __('Shipping', 'ebook-store'), |
| 333 |
'width' => 'third', |
| 334 |
), |
| 335 |
'handling_amount' => array( |
| 336 |
'label' => __('Handling', 'ebook-store'), |
| 337 |
'width' => 'third', |
| 338 |
), |
| 339 |
), |
| 340 |
), |
| 341 |
'transaction' => array( |
| 342 |
'title' => __('Transaction details', 'ebook-store'), |
| 343 |
'description' => __('Gateway responses and identifiers for this order.', 'ebook-store'), |
| 344 |
'open' => false, |
| 345 |
'fields' => array( |
| 346 |
'txn_id' => array( |
| 347 |
'label' => __('Transaction ID', 'ebook-store'), |
| 348 |
'width' => 'full', |
| 349 |
), |
| 350 |
'txn_type' => array( |
| 351 |
'label' => __('Transaction type', 'ebook-store'), |
| 352 |
'width' => 'half', |
| 353 |
), |
| 354 |
'payment_type' => array( |
| 355 |
'label' => __('Payment type', 'ebook-store'), |
| 356 |
'width' => 'half', |
| 357 |
), |
| 358 |
'payment_fee' => array( |
| 359 |
'label' => __('Payment fee', 'ebook-store'), |
| 360 |
'width' => 'third', |
| 361 |
), |
| 362 |
'business' => array( |
| 363 |
'label' => __('Business email', 'ebook-store'), |
| 364 |
'width' => 'half', |
| 365 |
), |
| 366 |
'receiver_email' => array( |
| 367 |
'label' => __('Receiver email', 'ebook-store'), |
| 368 |
'width' => 'half', |
| 369 |
), |
| 370 |
'receiver_id' => array( |
| 371 |
'label' => __('Receiver ID', 'ebook-store'), |
| 372 |
'width' => 'half', |
| 373 |
), |
| 374 |
'protection_eligibility' => array( |
| 375 |
'label' => __('Protection eligibility', 'ebook-store'), |
| 376 |
'width' => 'half', |
| 377 |
), |
| 378 |
), |
| 379 |
), |
| 380 |
'advanced' => array( |
| 381 |
'title' => __('Advanced metadata', 'ebook-store'), |
| 382 |
'description' => __('Less frequently edited IPN fields. Collapse this section if you do not need to tweak them.', 'ebook-store'), |
| 383 |
'open' => false, |
| 384 |
'fields' => array( |
| 385 |
'charset' => array( |
| 386 |
'label' => __('Charset', 'ebook-store'), |
| 387 |
'width' => 'third', |
| 388 |
), |
| 389 |
'mc_fee' => array( |
| 390 |
'label' => __('Gateway fee', 'ebook-store'), |
| 391 |
'width' => 'third', |
| 392 |
), |
| 393 |
'notify_version' => array( |
| 394 |
'label' => __('Notify version', 'ebook-store'), |
| 395 |
'width' => 'third', |
| 396 |
), |
| 397 |
'custom' => array( |
| 398 |
'label' => __('Custom payload', 'ebook-store'), |
| 399 |
'type' => 'textarea', |
| 400 |
'width' => 'full', |
| 401 |
), |
| 402 |
'verify_sign' => array( |
| 403 |
'label' => __('Verify signature', 'ebook-store'), |
| 404 |
'width' => 'full', |
| 405 |
), |
| 406 |
'test_ipn' => array( |
| 407 |
'label' => __('Sandbox flag', 'ebook-store'), |
| 408 |
'width' => 'third', |
| 409 |
), |
| 410 |
'transaction_subject' => array( |
| 411 |
'label' => __('Transaction subject', 'ebook-store'), |
| 412 |
'type' => 'textarea', |
| 413 |
'width' => 'full', |
| 414 |
), |
| 415 |
'ipn_track_id' => array( |
| 416 |
'label' => __('IPN Track ID', 'ebook-store'), |
| 417 |
'width' => 'half', |
| 418 |
), |
| 419 |
), |
| 420 |
), |
| 421 |
); |
| 422 |
|
| 423 |
return $sections; |
| 424 |
} |
| 425 |
|
| 426 |
function ebook_store_get_order_field_definitions() { |
| 427 |
static $definitions = null; |
| 428 |
if ($definitions !== null) { |
| 429 |
return $definitions; |
| 430 |
} |
| 431 |
$definitions = array(); |
| 432 |
foreach (ebook_store_get_order_field_sections() as $section) { |
| 433 |
foreach ($section['fields'] as $key => $config) { |
| 434 |
$definitions[$key] = $config; |
| 435 |
} |
| 436 |
} |
| 437 |
return $definitions; |
| 438 |
} |
| 439 |
|
| 440 |
function ebook_store_render_order_field($field_key, $config, $value) { |
| 441 |
$label = isset($config['label']) ? $config['label'] : $field_key; |
| 442 |
$type = isset($config['type']) ? $config['type'] : 'text'; |
| 443 |
$width = isset($config['width']) ? $config['width'] : 'half'; |
| 444 |
$input_id = 'ebook-order-field-' . sanitize_key($field_key); |
| 445 |
$description = isset($config['description']) ? $config['description'] : ''; |
| 446 |
$placeholder = isset($config['placeholder']) ? $config['placeholder'] : ''; |
| 447 |
$readonly = !empty($config['readonly']); |
| 448 |
$value = ebook_store_prepare_order_display_value($value); |
| 449 |
|
| 450 |
$classes = array('ebook-order-field', 'ebook-order-field--' . $width); |
| 451 |
echo '<div class="' . esc_attr(implode(' ', $classes)) . '">'; |
| 452 |
echo '<label for="' . esc_attr($input_id) . '">' . esc_html($label) . '</label>'; |
| 453 |
if ($type === 'textarea') { |
| 454 |
echo '<textarea id="' . esc_attr($input_id) . '" name="order[' . esc_attr($field_key) . ']" rows="3" ' . ($readonly ? 'readonly="readonly"' : '') . ' placeholder="' . esc_attr($placeholder) . '">' . esc_textarea($value) . '</textarea>'; |
| 455 |
} else { |
| 456 |
echo '<input type="text" class="regular-text" id="' . esc_attr($input_id) . '" name="order[' . esc_attr($field_key) . ']" value="' . esc_attr($value) . '" placeholder="' . esc_attr($placeholder) . '" ' . ($readonly ? 'readonly="readonly"' : '') . ' />'; |
| 457 |
} |
| 458 |
if ($description) { |
| 459 |
echo '<p class="description">' . esc_html($description) . '</p>'; |
| 460 |
} |
| 461 |
echo '</div>'; |
| 462 |
} |
| 463 |
|
| 464 |
function ebook_store_sanitize_order_field_value($value, $config) { |
| 465 |
if (is_array($value)) { |
| 466 |
$value = ''; |
| 467 |
} |
| 468 |
$type = isset($config['type']) ? $config['type'] : 'text'; |
| 469 |
if ($type === 'textarea') { |
| 470 |
return sanitize_textarea_field($value); |
| 471 |
} |
| 472 |
return sanitize_text_field($value); |
| 473 |
} |
| 474 |
|
| 475 |
function ebook_store_order_money_display( $amount, $currency = '' ) { |
| 476 |
$amount = (float) $amount; |
| 477 |
$symbol = ''; |
| 478 |
|
| 479 |
if ( class_exists( 'Currencies' ) ) { |
| 480 |
$currencies = new Currencies(); |
| 481 |
$symbol = $currencies->getSymbol( $currency ); |
| 482 |
} |
| 483 |
|
| 484 |
if ( empty( $symbol ) ) { |
| 485 |
$symbol = $currency ? $currency . ' ' : '$'; |
| 486 |
} |
| 487 |
|
| 488 |
return $symbol . number_format_i18n( $amount, 2 ); |
| 489 |
} |
| 490 |
|
| 491 |
function ebook_store_prepare_order_display_value( $value, $fallback = '' ) { |
| 492 |
if ( is_null( $value ) ) { |
| 493 |
return $fallback; |
| 494 |
} |
| 495 |
|
| 496 |
if ( is_bool( $value ) ) { |
| 497 |
return $value ? '1' : '0'; |
| 498 |
} |
| 499 |
|
| 500 |
if ( is_scalar( $value ) ) { |
| 501 |
return (string) $value; |
| 502 |
} |
| 503 |
|
| 504 |
if ( is_array( $value ) || is_object( $value ) ) { |
| 505 |
$encoded = wp_json_encode( $value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); |
| 506 |
return is_string( $encoded ) ? $encoded : $fallback; |
| 507 |
} |
| 508 |
|
| 509 |
return $fallback; |
| 510 |
} |
| 511 |
|
| 512 |
function ebook_store_order_status_class( $value ) { |
| 513 |
$value = strtolower( trim( (string) $value ) ); |
| 514 |
|
| 515 |
if ( in_array( $value, array( 'completed', 'complete', 'paid', 'success', 'verified' ), true ) ) { |
| 516 |
return 'is-success'; |
| 517 |
} |
| 518 |
|
| 519 |
if ( in_array( $value, array( 'processing', 'pending', 'on-hold', 'echeck' ), true ) ) { |
| 520 |
return 'is-warning'; |
| 521 |
} |
| 522 |
|
| 523 |
if ( in_array( $value, array( 'failed', 'denied', 'cancelled', 'canceled', 'refunded', 'reversed' ), true ) ) { |
| 524 |
return 'is-danger'; |
| 525 |
} |
| 526 |
|
| 527 |
return 'is-neutral'; |
| 528 |
} |
| 529 |
|
| 530 |
function ebook_store_render_order_badge( $label, $value, $class = '' ) { |
| 531 |
$value = ebook_store_prepare_order_display_value( $value ); |
| 532 |
|
| 533 |
if ( $value === '' ) { |
| 534 |
return ''; |
| 535 |
} |
| 536 |
|
| 537 |
$classes = trim( 'ebook-order-badge ' . $class ); |
| 538 |
|
| 539 |
return '<span class="' . esc_attr( $classes ) . '"><span class="ebook-order-badge__label">' . esc_html( $label ) . '</span><strong>' . esc_html( $value ) . '</strong></span>'; |
| 540 |
} |
| 541 |
|
| 542 |
function ebook_store_is_negative_order_status( $status ) { |
| 543 |
$status = strtolower( trim( (string) $status ) ); |
| 544 |
|
| 545 |
return in_array( $status, array( 'failed', 'denied', 'cancelled', 'canceled', 'refunded', 'reversed' ), true ); |
| 546 |
} |
| 547 |
|
| 548 |
function ebook_store_get_dashboard_cache_key() { |
| 549 |
return 'ebook_store_dashboard_summary_v1'; |
| 550 |
} |
| 551 |
|
| 552 |
function ebook_store_get_ebook_sales_index_cache_key() { |
| 553 |
return 'ebook_store_ebook_sales_index_v1'; |
| 554 |
} |
| 555 |
|
| 556 |
function ebook_store_get_ebook_catalog_summary_cache_key() { |
| 557 |
return 'ebook_store_ebook_catalog_summary_v1'; |
| 558 |
} |
| 559 |
|
| 560 |
function ebook_store_get_reports_cache_key( $ebook_id = 0 ) { |
| 561 |
$ebook_id = absint( $ebook_id ); |
| 562 |
|
| 563 |
return 'ebook_store_reports_v1_' . $ebook_id; |
| 564 |
} |
| 565 |
|
| 566 |
function ebook_store_flush_dashboard_cache( $post_id = 0 ) { |
| 567 |
if ( $post_id && ! in_array( get_post_type( $post_id ), array( 'ebook_order', 'ebook' ), true ) ) { |
| 568 |
return; |
| 569 |
} |
| 570 |
|
| 571 |
delete_transient( ebook_store_get_dashboard_cache_key() ); |
| 572 |
delete_transient( ebook_store_get_ebook_sales_index_cache_key() ); |
| 573 |
delete_transient( ebook_store_get_ebook_catalog_summary_cache_key() ); |
| 574 |
delete_transient( ebook_store_get_reports_cache_key() ); |
| 575 |
} |
| 576 |
|
| 577 |
function ebook_store_get_dashboard_summary() { |
| 578 |
$cache_key = ebook_store_get_dashboard_cache_key(); |
| 579 |
$cached = get_transient( $cache_key ); |
| 580 |
if ( false !== $cached && is_array( $cached ) ) { |
| 581 |
return $cached; |
| 582 |
} |
| 583 |
|
| 584 |
global $wpdb; |
| 585 |
|
| 586 |
$rows = $wpdb->get_results( |
| 587 |
" |
| 588 |
SELECT |
| 589 |
p.ID, |
| 590 |
p.post_date_gmt, |
| 591 |
status.meta_value AS payment_status, |
| 592 |
gross.meta_value AS mc_gross, |
| 593 |
fee.meta_value AS mc_fee, |
| 594 |
currency.meta_value AS mc_currency, |
| 595 |
ebook.meta_value AS ebook_id, |
| 596 |
item.meta_value AS item_name, |
| 597 |
quantity.meta_value AS quantity |
| 598 |
FROM {$wpdb->posts} p |
| 599 |
LEFT JOIN {$wpdb->postmeta} status ON (p.ID = status.post_id AND status.meta_key = 'payment_status') |
| 600 |
LEFT JOIN {$wpdb->postmeta} gross ON (p.ID = gross.post_id AND gross.meta_key = 'mc_gross') |
| 601 |
LEFT JOIN {$wpdb->postmeta} fee ON (p.ID = fee.post_id AND fee.meta_key = 'mc_fee') |
| 602 |
LEFT JOIN {$wpdb->postmeta} currency ON (p.ID = currency.post_id AND currency.meta_key = 'mc_currency') |
| 603 |
LEFT JOIN {$wpdb->postmeta} ebook ON (p.ID = ebook.post_id AND ebook.meta_key = 'ebook') |
| 604 |
LEFT JOIN {$wpdb->postmeta} item ON (p.ID = item.post_id AND item.meta_key = 'item_name') |
| 605 |
LEFT JOIN {$wpdb->postmeta} quantity ON (p.ID = quantity.post_id AND quantity.meta_key = 'quantity') |
| 606 |
WHERE p.post_type = 'ebook_order' |
| 607 |
AND p.post_status = 'publish' |
| 608 |
", |
| 609 |
ARRAY_A |
| 610 |
); |
| 611 |
|
| 612 |
$now_gmt = current_time( 'timestamp', true ); |
| 613 |
$last_24h_cutoff = $now_gmt - DAY_IN_SECONDS; |
| 614 |
$last_7d_cutoff = $now_gmt - ( 7 * DAY_IN_SECONDS ); |
| 615 |
$default_currency = ebook_store_get_store_currency(); |
| 616 |
|
| 617 |
$summary = array( |
| 618 |
'total_sales' => 0.0, |
| 619 |
'total_profit' => 0.0, |
| 620 |
'last_24h_sales' => 0.0, |
| 621 |
'last_7d_sales' => 0.0, |
| 622 |
'total_orders' => 0, |
| 623 |
'reportable_orders' => 0, |
| 624 |
'paid_orders' => 0, |
| 625 |
'free_orders' => 0, |
| 626 |
'average_order' => 0.0, |
| 627 |
'latest_order_gmt' => '', |
| 628 |
'latest_order_id' => 0, |
| 629 |
'latest_order_amount' => 0.0, |
| 630 |
'currency' => $default_currency, |
| 631 |
'mixed_currencies' => false, |
| 632 |
'currency_count' => 0, |
| 633 |
'top_item' => array( |
| 634 |
'label' => __( 'No ebook sales yet', 'ebook-store' ), |
| 635 |
'units' => 0, |
| 636 |
'orders' => 0, |
| 637 |
'revenue' => 0.0, |
| 638 |
'ebook_id' => 0, |
| 639 |
), |
| 640 |
); |
| 641 |
|
| 642 |
$currencies_seen = array(); |
| 643 |
$item_totals = array(); |
| 644 |
|
| 645 |
foreach ( $rows as $row ) { |
| 646 |
$summary['total_orders']++; |
| 647 |
|
| 648 |
$status = isset( $row['payment_status'] ) ? (string) $row['payment_status'] : ''; |
| 649 |
if ( ebook_store_is_negative_order_status( $status ) ) { |
| 650 |
continue; |
| 651 |
} |
| 652 |
|
| 653 |
$summary['reportable_orders']++; |
| 654 |
|
| 655 |
$gross = isset( $row['mc_gross'] ) ? (float) $row['mc_gross'] : 0.0; |
| 656 |
$fee = isset( $row['mc_fee'] ) ? (float) $row['mc_fee'] : 0.0; |
| 657 |
$net = $gross - $fee; |
| 658 |
$currency = trim( isset( $row['mc_currency'] ) ? (string) $row['mc_currency'] : '' ); |
| 659 |
$timestamp = ! empty( $row['post_date_gmt'] ) ? strtotime( $row['post_date_gmt'] . ' UTC' ) : false; |
| 660 |
$quantity = isset( $row['quantity'] ) && is_numeric( $row['quantity'] ) && (float) $row['quantity'] > 0 ? (float) $row['quantity'] : 1.0; |
| 661 |
$ebook_id = isset( $row['ebook_id'] ) ? absint( $row['ebook_id'] ) : 0; |
| 662 |
$item_name = trim( isset( $row['item_name'] ) ? wp_strip_all_tags( (string) $row['item_name'] ) : '' ); |
| 663 |
|
| 664 |
if ( $currency !== '' ) { |
| 665 |
$currencies_seen[ $currency ] = true; |
| 666 |
if ( $summary['currency'] === '' || $summary['currency'] === $default_currency ) { |
| 667 |
$summary['currency'] = $currency; |
| 668 |
} |
| 669 |
} |
| 670 |
|
| 671 |
$summary['total_sales'] += $gross; |
| 672 |
$summary['total_profit'] += $net; |
| 673 |
|
| 674 |
if ( $gross > 0 ) { |
| 675 |
$summary['paid_orders']++; |
| 676 |
} else { |
| 677 |
$summary['free_orders']++; |
| 678 |
} |
| 679 |
|
| 680 |
if ( $timestamp ) { |
| 681 |
if ( $timestamp >= $last_24h_cutoff ) { |
| 682 |
$summary['last_24h_sales'] += $gross; |
| 683 |
} |
| 684 |
if ( $timestamp >= $last_7d_cutoff ) { |
| 685 |
$summary['last_7d_sales'] += $gross; |
| 686 |
} |
| 687 |
if ( $summary['latest_order_gmt'] === '' || strtotime( $summary['latest_order_gmt'] . ' UTC' ) < $timestamp ) { |
| 688 |
$summary['latest_order_gmt'] = gmdate( 'Y-m-d H:i:s', $timestamp ); |
| 689 |
$summary['latest_order_id'] = (int) $row['ID']; |
| 690 |
$summary['latest_order_amount'] = $gross; |
| 691 |
} |
| 692 |
} |
| 693 |
|
| 694 |
$item_key = $ebook_id > 0 ? 'ebook:' . $ebook_id : 'item:' . md5( $item_name !== '' ? $item_name : __( 'Unknown ebook', 'ebook-store' ) ); |
| 695 |
if ( ! isset( $item_totals[ $item_key ] ) ) { |
| 696 |
$item_totals[ $item_key ] = array( |
| 697 |
'label' => $ebook_id > 0 ? get_the_title( $ebook_id ) : '', |
| 698 |
'units' => 0.0, |
| 699 |
'orders' => 0, |
| 700 |
'revenue' => 0.0, |
| 701 |
'ebook_id' => $ebook_id, |
| 702 |
); |
| 703 |
|
| 704 |
if ( $item_totals[ $item_key ]['label'] === '' ) { |
| 705 |
$item_totals[ $item_key ]['label'] = $item_name !== '' ? $item_name : __( 'Unknown ebook', 'ebook-store' ); |
| 706 |
} |
| 707 |
} |
| 708 |
|
| 709 |
$item_totals[ $item_key ]['units'] += $quantity; |
| 710 |
$item_totals[ $item_key ]['orders']++; |
| 711 |
$item_totals[ $item_key ]['revenue'] += $gross; |
| 712 |
} |
| 713 |
|
| 714 |
$summary['currency_count'] = count( $currencies_seen ); |
| 715 |
$summary['mixed_currencies'] = $summary['currency_count'] > 1; |
| 716 |
|
| 717 |
if ( $summary['paid_orders'] > 0 ) { |
| 718 |
$summary['average_order'] = $summary['total_sales'] / $summary['paid_orders']; |
| 719 |
} |
| 720 |
|
| 721 |
if ( ! empty( $item_totals ) ) { |
| 722 |
uasort( |
| 723 |
$item_totals, |
| 724 |
function( $left, $right ) { |
| 725 |
if ( $left['units'] === $right['units'] ) { |
| 726 |
if ( $left['revenue'] === $right['revenue'] ) { |
| 727 |
return $right['orders'] <=> $left['orders']; |
| 728 |
} |
| 729 |
return $right['revenue'] <=> $left['revenue']; |
| 730 |
} |
| 731 |
return $right['units'] <=> $left['units']; |
| 732 |
} |
| 733 |
); |
| 734 |
|
| 735 |
$summary['top_item'] = reset( $item_totals ); |
| 736 |
} |
| 737 |
|
| 738 |
set_transient( $cache_key, $summary, 10 * MINUTE_IN_SECONDS ); |
| 739 |
|
| 740 |
return $summary; |
| 741 |
} |
| 742 |
|
| 743 |
function ebook_store_render_dashboard_stat_card( $label, $value, $tone = 'default', $meta = '' ) { |
| 744 |
$classes = 'ebook-store-dashboard__stat ebook-store-dashboard__stat--' . sanitize_html_class( $tone ); |
| 745 |
|
| 746 |
echo '<div class="' . esc_attr( $classes ) . '">'; |
| 747 |
echo '<span class="ebook-store-dashboard__stat-label">' . esc_html( $label ) . '</span>'; |
| 748 |
echo '<strong class="ebook-store-dashboard__stat-value">' . esc_html( $value ) . '</strong>'; |
| 749 |
if ( $meta !== '' ) { |
| 750 |
echo '<span class="ebook-store-dashboard__stat-meta">' . esc_html( $meta ) . '</span>'; |
| 751 |
} |
| 752 |
echo '</div>'; |
| 753 |
} |
| 754 |
|
| 755 |
function ebook_store_render_dashboard_widget() { |
| 756 |
$summary = ebook_store_get_dashboard_summary(); |
| 757 |
$currency = $summary['currency']; |
| 758 |
$top_item = $summary['top_item']; |
| 759 |
$last_order_label = __( 'No recent orders yet', 'ebook-store' ); |
| 760 |
|
| 761 |
if ( ! empty( $summary['latest_order_gmt'] ) ) { |
| 762 |
$latest_timestamp = strtotime( $summary['latest_order_gmt'] . ' UTC' ); |
| 763 |
if ( $latest_timestamp ) { |
| 764 |
$last_order_label = sprintf( |
| 765 |
/* translators: 1: relative time, 2: order ID */ |
| 766 |
__( '%1$s ago, order #%2$d', 'ebook-store' ), |
| 767 |
human_time_diff( $latest_timestamp, current_time( 'timestamp', true ) ), |
| 768 |
$summary['latest_order_id'] |
| 769 |
); |
| 770 |
} |
| 771 |
} |
| 772 |
|
| 773 |
echo '<div class="ebook-store-dashboard">'; |
| 774 |
echo '<div class="ebook-store-dashboard__hero">'; |
| 775 |
echo '<div>'; |
| 776 |
echo '<span class="ebook-store-dashboard__eyebrow">' . esc_html__( 'Ebook Store', 'ebook-store' ) . '</span>'; |
| 777 |
echo '<h3 class="ebook-store-dashboard__title">' . esc_html__( 'Sales summary at a glance', 'ebook-store' ) . '</h3>'; |
| 778 |
echo '<p class="ebook-store-dashboard__summary">' . esc_html__( 'A fast snapshot of revenue, order flow, and your strongest-performing ebook directly on the WordPress dashboard.', 'ebook-store' ) . '</p>'; |
| 779 |
echo '</div>'; |
| 780 |
echo '<div class="ebook-store-dashboard__hero-meta">'; |
| 781 |
echo '<span>' . esc_html__( 'Latest order', 'ebook-store' ) . '</span>'; |
| 782 |
echo '<strong>' . esc_html( $last_order_label ) . '</strong>'; |
| 783 |
echo '</div>'; |
| 784 |
echo '</div>'; |
| 785 |
|
| 786 |
echo '<div class="ebook-store-dashboard__stats">'; |
| 787 |
ebook_store_render_dashboard_stat_card( |
| 788 |
__( 'All-time sales', 'ebook-store' ), |
| 789 |
ebook_store_order_money_display( $summary['total_sales'], $currency ), |
| 790 |
'primary', |
| 791 |
sprintf( __( '%d paid orders', 'ebook-store' ), $summary['paid_orders'] ) |
| 792 |
); |
| 793 |
ebook_store_render_dashboard_stat_card( |
| 794 |
__( 'Profit', 'ebook-store' ), |
| 795 |
ebook_store_order_money_display( $summary['total_profit'], $currency ), |
| 796 |
'success', |
| 797 |
__( 'Gross minus gateway fees', 'ebook-store' ) |
| 798 |
); |
| 799 |
ebook_store_render_dashboard_stat_card( |
| 800 |
__( 'Last 7 days', 'ebook-store' ), |
| 801 |
ebook_store_order_money_display( $summary['last_7d_sales'], $currency ), |
| 802 |
'warning', |
| 803 |
sprintf( __( '%d total reportable orders', 'ebook-store' ), $summary['reportable_orders'] ) |
| 804 |
); |
| 805 |
ebook_store_render_dashboard_stat_card( |
| 806 |
__( 'Last 24 hours', 'ebook-store' ), |
| 807 |
ebook_store_order_money_display( $summary['last_24h_sales'], $currency ), |
| 808 |
'default', |
| 809 |
sprintf( __( '%d free orders', 'ebook-store' ), $summary['free_orders'] ) |
| 810 |
); |
| 811 |
echo '</div>'; |
| 812 |
|
| 813 |
echo '<div class="ebook-store-dashboard__details">'; |
| 814 |
echo '<section class="ebook-store-dashboard__panel">'; |
| 815 |
echo '<div class="ebook-store-dashboard__panel-head">'; |
| 816 |
echo '<h4>' . esc_html__( 'Store pulse', 'ebook-store' ) . '</h4>'; |
| 817 |
echo '<p>' . esc_html__( 'Core order metrics for the current store history.', 'ebook-store' ) . '</p>'; |
| 818 |
echo '</div>'; |
| 819 |
echo '<div class="ebook-store-dashboard__metric-list">'; |
| 820 |
echo '<div><span>' . esc_html__( 'All orders', 'ebook-store' ) . '</span><strong>' . esc_html( number_format_i18n( $summary['total_orders'] ) ) . '</strong></div>'; |
| 821 |
echo '<div><span>' . esc_html__( 'Average paid order', 'ebook-store' ) . '</span><strong>' . esc_html( ebook_store_order_money_display( $summary['average_order'], $currency ) ) . '</strong></div>'; |
| 822 |
echo '<div><span>' . esc_html__( 'Latest order amount', 'ebook-store' ) . '</span><strong>' . esc_html( ebook_store_order_money_display( $summary['latest_order_amount'], $currency ) ) . '</strong></div>'; |
| 823 |
echo '</div>'; |
| 824 |
echo '</section>'; |
| 825 |
|
| 826 |
echo '<section class="ebook-store-dashboard__panel">'; |
| 827 |
echo '<div class="ebook-store-dashboard__panel-head">'; |
| 828 |
echo '<h4>' . esc_html__( 'Most sold item', 'ebook-store' ) . '</h4>'; |
| 829 |
echo '<p>' . esc_html__( 'Top performer based on total units across valid ebook orders.', 'ebook-store' ) . '</p>'; |
| 830 |
echo '</div>'; |
| 831 |
echo '<div class="ebook-store-dashboard__top-item">'; |
| 832 |
echo '<strong class="ebook-store-dashboard__top-title">' . esc_html( $top_item['label'] ) . '</strong>'; |
| 833 |
echo '<div class="ebook-store-dashboard__top-metrics">'; |
| 834 |
echo '<span>' . esc_html( sprintf( __( '%s units', 'ebook-store' ), number_format_i18n( $top_item['units'] ) ) ) . '</span>'; |
| 835 |
echo '<span>' . esc_html( sprintf( __( '%s orders', 'ebook-store' ), number_format_i18n( $top_item['orders'] ) ) ) . '</span>'; |
| 836 |
echo '<span>' . esc_html( ebook_store_order_money_display( $top_item['revenue'], $currency ) ) . '</span>'; |
| 837 |
echo '</div>'; |
| 838 |
if ( ! empty( $top_item['ebook_id'] ) ) { |
| 839 |
echo '<a class="button button-secondary" href="' . esc_url( get_edit_post_link( $top_item['ebook_id'] ) ) . '">' . esc_html__( 'Open ebook', 'ebook-store' ) . '</a>'; |
| 840 |
} |
| 841 |
echo '</div>'; |
| 842 |
echo '</section>'; |
| 843 |
echo '</div>'; |
| 844 |
|
| 845 |
echo '<div class="ebook-store-dashboard__footer">'; |
| 846 |
echo '<a class="button button-primary" href="' . esc_url( admin_url( 'edit.php?post_type=ebook_order' ) ) . '">' . esc_html__( 'View all orders', 'ebook-store' ) . '</a>'; |
| 847 |
echo '<a class="button button-secondary" href="' . esc_url( admin_url( 'edit.php?post_type=ebook&page=ebook-store-order_reports' ) ) . '">' . esc_html__( 'Open reports', 'ebook-store' ) . '</a>'; |
| 848 |
if ( $summary['mixed_currencies'] ) { |
| 849 |
echo '<span class="ebook-store-dashboard__notice">' . esc_html( sprintf( __( 'Heads up: totals combine %d currencies. Review reports if you need a per-currency breakdown.', 'ebook-store' ), $summary['currency_count'] ) ) . '</span>'; |
| 850 |
} |
| 851 |
echo '</div>'; |
| 852 |
echo '</div>'; |
| 853 |
} |
| 854 |
|
| 855 |
function ebook_store_register_dashboard_widget() { |
| 856 |
if ( ! current_user_can( 'edit_posts' ) ) { |
| 857 |
return; |
| 858 |
} |
| 859 |
|
| 860 |
wp_add_dashboard_widget( |
| 861 |
'ebook_store_dashboard_widget', |
| 862 |
__( 'Ebook Store Summary', 'ebook-store' ), |
| 863 |
'ebook_store_render_dashboard_widget' |
| 864 |
); |
| 865 |
|
| 866 |
global $wp_meta_boxes; |
| 867 |
if ( isset( $wp_meta_boxes['dashboard']['normal']['core']['ebook_store_dashboard_widget'] ) ) { |
| 868 |
$widget = array( |
| 869 |
'ebook_store_dashboard_widget' => $wp_meta_boxes['dashboard']['normal']['core']['ebook_store_dashboard_widget'], |
| 870 |
); |
| 871 |
unset( $wp_meta_boxes['dashboard']['normal']['core']['ebook_store_dashboard_widget'] ); |
| 872 |
$wp_meta_boxes['dashboard']['normal']['core'] = $widget + $wp_meta_boxes['dashboard']['normal']['core']; |
| 873 |
} |
| 874 |
} |
| 875 |
|
| 876 |
function ebook_store_render_dashboard_styles() { |
| 877 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 878 |
if ( ! $screen || $screen->base !== 'dashboard' ) { |
| 879 |
return; |
| 880 |
} |
| 881 |
?> |
| 882 |
<style> |
| 883 |
#ebook_store_dashboard_widget .inside { |
| 884 |
margin: 0; |
| 885 |
padding: 0; |
| 886 |
} |
| 887 |
.ebook-store-dashboard { |
| 888 |
padding: 18px; |
| 889 |
background: |
| 890 |
radial-gradient(circle at top right, rgba(21, 94, 117, 0.13), transparent 32%), |
| 891 |
linear-gradient(180deg, #ffffff 0%, #f7fbfc 100%); |
| 892 |
} |
| 893 |
.ebook-store-dashboard__hero { |
| 894 |
display: grid; |
| 895 |
grid-template-columns: minmax(260px, 1.6fr) minmax(180px, 0.7fr); |
| 896 |
gap: 18px; |
| 897 |
padding: 20px; |
| 898 |
border: 1px solid #d9e7ea; |
| 899 |
border-radius: 22px; |
| 900 |
background: linear-gradient(135deg, #0f766e 0%, #14532d 100%); |
| 901 |
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12); |
| 902 |
color: #f8fffd; |
| 903 |
} |
| 904 |
.ebook-store-dashboard__eyebrow { |
| 905 |
display: inline-flex; |
| 906 |
font-size: 11px; |
| 907 |
font-weight: 700; |
| 908 |
letter-spacing: 0.08em; |
| 909 |
text-transform: uppercase; |
| 910 |
opacity: 0.82; |
| 911 |
} |
| 912 |
.ebook-store-dashboard__title { |
| 913 |
margin: 8px 0 10px; |
| 914 |
font-size: 24px; |
| 915 |
line-height: 1.18; |
| 916 |
color: inherit; |
| 917 |
} |
| 918 |
.ebook-store-dashboard__summary { |
| 919 |
margin: 0; |
| 920 |
font-size: 14px; |
| 921 |
line-height: 1.7; |
| 922 |
max-width: 640px; |
| 923 |
color: rgba(248, 255, 253, 0.88); |
| 924 |
} |
| 925 |
.ebook-store-dashboard__hero-meta { |
| 926 |
align-self: start; |
| 927 |
padding: 14px 16px; |
| 928 |
border: 1px solid rgba(255, 255, 255, 0.16); |
| 929 |
border-radius: 18px; |
| 930 |
background: rgba(255, 255, 255, 0.09); |
| 931 |
backdrop-filter: blur(8px); |
| 932 |
min-width: 0; |
| 933 |
} |
| 934 |
.ebook-store-dashboard__hero-meta span { |
| 935 |
display: block; |
| 936 |
margin-bottom: 6px; |
| 937 |
font-size: 11px; |
| 938 |
font-weight: 700; |
| 939 |
letter-spacing: 0.06em; |
| 940 |
text-transform: uppercase; |
| 941 |
opacity: 0.76; |
| 942 |
} |
| 943 |
.ebook-store-dashboard__hero-meta strong { |
| 944 |
display: block; |
| 945 |
font-size: 14px; |
| 946 |
line-height: 1.5; |
| 947 |
word-break: break-word; |
| 948 |
} |
| 949 |
.ebook-store-dashboard__stats { |
| 950 |
display: grid; |
| 951 |
grid-template-columns: repeat(4, minmax(0, 1fr)); |
| 952 |
gap: 14px; |
| 953 |
margin-top: 16px; |
| 954 |
} |
| 955 |
.ebook-store-dashboard__stat { |
| 956 |
display: grid; |
| 957 |
gap: 8px; |
| 958 |
padding: 16px; |
| 959 |
border: 1px solid #dbe7ee; |
| 960 |
border-radius: 18px; |
| 961 |
background: #ffffff; |
| 962 |
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05); |
| 963 |
} |
| 964 |
.ebook-store-dashboard__stat--primary { |
| 965 |
background: linear-gradient(180deg, #f2fbff 0%, #ffffff 100%); |
| 966 |
} |
| 967 |
.ebook-store-dashboard__stat--success { |
| 968 |
background: linear-gradient(180deg, #f2fcf2 0%, #ffffff 100%); |
| 969 |
border-color: #cbe7cb; |
| 970 |
} |
| 971 |
.ebook-store-dashboard__stat--warning { |
| 972 |
background: linear-gradient(180deg, #fff9ee 0%, #ffffff 100%); |
| 973 |
border-color: #edd9b0; |
| 974 |
} |
| 975 |
.ebook-store-dashboard__stat-label { |
| 976 |
font-size: 11px; |
| 977 |
font-weight: 700; |
| 978 |
letter-spacing: 0.06em; |
| 979 |
text-transform: uppercase; |
| 980 |
color: #64748b; |
| 981 |
} |
| 982 |
.ebook-store-dashboard__stat-value { |
| 983 |
font-size: 26px; |
| 984 |
line-height: 1.15; |
| 985 |
color: #17212b; |
| 986 |
} |
| 987 |
.ebook-store-dashboard__stat-meta { |
| 988 |
font-size: 12px; |
| 989 |
line-height: 1.5; |
| 990 |
color: #5b6b78; |
| 991 |
} |
| 992 |
.ebook-store-dashboard__details { |
| 993 |
display: grid; |
| 994 |
grid-template-columns: minmax(280px, 1fr) minmax(280px, 1fr); |
| 995 |
gap: 16px; |
| 996 |
margin-top: 16px; |
| 997 |
} |
| 998 |
.ebook-store-dashboard__panel { |
| 999 |
padding: 18px; |
| 1000 |
border: 1px solid #dbe7ee; |
| 1001 |
border-radius: 20px; |
| 1002 |
background: #ffffff; |
| 1003 |
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05); |
| 1004 |
min-width: 0; |
| 1005 |
} |
| 1006 |
.ebook-store-dashboard__panel-head h4 { |
| 1007 |
margin: 0 0 4px; |
| 1008 |
font-size: 16px; |
| 1009 |
color: #17212b; |
| 1010 |
} |
| 1011 |
.ebook-store-dashboard__panel-head p { |
| 1012 |
margin: 0; |
| 1013 |
font-size: 13px; |
| 1014 |
line-height: 1.6; |
| 1015 |
color: #607080; |
| 1016 |
} |
| 1017 |
.ebook-store-dashboard__metric-list { |
| 1018 |
display: grid; |
| 1019 |
gap: 12px; |
| 1020 |
margin-top: 14px; |
| 1021 |
} |
| 1022 |
.ebook-store-dashboard__metric-list div { |
| 1023 |
display: flex; |
| 1024 |
justify-content: space-between; |
| 1025 |
gap: 16px; |
| 1026 |
padding: 12px 0; |
| 1027 |
border-bottom: 1px solid #edf2f7; |
| 1028 |
} |
| 1029 |
.ebook-store-dashboard__metric-list div:last-child { |
| 1030 |
border-bottom: 0; |
| 1031 |
padding-bottom: 0; |
| 1032 |
} |
| 1033 |
.ebook-store-dashboard__metric-list span { |
| 1034 |
color: #607080; |
| 1035 |
} |
| 1036 |
.ebook-store-dashboard__metric-list strong, |
| 1037 |
.ebook-store-dashboard__top-title { |
| 1038 |
color: #17212b; |
| 1039 |
} |
| 1040 |
.ebook-store-dashboard__top-item { |
| 1041 |
display: grid; |
| 1042 |
gap: 12px; |
| 1043 |
margin-top: 14px; |
| 1044 |
} |
| 1045 |
.ebook-store-dashboard__top-title { |
| 1046 |
font-size: 18px; |
| 1047 |
line-height: 1.35; |
| 1048 |
word-break: break-word; |
| 1049 |
} |
| 1050 |
.ebook-store-dashboard__top-metrics { |
| 1051 |
display: flex; |
| 1052 |
flex-wrap: wrap; |
| 1053 |
gap: 8px; |
| 1054 |
} |
| 1055 |
.ebook-store-dashboard__top-metrics span { |
| 1056 |
display: inline-flex; |
| 1057 |
align-items: center; |
| 1058 |
padding: 7px 10px; |
| 1059 |
border-radius: 999px; |
| 1060 |
background: #f3f8fb; |
| 1061 |
color: #28556f; |
| 1062 |
font-size: 12px; |
| 1063 |
font-weight: 600; |
| 1064 |
} |
| 1065 |
.ebook-store-dashboard__footer { |
| 1066 |
display: flex; |
| 1067 |
flex-wrap: wrap; |
| 1068 |
align-items: center; |
| 1069 |
gap: 10px; |
| 1070 |
margin-top: 16px; |
| 1071 |
} |
| 1072 |
.ebook-store-dashboard__notice { |
| 1073 |
font-size: 12px; |
| 1074 |
line-height: 1.5; |
| 1075 |
color: #8a5b12; |
| 1076 |
} |
| 1077 |
@media (max-width: 1360px) { |
| 1078 |
.ebook-store-dashboard__hero, |
| 1079 |
.ebook-store-dashboard__details { |
| 1080 |
grid-template-columns: 1fr; |
| 1081 |
} |
| 1082 |
} |
| 1083 |
@media (max-width: 1200px) { |
| 1084 |
.ebook-store-dashboard__stats { |
| 1085 |
grid-template-columns: repeat(2, minmax(0, 1fr)); |
| 1086 |
} |
| 1087 |
} |
| 1088 |
@media (max-width: 900px) { |
| 1089 |
.ebook-store-dashboard__hero, |
| 1090 |
.ebook-store-dashboard__details, |
| 1091 |
.ebook-store-dashboard__stats { |
| 1092 |
grid-template-columns: 1fr; |
| 1093 |
} |
| 1094 |
} |
| 1095 |
</style> |
| 1096 |
<?php |
| 1097 |
} |
| 1098 |
|
| 1099 |
function ebook_store_get_ebook_sales_index() { |
| 1100 |
$cache_key = ebook_store_get_ebook_sales_index_cache_key(); |
| 1101 |
$cached = get_transient( $cache_key ); |
| 1102 |
if ( false !== $cached && is_array( $cached ) ) { |
| 1103 |
return $cached; |
| 1104 |
} |
| 1105 |
|
| 1106 |
global $wpdb; |
| 1107 |
|
| 1108 |
$rows = $wpdb->get_results( |
| 1109 |
" |
| 1110 |
SELECT |
| 1111 |
p.ID, |
| 1112 |
p.post_date_gmt, |
| 1113 |
status.meta_value AS payment_status, |
| 1114 |
gross.meta_value AS mc_gross, |
| 1115 |
ebook.meta_value AS ebook_id |
| 1116 |
FROM {$wpdb->posts} p |
| 1117 |
LEFT JOIN {$wpdb->postmeta} status ON (p.ID = status.post_id AND status.meta_key = 'payment_status') |
| 1118 |
LEFT JOIN {$wpdb->postmeta} gross ON (p.ID = gross.post_id AND gross.meta_key = 'mc_gross') |
| 1119 |
LEFT JOIN {$wpdb->postmeta} ebook ON (p.ID = ebook.post_id AND ebook.meta_key = 'ebook') |
| 1120 |
WHERE p.post_type = 'ebook_order' |
| 1121 |
AND p.post_status = 'publish' |
| 1122 |
", |
| 1123 |
ARRAY_A |
| 1124 |
); |
| 1125 |
|
| 1126 |
$now_gmt = current_time( 'timestamp', true ); |
| 1127 |
$last_7d_cutoff = $now_gmt - ( 7 * DAY_IN_SECONDS ); |
| 1128 |
$last_30d_cutoff = $now_gmt - ( 30 * DAY_IN_SECONDS ); |
| 1129 |
$index = array(); |
| 1130 |
|
| 1131 |
foreach ( $rows as $row ) { |
| 1132 |
$ebook_id = isset( $row['ebook_id'] ) ? absint( $row['ebook_id'] ) : 0; |
| 1133 |
if ( $ebook_id <= 0 ) { |
| 1134 |
continue; |
| 1135 |
} |
| 1136 |
|
| 1137 |
$status = isset( $row['payment_status'] ) ? (string) $row['payment_status'] : ''; |
| 1138 |
if ( ebook_store_is_negative_order_status( $status ) ) { |
| 1139 |
continue; |
| 1140 |
} |
| 1141 |
|
| 1142 |
if ( ! isset( $index[ $ebook_id ] ) ) { |
| 1143 |
$index[ $ebook_id ] = array( |
| 1144 |
'orders' => 0, |
| 1145 |
'total' => 0.0, |
| 1146 |
'last_7d' => 0.0, |
| 1147 |
'last_30d' => 0.0, |
| 1148 |
'latest_order_gmt' => '', |
| 1149 |
); |
| 1150 |
} |
| 1151 |
|
| 1152 |
$gross = isset( $row['mc_gross'] ) ? (float) $row['mc_gross'] : 0.0; |
| 1153 |
$timestamp = ! empty( $row['post_date_gmt'] ) ? strtotime( $row['post_date_gmt'] . ' UTC' ) : false; |
| 1154 |
|
| 1155 |
$index[ $ebook_id ]['orders']++; |
| 1156 |
$index[ $ebook_id ]['total'] += $gross; |
| 1157 |
|
| 1158 |
if ( $timestamp ) { |
| 1159 |
if ( $timestamp >= $last_7d_cutoff ) { |
| 1160 |
$index[ $ebook_id ]['last_7d'] += $gross; |
| 1161 |
} |
| 1162 |
if ( $timestamp >= $last_30d_cutoff ) { |
| 1163 |
$index[ $ebook_id ]['last_30d'] += $gross; |
| 1164 |
} |
| 1165 |
if ( $index[ $ebook_id ]['latest_order_gmt'] === '' || strtotime( $index[ $ebook_id ]['latest_order_gmt'] . ' UTC' ) < $timestamp ) { |
| 1166 |
$index[ $ebook_id ]['latest_order_gmt'] = gmdate( 'Y-m-d H:i:s', $timestamp ); |
| 1167 |
} |
| 1168 |
} |
| 1169 |
} |
| 1170 |
|
| 1171 |
set_transient( $cache_key, $index, 10 * MINUTE_IN_SECONDS ); |
| 1172 |
|
| 1173 |
return $index; |
| 1174 |
} |
| 1175 |
|
| 1176 |
function ebook_store_get_ebook_catalog_summary() { |
| 1177 |
$cache_key = ebook_store_get_ebook_catalog_summary_cache_key(); |
| 1178 |
$cached = get_transient( $cache_key ); |
| 1179 |
if ( false !== $cached && is_array( $cached ) ) { |
| 1180 |
return $cached; |
| 1181 |
} |
| 1182 |
|
| 1183 |
$counts = wp_count_posts( 'ebook' ); |
| 1184 |
$ebook_ids = get_posts( |
| 1185 |
array( |
| 1186 |
'post_type' => 'ebook', |
| 1187 |
'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ), |
| 1188 |
'posts_per_page' => -1, |
| 1189 |
'fields' => 'ids', |
| 1190 |
'orderby' => 'date', |
| 1191 |
'order' => 'DESC', |
| 1192 |
'no_found_rows' => true, |
| 1193 |
) |
| 1194 |
); |
| 1195 |
|
| 1196 |
$summary = array( |
| 1197 |
'total_titles' => 0, |
| 1198 |
'published_titles' => isset( $counts->publish ) ? (int) $counts->publish : 0, |
| 1199 |
'draft_titles' => isset( $counts->draft ) ? (int) $counts->draft : 0, |
| 1200 |
'free_titles' => 0, |
| 1201 |
'paid_titles' => 0, |
| 1202 |
); |
| 1203 |
|
| 1204 |
foreach ( $ebook_ids as $ebook_id ) { |
| 1205 |
$summary['total_titles']++; |
| 1206 |
$ebook_meta = get_post_meta( $ebook_id, 'ebook', true ); |
| 1207 |
$price = 0.0; |
| 1208 |
if ( is_array( $ebook_meta ) && isset( $ebook_meta['ebook_price'] ) ) { |
| 1209 |
$price = (float) $ebook_meta['ebook_price']; |
| 1210 |
} |
| 1211 |
if ( $price > 0 ) { |
| 1212 |
$summary['paid_titles']++; |
| 1213 |
} else { |
| 1214 |
$summary['free_titles']++; |
| 1215 |
} |
| 1216 |
} |
| 1217 |
|
| 1218 |
set_transient( $cache_key, $summary, 10 * MINUTE_IN_SECONDS ); |
| 1219 |
|
| 1220 |
return $summary; |
| 1221 |
} |
| 1222 |
|
| 1223 |
function ebook_store_render_ebook_list_summary() { |
| 1224 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 1225 |
if ( ! $screen || $screen->base !== 'edit' || $screen->post_type !== 'ebook' ) { |
| 1226 |
return; |
| 1227 |
} |
| 1228 |
|
| 1229 |
$catalog = ebook_store_get_ebook_catalog_summary(); |
| 1230 |
$dashboard = ebook_store_get_dashboard_summary(); |
| 1231 |
$currency = $dashboard['currency']; |
| 1232 |
$top_item = $dashboard['top_item']; |
| 1233 |
|
| 1234 |
echo '<div class="ebook-catalog-admin">'; |
| 1235 |
echo '<section class="ebook-catalog-admin__hero">'; |
| 1236 |
echo '<div class="ebook-catalog-admin__hero-copy">'; |
| 1237 |
echo '<span class="ebook-catalog-admin__eyebrow">' . esc_html__( 'Ebook Store', 'ebook-store' ) . '</span>'; |
| 1238 |
echo '<h2 class="ebook-catalog-admin__title">' . esc_html__( 'Catalog overview', 'ebook-store' ) . '</h2>'; |
| 1239 |
echo '<p class="ebook-catalog-admin__summary">' . esc_html__( 'Manage covers, pricing, file formats, and sales performance from one cleaner inventory view. The list below is tuned for faster scanning and quicker publishing decisions.', 'ebook-store' ) . '</p>'; |
| 1240 |
echo '<div class="ebook-catalog-admin__actions">'; |
| 1241 |
echo '<a class="button button-primary" href="' . esc_url( admin_url( 'post-new.php?post_type=ebook' ) ) . '">' . esc_html__( 'Add new ebook', 'ebook-store' ) . '</a>'; |
| 1242 |
echo '<a class="button button-secondary" href="' . esc_url( admin_url( 'edit.php?post_type=ebook_order' ) ) . '">' . esc_html__( 'View orders', 'ebook-store' ) . '</a>'; |
| 1243 |
echo '<a class="button button-secondary" href="' . esc_url( admin_url( 'edit.php?post_type=ebook&page=ebook-store-order_reports' ) ) . '">' . esc_html__( 'Open reports', 'ebook-store' ) . '</a>'; |
| 1244 |
echo '<a class="button button-secondary" href="' . esc_url( admin_url( 'options-general.php?page=ebook_options.php' ) ) . '">' . esc_html__( 'Settings', 'ebook-store' ) . '</a>'; |
| 1245 |
echo '</div>'; |
| 1246 |
echo '</div>'; |
| 1247 |
echo '<div class="ebook-catalog-admin__hero-side">'; |
| 1248 |
echo '<span class="ebook-catalog-admin__hero-label">' . esc_html__( 'Best seller right now', 'ebook-store' ) . '</span>'; |
| 1249 |
echo '<strong class="ebook-catalog-admin__hero-value">' . esc_html( $top_item['label'] ) . '</strong>'; |
| 1250 |
echo '<span class="ebook-catalog-admin__hero-meta">' . esc_html( sprintf( __( '%1$s units sold, %2$s revenue', 'ebook-store' ), number_format_i18n( $top_item['units'] ), ebook_store_order_money_display( $top_item['revenue'], $currency ) ) ) . '</span>'; |
| 1251 |
echo '</div>'; |
| 1252 |
echo '</section>'; |
| 1253 |
|
| 1254 |
echo '<div class="ebook-catalog-admin__stats">'; |
| 1255 |
echo '<div class="ebook-catalog-admin__stat"><span>' . esc_html__( 'Published titles', 'ebook-store' ) . '</span><strong>' . esc_html( number_format_i18n( $catalog['published_titles'] ) ) . '</strong><small>' . esc_html__( 'Live in the catalog', 'ebook-store' ) . '</small></div>'; |
| 1256 |
echo '<div class="ebook-catalog-admin__stat"><span>' . esc_html__( 'Paid titles', 'ebook-store' ) . '</span><strong>' . esc_html( number_format_i18n( $catalog['paid_titles'] ) ) . '</strong><small>' . esc_html__( 'Pricing above zero', 'ebook-store' ) . '</small></div>'; |
| 1257 |
echo '<div class="ebook-catalog-admin__stat"><span>' . esc_html__( 'Free titles', 'ebook-store' ) . '</span><strong>' . esc_html( number_format_i18n( $catalog['free_titles'] ) ) . '</strong><small>' . esc_html__( 'Lead magnets or free downloads', 'ebook-store' ) . '</small></div>'; |
| 1258 |
echo '<div class="ebook-catalog-admin__stat ebook-catalog-admin__stat--accent"><span>' . esc_html__( 'All-time revenue', 'ebook-store' ) . '</span><strong>' . esc_html( ebook_store_order_money_display( $dashboard['total_sales'], $currency ) ) . '</strong><small>' . esc_html__( 'Across reportable ebook orders', 'ebook-store' ) . '</small></div>'; |
| 1259 |
echo '</div>'; |
| 1260 |
echo '</div>'; |
| 1261 |
} |
| 1262 |
|
| 1263 |
function ebook_store_render_ebook_list_styles() { |
| 1264 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 1265 |
if ( ! $screen || $screen->base !== 'edit' || $screen->post_type !== 'ebook' ) { |
| 1266 |
return; |
| 1267 |
} |
| 1268 |
?> |
| 1269 |
<style> |
| 1270 |
.ebook-catalog-admin { |
| 1271 |
margin: 18px 0 20px; |
| 1272 |
} |
| 1273 |
.ebook-catalog-admin__hero { |
| 1274 |
display: grid; |
| 1275 |
grid-template-columns: minmax(320px, 1.5fr) minmax(220px, 0.8fr); |
| 1276 |
gap: 18px; |
| 1277 |
padding: 22px; |
| 1278 |
border: 1px solid #d8e4ee; |
| 1279 |
border-radius: 24px; |
| 1280 |
background: |
| 1281 |
radial-gradient(circle at top right, rgba(14, 116, 144, 0.12), transparent 34%), |
| 1282 |
linear-gradient(135deg, #ffffff 0%, #f6fbff 100%); |
| 1283 |
box-shadow: 0 16px 38px rgba(15, 23, 42, 0.06); |
| 1284 |
} |
| 1285 |
.ebook-catalog-admin__eyebrow { |
| 1286 |
display: inline-flex; |
| 1287 |
font-size: 11px; |
| 1288 |
font-weight: 700; |
| 1289 |
letter-spacing: 0.08em; |
| 1290 |
text-transform: uppercase; |
| 1291 |
color: #0f5f8f; |
| 1292 |
} |
| 1293 |
.ebook-catalog-admin__title { |
| 1294 |
margin: 8px 0 10px; |
| 1295 |
font-size: 28px; |
| 1296 |
line-height: 1.15; |
| 1297 |
color: #17212b; |
| 1298 |
} |
| 1299 |
.ebook-catalog-admin__summary { |
| 1300 |
margin: 0; |
| 1301 |
max-width: 760px; |
| 1302 |
font-size: 14px; |
| 1303 |
line-height: 1.75; |
| 1304 |
color: #5b6b78; |
| 1305 |
} |
| 1306 |
.ebook-catalog-admin__actions { |
| 1307 |
display: flex; |
| 1308 |
flex-wrap: wrap; |
| 1309 |
gap: 10px; |
| 1310 |
margin-top: 16px; |
| 1311 |
} |
| 1312 |
.ebook-catalog-admin__hero-side { |
| 1313 |
align-self: start; |
| 1314 |
display: grid; |
| 1315 |
gap: 8px; |
| 1316 |
padding: 18px; |
| 1317 |
border: 1px solid #d7e7dd; |
| 1318 |
border-radius: 20px; |
| 1319 |
background: linear-gradient(180deg, #f0fbf2 0%, #ffffff 100%); |
| 1320 |
min-width: 0; |
| 1321 |
} |
| 1322 |
.ebook-catalog-admin__hero-label { |
| 1323 |
font-size: 11px; |
| 1324 |
font-weight: 700; |
| 1325 |
letter-spacing: 0.06em; |
| 1326 |
text-transform: uppercase; |
| 1327 |
color: #577161; |
| 1328 |
} |
| 1329 |
.ebook-catalog-admin__hero-value { |
| 1330 |
font-size: 18px; |
| 1331 |
line-height: 1.35; |
| 1332 |
color: #17212b; |
| 1333 |
word-break: break-word; |
| 1334 |
} |
| 1335 |
.ebook-catalog-admin__hero-meta { |
| 1336 |
font-size: 13px; |
| 1337 |
line-height: 1.6; |
| 1338 |
color: #5f6f67; |
| 1339 |
} |
| 1340 |
.ebook-catalog-admin__stats { |
| 1341 |
display: grid; |
| 1342 |
grid-template-columns: repeat(4, minmax(0, 1fr)); |
| 1343 |
gap: 14px; |
| 1344 |
margin-top: 14px; |
| 1345 |
} |
| 1346 |
.ebook-catalog-admin__stat { |
| 1347 |
display: grid; |
| 1348 |
gap: 7px; |
| 1349 |
padding: 16px 18px; |
| 1350 |
border: 1px solid #dde7f0; |
| 1351 |
border-radius: 18px; |
| 1352 |
background: #ffffff; |
| 1353 |
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04); |
| 1354 |
} |
| 1355 |
.ebook-catalog-admin__stat--accent { |
| 1356 |
background: linear-gradient(180deg, #effaf6 0%, #ffffff 100%); |
| 1357 |
border-color: #cae4d7; |
| 1358 |
} |
| 1359 |
.ebook-catalog-admin__stat span { |
| 1360 |
font-size: 11px; |
| 1361 |
font-weight: 700; |
| 1362 |
letter-spacing: 0.06em; |
| 1363 |
text-transform: uppercase; |
| 1364 |
color: #64748b; |
| 1365 |
} |
| 1366 |
.ebook-catalog-admin__stat strong { |
| 1367 |
font-size: 28px; |
| 1368 |
line-height: 1.08; |
| 1369 |
color: #17212b; |
| 1370 |
} |
| 1371 |
.ebook-catalog-admin__stat small { |
| 1372 |
font-size: 12px; |
| 1373 |
line-height: 1.55; |
| 1374 |
color: #607080; |
| 1375 |
} |
| 1376 |
.edit-php.post-type-ebook .wp-header-end { |
| 1377 |
margin-bottom: 0; |
| 1378 |
} |
| 1379 |
.edit-php.post-type-ebook .tablenav.top { |
| 1380 |
margin-top: 8px; |
| 1381 |
} |
| 1382 |
.edit-php.post-type-ebook .wp-list-table.fixed { |
| 1383 |
table-layout: auto; |
| 1384 |
} |
| 1385 |
.edit-php.post-type-ebook .column-cover { width: 132px; } |
| 1386 |
.edit-php.post-type-ebook .column-title, |
| 1387 |
.edit-php.post-type-ebook .column-title .row-title { |
| 1388 |
white-space: nowrap; |
| 1389 |
} |
| 1390 |
.edit-php.post-type-ebook .column-title { |
| 1391 |
width: auto; |
| 1392 |
} |
| 1393 |
.edit-php.post-type-ebook .column-price { width: 1%; white-space: nowrap; } |
| 1394 |
.edit-php.post-type-ebook .column-formats { width: 1%; } |
| 1395 |
.edit-php.post-type-ebook .column-sales { width: 1%; } |
| 1396 |
.edit-php.post-type-ebook .column-embed_code { |
| 1397 |
width: 320px; |
| 1398 |
min-width: 320px; |
| 1399 |
} |
| 1400 |
.edit-php.post-type-ebook td.column-sales .ebook-list-sales { |
| 1401 |
gap: 8px; |
| 1402 |
} |
| 1403 |
.edit-php.post-type-ebook #the-list td { |
| 1404 |
vertical-align: top; |
| 1405 |
} |
| 1406 |
.ebook-list-cover { |
| 1407 |
display: grid; |
| 1408 |
gap: 8px; |
| 1409 |
} |
| 1410 |
.ebook-list-cover__frame { |
| 1411 |
display: grid; |
| 1412 |
place-items: center; |
| 1413 |
width: 96px; |
| 1414 |
min-height: 132px; |
| 1415 |
padding: 6px; |
| 1416 |
border: 1px solid #d7dfeb; |
| 1417 |
border-radius: 18px; |
| 1418 |
background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%); |
| 1419 |
overflow: hidden; |
| 1420 |
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06); |
| 1421 |
} |
| 1422 |
.ebook-list-cover__frame img { |
| 1423 |
display: block; |
| 1424 |
max-width: 100%; |
| 1425 |
height: auto; |
| 1426 |
border-radius: 12px; |
| 1427 |
} |
| 1428 |
.ebook-list-cover__placeholder { |
| 1429 |
padding: 18px 12px; |
| 1430 |
font-size: 12px; |
| 1431 |
line-height: 1.5; |
| 1432 |
text-align: center; |
| 1433 |
color: #789; |
| 1434 |
} |
| 1435 |
.ebook-list-status { |
| 1436 |
display: inline-flex; |
| 1437 |
align-items: center; |
| 1438 |
justify-content: center; |
| 1439 |
padding: 5px 9px; |
| 1440 |
border-radius: 999px; |
| 1441 |
background: #eef6ff; |
| 1442 |
color: #28506c; |
| 1443 |
font-size: 11px; |
| 1444 |
font-weight: 700; |
| 1445 |
text-transform: capitalize; |
| 1446 |
} |
| 1447 |
.ebook-list-price, |
| 1448 |
.ebook-list-sales, |
| 1449 |
.ebook-list-shortcuts { |
| 1450 |
display: grid; |
| 1451 |
gap: 10px; |
| 1452 |
} |
| 1453 |
.ebook-list-price__amount, |
| 1454 |
.ebook-list-sales__amount { |
| 1455 |
font-size: 22px; |
| 1456 |
font-weight: 700; |
| 1457 |
line-height: 1.15; |
| 1458 |
color: #17212b; |
| 1459 |
} |
| 1460 |
.ebook-list-price__meta, |
| 1461 |
.ebook-list-sales__meta { |
| 1462 |
font-size: 12px; |
| 1463 |
line-height: 1.6; |
| 1464 |
color: #5b6b78; |
| 1465 |
} |
| 1466 |
.ebook-list-price__pills, |
| 1467 |
.ebook-list-sales__pills, |
| 1468 |
.ebook-list-formats { |
| 1469 |
display: flex; |
| 1470 |
flex-wrap: wrap; |
| 1471 |
gap: 6px; |
| 1472 |
} |
| 1473 |
.edit-php.post-type-ebook td.column-sales .ebook-list-sales__pills { |
| 1474 |
flex-direction: column; |
| 1475 |
align-items: flex-start; |
| 1476 |
} |
| 1477 |
.edit-php.post-type-ebook td.column-sales .ebook-list-sales__pills .ebook-list-pill { |
| 1478 |
width: 100%; |
| 1479 |
justify-content: flex-start; |
| 1480 |
} |
| 1481 |
.ebook-list-pill { |
| 1482 |
display: inline-flex; |
| 1483 |
align-items: center; |
| 1484 |
padding: 6px 9px; |
| 1485 |
border-radius: 999px; |
| 1486 |
background: #f2f7fb; |
| 1487 |
color: #305066; |
| 1488 |
font-size: 11px; |
| 1489 |
font-weight: 700; |
| 1490 |
line-height: 1.2; |
| 1491 |
} |
| 1492 |
.ebook-list-pill.is-green { |
| 1493 |
background: #edf8f0; |
| 1494 |
color: #27613b; |
| 1495 |
} |
| 1496 |
.ebook-list-pill.is-warm { |
| 1497 |
background: #fff5e8; |
| 1498 |
color: #8a5b12; |
| 1499 |
} |
| 1500 |
.ebook-list-formats__item { |
| 1501 |
display: inline-flex; |
| 1502 |
align-items: center; |
| 1503 |
justify-content: center; |
| 1504 |
width: 42px; |
| 1505 |
height: 42px; |
| 1506 |
padding: 6px; |
| 1507 |
border: 1px solid #d8e2ec; |
| 1508 |
border-radius: 14px; |
| 1509 |
background: #ffffff; |
| 1510 |
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.04); |
| 1511 |
} |
| 1512 |
.ebook-list-formats__item img { |
| 1513 |
max-width: 100%; |
| 1514 |
height: auto; |
| 1515 |
} |
| 1516 |
.ebook-format-test-link { |
| 1517 |
margin-top: 4px; |
| 1518 |
} |
| 1519 |
.ebook-format-test { |
| 1520 |
margin-top: 4px; |
| 1521 |
} |
| 1522 |
.ebook-format-test > summary { |
| 1523 |
display: inline-block; |
| 1524 |
list-style: none; |
| 1525 |
cursor: pointer; |
| 1526 |
} |
| 1527 |
.ebook-format-test > summary::-webkit-details-marker { display: none; } |
| 1528 |
.ebook-format-test__menu { |
| 1529 |
margin-top: 6px; |
| 1530 |
display: grid; |
| 1531 |
gap: 4px; |
| 1532 |
justify-items: start; |
| 1533 |
} |
| 1534 |
.ebook-format-test__prompt { |
| 1535 |
font-size: 11px; |
| 1536 |
font-weight: 700; |
| 1537 |
letter-spacing: 0.04em; |
| 1538 |
text-transform: uppercase; |
| 1539 |
color: #64748b; |
| 1540 |
} |
| 1541 |
.ebook-list-shortcuts__field { |
| 1542 |
display: grid; |
| 1543 |
gap: 4px; |
| 1544 |
} |
| 1545 |
.ebook-list-shortcuts__label { |
| 1546 |
font-size: 11px; |
| 1547 |
font-weight: 700; |
| 1548 |
letter-spacing: 0.06em; |
| 1549 |
text-transform: uppercase; |
| 1550 |
color: #64748b; |
| 1551 |
} |
| 1552 |
.ebook-list-shortcuts__control { |
| 1553 |
display: flex; |
| 1554 |
gap: 8px; |
| 1555 |
align-items: center; |
| 1556 |
} |
| 1557 |
.ebook-list-shortcuts__control input { |
| 1558 |
flex: 1 1 auto; |
| 1559 |
min-width: 220px; |
| 1560 |
border-color: #ccd8e4; |
| 1561 |
border-radius: 12px; |
| 1562 |
font-family: Consolas, Monaco, monospace; |
| 1563 |
font-size: 12px; |
| 1564 |
background: #fff; |
| 1565 |
} |
| 1566 |
.ebook-list-copy.is-copied { |
| 1567 |
border-color: #16754b; |
| 1568 |
color: #16754b; |
| 1569 |
} |
| 1570 |
.edit-php.post-type-ebook .row-title { |
| 1571 |
font-size: 15px; |
| 1572 |
font-weight: 700; |
| 1573 |
} |
| 1574 |
.edit-php.post-type-ebook .row-actions { |
| 1575 |
margin-top: 6px; |
| 1576 |
} |
| 1577 |
@media (max-width: 1400px) { |
| 1578 |
.ebook-catalog-admin__hero, |
| 1579 |
.ebook-catalog-admin__stats { |
| 1580 |
grid-template-columns: repeat(2, minmax(0, 1fr)); |
| 1581 |
} |
| 1582 |
} |
| 1583 |
@media (max-width: 900px) { |
| 1584 |
.ebook-catalog-admin__hero, |
| 1585 |
.ebook-catalog-admin__stats { |
| 1586 |
grid-template-columns: 1fr; |
| 1587 |
} |
| 1588 |
.ebook-list-shortcuts__control { |
| 1589 |
flex-direction: column; |
| 1590 |
align-items: stretch; |
| 1591 |
} |
| 1592 |
} |
| 1593 |
</style> |
| 1594 |
<script> |
| 1595 |
document.addEventListener("click", function(event){ |
| 1596 |
var button = event.target.closest(".ebook-list-copy"); |
| 1597 |
if (!button) { |
| 1598 |
return; |
| 1599 |
} |
| 1600 |
var selector = button.getAttribute("data-copy-target"); |
| 1601 |
if (!selector) { |
| 1602 |
return; |
| 1603 |
} |
| 1604 |
var input = document.querySelector(selector); |
| 1605 |
if (!input) { |
| 1606 |
return; |
| 1607 |
} |
| 1608 |
input.focus(); |
| 1609 |
input.select(); |
| 1610 |
try { |
| 1611 |
document.execCommand("copy"); |
| 1612 |
var originalText = button.textContent; |
| 1613 |
var successText = button.getAttribute("data-copied-text") || "Copied!"; |
| 1614 |
button.textContent = successText; |
| 1615 |
button.classList.add("is-copied"); |
| 1616 |
setTimeout(function(){ |
| 1617 |
button.textContent = originalText; |
| 1618 |
button.classList.remove("is-copied"); |
| 1619 |
}, 1800); |
| 1620 |
} catch (error) {} |
| 1621 |
}); |
| 1622 |
</script> |
| 1623 |
<?php |
| 1624 |
} |
| 1625 |
|
| 1626 |
function ebook_store_render_order_admin_styles() { |
| 1627 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 1628 |
if ( ! $screen ) { |
| 1629 |
return; |
| 1630 |
} |
| 1631 |
|
| 1632 |
$is_order_list = ( $screen->base === 'edit' && $screen->post_type === 'ebook_order' ); |
| 1633 |
$is_order_edit = ( $screen->base === 'post' && $screen->post_type === 'ebook_order' ); |
| 1634 |
|
| 1635 |
if ( ! $is_order_list && ! $is_order_edit ) { |
| 1636 |
return; |
| 1637 |
} |
| 1638 |
?> |
| 1639 |
<style> |
| 1640 |
.ebook-order-admin .column-order_id { width: 7%; } |
| 1641 |
.ebook-order-admin .column-title { width: 18%; } |
| 1642 |
.ebook-order-admin .column-product { width: 18%; } |
| 1643 |
.ebook-order-admin .column-amount { width: 12%; } |
| 1644 |
.ebook-order-admin .column-paypal { width: 12%; } |
| 1645 |
.ebook-order-admin .column-downloadlink { width: 15%; } |
| 1646 |
.ebook-order-admin .column-formData { width: 18%; } |
| 1647 |
.ebook-order-list { |
| 1648 |
display: flex; |
| 1649 |
flex-direction: column; |
| 1650 |
gap: 8px; |
| 1651 |
min-width: 0; |
| 1652 |
} |
| 1653 |
.ebook-order-list__title { |
| 1654 |
font-weight: 700; |
| 1655 |
color: #17212b; |
| 1656 |
line-height: 1.35; |
| 1657 |
} |
| 1658 |
.ebook-order-list__meta, |
| 1659 |
.ebook-order-list__subtle, |
| 1660 |
.ebook-order-list__stack { |
| 1661 |
font-size: 12px; |
| 1662 |
line-height: 1.55; |
| 1663 |
color: #52606d; |
| 1664 |
} |
| 1665 |
.ebook-order-list__stack { |
| 1666 |
display: flex; |
| 1667 |
flex-direction: column; |
| 1668 |
gap: 2px; |
| 1669 |
} |
| 1670 |
.ebook-order-list__badges, |
| 1671 |
.ebook-order-list__actions { |
| 1672 |
display: flex; |
| 1673 |
flex-wrap: wrap; |
| 1674 |
gap: 6px; |
| 1675 |
} |
| 1676 |
.ebook-order-badge { |
| 1677 |
display: inline-flex; |
| 1678 |
flex-direction: column; |
| 1679 |
gap: 2px; |
| 1680 |
padding: 7px 9px; |
| 1681 |
border: 1px solid #d7dfeb; |
| 1682 |
border-radius: 10px; |
| 1683 |
background: #ffffff; |
| 1684 |
min-width: 76px; |
| 1685 |
} |
| 1686 |
.ebook-order-badge__label { |
| 1687 |
font-size: 10px; |
| 1688 |
font-weight: 700; |
| 1689 |
line-height: 1.2; |
| 1690 |
letter-spacing: 0.04em; |
| 1691 |
text-transform: uppercase; |
| 1692 |
color: #64748b; |
| 1693 |
} |
| 1694 |
.ebook-order-badge strong { |
| 1695 |
font-size: 12px; |
| 1696 |
line-height: 1.3; |
| 1697 |
color: #17212b; |
| 1698 |
} |
| 1699 |
.ebook-order-badge.is-success { |
| 1700 |
border-color: #b7e3c6; |
| 1701 |
background: #edf9f1; |
| 1702 |
} |
| 1703 |
.ebook-order-badge.is-warning { |
| 1704 |
border-color: #f5ddb4; |
| 1705 |
background: #fff6e8; |
| 1706 |
} |
| 1707 |
.ebook-order-badge.is-danger { |
| 1708 |
border-color: #efc1c1; |
| 1709 |
background: #fff1f1; |
| 1710 |
} |
| 1711 |
.ebook-order-badge.is-neutral { |
| 1712 |
border-color: #d7dfeb; |
| 1713 |
background: #f8fbff; |
| 1714 |
} |
| 1715 |
.ebook-order-list__actions a { |
| 1716 |
text-decoration: none; |
| 1717 |
} |
| 1718 |
.ebook-order-list__inline-code { |
| 1719 |
display: inline-block; |
| 1720 |
padding: 3px 7px; |
| 1721 |
border-radius: 8px; |
| 1722 |
background: #f1f5f9; |
| 1723 |
color: #0f4c75; |
| 1724 |
font-family: Consolas, Monaco, monospace; |
| 1725 |
font-size: 11px; |
| 1726 |
} |
| 1727 |
.ebook-order-list__pairs { |
| 1728 |
display: grid; |
| 1729 |
gap: 4px; |
| 1730 |
} |
| 1731 |
.ebook-order-list__pair { |
| 1732 |
display: grid; |
| 1733 |
grid-template-columns: minmax(60px, 90px) 1fr; |
| 1734 |
gap: 6px; |
| 1735 |
font-size: 12px; |
| 1736 |
line-height: 1.45; |
| 1737 |
} |
| 1738 |
.ebook-order-list__pair strong { |
| 1739 |
color: #334155; |
| 1740 |
} |
| 1741 |
.ebook-order-list__empty { |
| 1742 |
color: #8a94a6; |
| 1743 |
font-style: italic; |
| 1744 |
} |
| 1745 |
.ebook-order-admin #the-list tr[type="post"] td { |
| 1746 |
vertical-align: top; |
| 1747 |
} |
| 1748 |
.ebook-order-meta-box * { box-sizing: border-box; } |
| 1749 |
.ebook-order-shell { |
| 1750 |
display: grid; |
| 1751 |
gap: 18px; |
| 1752 |
} |
| 1753 |
.ebook-order-hero { |
| 1754 |
display: grid; |
| 1755 |
grid-template-columns: minmax(260px, 1.2fr) minmax(320px, 1.4fr); |
| 1756 |
gap: 18px; |
| 1757 |
padding: 22px; |
| 1758 |
border: 1px solid #d7dfeb; |
| 1759 |
border-radius: 22px; |
| 1760 |
background: |
| 1761 |
radial-gradient(circle at top right, rgba(29, 124, 191, 0.10), transparent 35%), |
| 1762 |
linear-gradient(135deg, #ffffff 0%, #f5f9fc 100%); |
| 1763 |
box-shadow: 0 16px 34px rgba(15, 23, 42, 0.06); |
| 1764 |
} |
| 1765 |
.ebook-order-hero__eyebrow { |
| 1766 |
display: inline-flex; |
| 1767 |
font-size: 11px; |
| 1768 |
font-weight: 700; |
| 1769 |
letter-spacing: 0.08em; |
| 1770 |
text-transform: uppercase; |
| 1771 |
color: #135e96; |
| 1772 |
} |
| 1773 |
.ebook-order-hero__title { |
| 1774 |
margin: 8px 0 10px; |
| 1775 |
font-size: 24px; |
| 1776 |
line-height: 1.2; |
| 1777 |
color: #17212b; |
| 1778 |
} |
| 1779 |
.ebook-order-hero__summary { |
| 1780 |
margin: 0; |
| 1781 |
font-size: 14px; |
| 1782 |
line-height: 1.7; |
| 1783 |
color: #52606d; |
| 1784 |
} |
| 1785 |
.ebook-order-hero__meta { |
| 1786 |
display: flex; |
| 1787 |
flex-wrap: wrap; |
| 1788 |
gap: 10px; |
| 1789 |
margin-top: 16px; |
| 1790 |
} |
| 1791 |
.ebook-order-panel { |
| 1792 |
border: 1px solid #d7dfeb; |
| 1793 |
border-radius: 22px; |
| 1794 |
background: #ffffff; |
| 1795 |
box-shadow: 0 14px 32px rgba(15, 23, 42, 0.05); |
| 1796 |
overflow: hidden; |
| 1797 |
} |
| 1798 |
.ebook-order-panel__header { |
| 1799 |
display: flex; |
| 1800 |
justify-content: space-between; |
| 1801 |
gap: 16px; |
| 1802 |
align-items: flex-start; |
| 1803 |
padding: 18px 20px 14px; |
| 1804 |
border-bottom: 1px solid #ecf1f6; |
| 1805 |
} |
| 1806 |
.ebook-order-panel__header h3 { |
| 1807 |
margin: 0 0 6px; |
| 1808 |
font-size: 18px; |
| 1809 |
line-height: 1.3; |
| 1810 |
color: #17212b; |
| 1811 |
} |
| 1812 |
.ebook-order-panel__header p { |
| 1813 |
margin: 0; |
| 1814 |
font-size: 13px; |
| 1815 |
line-height: 1.6; |
| 1816 |
color: #607080; |
| 1817 |
} |
| 1818 |
.ebook-order-panel__body { |
| 1819 |
padding: 18px 20px 20px; |
| 1820 |
} |
| 1821 |
.ebook-order-quick-grid { |
| 1822 |
display: grid; |
| 1823 |
gap: 16px; |
| 1824 |
grid-template-columns: repeat(2, minmax(0, 1fr)); |
| 1825 |
} |
| 1826 |
.ebook-order-card { |
| 1827 |
padding: 16px; |
| 1828 |
border: 1px solid #e3ebf3; |
| 1829 |
border-radius: 16px; |
| 1830 |
background: #fbfdff; |
| 1831 |
} |
| 1832 |
.ebook-order-card h4 { |
| 1833 |
margin: 0 0 10px; |
| 1834 |
font-size: 14px; |
| 1835 |
color: #17212b; |
| 1836 |
} |
| 1837 |
.ebook-order-card__meta { |
| 1838 |
display: flex; |
| 1839 |
flex-wrap: wrap; |
| 1840 |
gap: 8px; |
| 1841 |
margin-bottom: 12px; |
| 1842 |
} |
| 1843 |
.ebook-order-card__rows { |
| 1844 |
display: grid; |
| 1845 |
gap: 10px; |
| 1846 |
} |
| 1847 |
.ebook-order-card__row label { |
| 1848 |
display: block; |
| 1849 |
font-weight: 600; |
| 1850 |
margin-bottom: 4px; |
| 1851 |
color: #17212b; |
| 1852 |
} |
| 1853 |
.ebook-order-card__control { |
| 1854 |
display: flex; |
| 1855 |
gap: 8px; |
| 1856 |
align-items: center; |
| 1857 |
} |
| 1858 |
.ebook-order-card__control input { |
| 1859 |
flex: 1 1 auto; |
| 1860 |
} |
| 1861 |
.ebook-order-sections { |
| 1862 |
display: grid; |
| 1863 |
gap: 16px; |
| 1864 |
} |
| 1865 |
.ebook-order-section { |
| 1866 |
border: 1px solid #d7dfeb; |
| 1867 |
border-radius: 18px; |
| 1868 |
background: #ffffff; |
| 1869 |
overflow: hidden; |
| 1870 |
} |
| 1871 |
.ebook-order-section summary { |
| 1872 |
cursor: pointer; |
| 1873 |
padding: 16px 18px; |
| 1874 |
font-weight: 700; |
| 1875 |
font-size: 14px; |
| 1876 |
color: #17212b; |
| 1877 |
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%); |
| 1878 |
border-bottom: 1px solid #ecf1f6; |
| 1879 |
list-style: none; |
| 1880 |
} |
| 1881 |
.ebook-order-section summary::-webkit-details-marker { display:none; } |
| 1882 |
.ebook-order-section__description { |
| 1883 |
margin: 0; |
| 1884 |
padding: 14px 18px 0; |
| 1885 |
color: #607080; |
| 1886 |
font-size: 13px; |
| 1887 |
line-height: 1.6; |
| 1888 |
} |
| 1889 |
.ebook-order-grid { |
| 1890 |
display: grid; |
| 1891 |
gap: 16px; |
| 1892 |
grid-template-columns: repeat(2, minmax(0, 1fr)); |
| 1893 |
padding: 18px; |
| 1894 |
} |
| 1895 |
.ebook-order-field { |
| 1896 |
min-width: 0; |
| 1897 |
} |
| 1898 |
.ebook-order-field--full { grid-column: 1 / -1; } |
| 1899 |
.ebook-order-field--half, |
| 1900 |
.ebook-order-field--third { grid-column: span 1; } |
| 1901 |
.ebook-order-field label { |
| 1902 |
font-weight: 700; |
| 1903 |
margin-bottom: 6px; |
| 1904 |
display: block; |
| 1905 |
color: #17212b; |
| 1906 |
} |
| 1907 |
.ebook-order-field input[type="text"], |
| 1908 |
.ebook-order-field textarea { |
| 1909 |
width: 100%; |
| 1910 |
border-color: #cbd6e2; |
| 1911 |
border-radius: 12px; |
| 1912 |
background: #ffffff; |
| 1913 |
} |
| 1914 |
.ebook-order-meta-box .description { |
| 1915 |
margin-top: 6px; |
| 1916 |
font-size: 12px; |
| 1917 |
color: #607080; |
| 1918 |
} |
| 1919 |
@media (max-width: 960px) { |
| 1920 |
.ebook-order-hero, |
| 1921 |
.ebook-order-quick-grid, |
| 1922 |
.ebook-order-grid { |
| 1923 |
grid-template-columns: 1fr; |
| 1924 |
} |
| 1925 |
} |
| 1926 |
</style> |
| 1927 |
<script> |
| 1928 |
document.documentElement.classList.add("ebook-order-admin"); |
| 1929 |
</script> |
| 1930 |
<?php |
| 1931 |
} |
| 1932 |
add_action( 'admin_head', 'ebook_store_render_order_admin_styles' ); |
| 1933 |
|
| 1934 |
function ebook_order_box() { |
| 1935 |
wp_nonce_field(plugin_basename(__FILE__), 'ebook_order_nonce'); |
| 1936 |
$post_id = get_the_ID(); |
| 1937 |
$sections = ebook_store_get_order_field_sections(); |
| 1938 |
$field_definitions = ebook_store_get_order_field_definitions(); |
| 1939 |
|
| 1940 |
$field_values = array(); |
| 1941 |
foreach ($field_definitions as $field_key => $config) { |
| 1942 |
$field_values[$field_key] = get_post_meta($post_id, $field_key, true); |
| 1943 |
} |
| 1944 |
|
| 1945 |
$download_link = get_post_meta($post_id, 'downloadlink', true); |
| 1946 |
$downloads = intval(get_post_meta($post_id, 'downloads', true)); |
| 1947 |
$ebook_id = intval(get_post_meta($post_id, 'ebook', true)); |
| 1948 |
$ebook_title = $ebook_id > 0 ? get_the_title($ebook_id) : ''; |
| 1949 |
$ebook_edit_link = $ebook_id > 0 ? get_edit_post_link($ebook_id) : ''; |
| 1950 |
$payment_status = isset( $field_values['payment_status'] ) ? (string) $field_values['payment_status'] : ''; |
| 1951 |
$payment_date = isset( $field_values['payment_date'] ) ? (string) $field_values['payment_date'] : ''; |
| 1952 |
$buyer_name = trim( ( isset( $field_values['first_name'] ) ? $field_values['first_name'] : '' ) . ' ' . ( isset( $field_values['last_name'] ) ? $field_values['last_name'] : '' ) ); |
| 1953 |
$buyer_email = isset( $field_values['payer_email'] ) ? (string) $field_values['payer_email'] : ''; |
| 1954 |
$country = isset( $field_values['residence_country'] ) ? (string) $field_values['residence_country'] : ''; |
| 1955 |
$payment_type = isset( $field_values['payment_type'] ) ? (string) $field_values['payment_type'] : ''; |
| 1956 |
$gateway = $payment_type !== '' ? $payment_type : __( 'PayPal', 'ebook-store' ); |
| 1957 |
$gross_total = isset( $field_values['mc_gross'] ) ? (string) $field_values['mc_gross'] : ''; |
| 1958 |
$currency = isset( $field_values['mc_currency'] ) ? (string) $field_values['mc_currency'] : ''; |
| 1959 |
$password_value = ''; |
| 1960 |
if (function_exists('ebook_store_get_password_for_order')) { |
| 1961 |
$password_value = ebook_store_get_password_for_order($post_id, array( |
| 1962 |
'payer_email' => isset($field_values['payer_email']) ? $field_values['payer_email'] : '', |
| 1963 |
)); |
| 1964 |
} else { |
| 1965 |
$password_value = get_post_meta($post_id, 'password', true); |
| 1966 |
} |
| 1967 |
$password_value = trim((string)$password_value); |
| 1968 |
|
| 1969 |
$download_input_id = 'ebook-order-download-link-' . $post_id; |
| 1970 |
$password_input_id = 'ebook-order-password-' . $post_id; |
| 1971 |
|
| 1972 |
echo '<div class="ebook-order-meta-box">'; |
| 1973 |
echo '<div class="ebook-order-shell">'; |
| 1974 |
echo '<section class="ebook-order-hero">'; |
| 1975 |
echo '<div class="ebook-order-hero__copy">'; |
| 1976 |
echo '<span class="ebook-order-hero__eyebrow">' . esc_html__( 'Ebook Order', 'ebook-store' ) . '</span>'; |
| 1977 |
echo '<h2 class="ebook-order-hero__title">' . esc_html( $ebook_title ? $ebook_title : __( 'Order without linked ebook', 'ebook-store' ) ) . '</h2>'; |
| 1978 |
$summary_bits = array(); |
| 1979 |
if ( $buyer_name !== '' ) { |
| 1980 |
$summary_bits[] = sprintf( __( 'Buyer: %s', 'ebook-store' ), $buyer_name ); |
| 1981 |
} |
| 1982 |
if ( $buyer_email !== '' ) { |
| 1983 |
$summary_bits[] = sprintf( __( 'Email: %s', 'ebook-store' ), $buyer_email ); |
| 1984 |
} |
| 1985 |
if ( $payment_date !== '' ) { |
| 1986 |
$summary_bits[] = sprintf( __( 'Paid: %s', 'ebook-store' ), $payment_date ); |
| 1987 |
} |
| 1988 |
echo '<p class="ebook-order-hero__summary">' . esc_html( implode( ' • ', $summary_bits ) ) . '</p>'; |
| 1989 |
echo '<div class="ebook-order-hero__meta">'; |
| 1990 |
echo ebook_store_render_order_badge( __( 'Order ID', 'ebook-store' ), '#' . $post_id ); |
| 1991 |
echo ebook_store_render_order_badge( __( 'Status', 'ebook-store' ), $payment_status !== '' ? $payment_status : __( 'Unknown', 'ebook-store' ), ebook_store_order_status_class( $payment_status ) ); |
| 1992 |
echo ebook_store_render_order_badge( __( 'Gateway', 'ebook-store' ), $gateway ); |
| 1993 |
if ( $gross_total !== '' ) { |
| 1994 |
echo ebook_store_render_order_badge( __( 'Gross', 'ebook-store' ), ebook_store_order_money_display( $gross_total, $currency ) ); |
| 1995 |
} |
| 1996 |
echo '</div>'; |
| 1997 |
echo '</div>'; |
| 1998 |
|
| 1999 |
echo '<div class="ebook-order-quick-grid">'; |
| 2000 |
echo '<div class="ebook-order-card">'; |
| 2001 |
echo '<h4>' . esc_html__( 'Order access', 'ebook-store' ) . '</h4>'; |
| 2002 |
echo '<div class="ebook-order-card__meta">'; |
| 2003 |
echo ebook_store_render_order_badge( __( 'Downloads', 'ebook-store' ), number_format_i18n( $downloads ) ); |
| 2004 |
echo ebook_store_render_order_badge( __( 'Country', 'ebook-store' ), $country !== '' ? $country : __( 'Not set', 'ebook-store' ) ); |
| 2005 |
echo '</div>'; |
| 2006 |
echo '<div class="ebook-order-card__rows">'; |
| 2007 |
if ($download_link) { |
| 2008 |
echo '<div class="ebook-order-card__row">'; |
| 2009 |
echo '<label for="' . esc_attr($download_input_id) . '">' . esc_html__('Download link', 'ebook-store') . '</label>'; |
| 2010 |
echo '<div class="ebook-order-card__control">'; |
| 2011 |
echo '<input type="text" id="' . esc_attr($download_input_id) . '" class="regular-text" readonly value="' . esc_attr($download_link) . '" />'; |
| 2012 |
echo '<button type="button" class="button button-secondary ebook-order-copy-btn" data-copy-target="#' . esc_attr($download_input_id) . '" data-copied-text="' . esc_attr__('Copied!', 'ebook-store') . '">' . esc_html__('Copy', 'ebook-store') . '</button>'; |
| 2013 |
echo '<a href="' . esc_url($download_link) . '" class="button" target="_blank" rel="noopener">' . esc_html__('Open', 'ebook-store') . '</a>'; |
| 2014 |
echo '</div>'; |
| 2015 |
echo '</div>'; |
| 2016 |
} |
| 2017 |
if ($password_value !== '') { |
| 2018 |
echo '<div class="ebook-order-card__row">'; |
| 2019 |
echo '<label for="' . esc_attr($password_input_id) . '">' . esc_html__('Customer PDF password', 'ebook-store') . '</label>'; |
| 2020 |
echo '<div class="ebook-order-card__control">'; |
| 2021 |
echo '<input type="text" id="' . esc_attr($password_input_id) . '" class="regular-text" readonly value="' . esc_attr($password_value) . '" />'; |
| 2022 |
echo '<button type="button" class="button button-secondary ebook-order-copy-btn" data-copy-target="#' . esc_attr($password_input_id) . '" data-copied-text="' . esc_attr__('Copied!', 'ebook-store') . '">' . esc_html__('Copy', 'ebook-store') . '</button>'; |
| 2023 |
echo '</div>'; |
| 2024 |
echo '</div>'; |
| 2025 |
} |
| 2026 |
echo '</div>'; |
| 2027 |
echo '</div>'; |
| 2028 |
|
| 2029 |
echo '<div class="ebook-order-card">'; |
| 2030 |
echo '<h4>' . esc_html__( 'Order context', 'ebook-store' ) . '</h4>'; |
| 2031 |
echo '<div class="ebook-order-card__meta">'; |
| 2032 |
if ( $ebook_title ) { |
| 2033 |
echo ebook_store_render_order_badge( __( 'Product', 'ebook-store' ), $ebook_title ); |
| 2034 |
} |
| 2035 |
if ( isset( $field_values['quantity'] ) && $field_values['quantity'] !== '' ) { |
| 2036 |
echo ebook_store_render_order_badge( __( 'Qty', 'ebook-store' ), $field_values['quantity'] ); |
| 2037 |
} |
| 2038 |
if ( isset( $field_values['txn_id'] ) && $field_values['txn_id'] !== '' ) { |
| 2039 |
echo ebook_store_render_order_badge( __( 'Txn', 'ebook-store' ), $field_values['txn_id'] ); |
| 2040 |
} |
| 2041 |
echo '</div>'; |
| 2042 |
if ($ebook_title) { |
| 2043 |
$link_open = $ebook_edit_link ? '<a href="' . esc_url($ebook_edit_link) . '">' : ''; |
| 2044 |
$link_close = $ebook_edit_link ? '</a>' : ''; |
| 2045 |
echo '<p class="description"><strong>' . esc_html__('Linked ebook', 'ebook-store') . ':</strong> ' . $link_open . esc_html($ebook_title) . $link_close . '</p>'; |
| 2046 |
} else { |
| 2047 |
echo '<p class="description">' . esc_html__('This order is not currently linked to an ebook.', 'ebook-store') . '</p>'; |
| 2048 |
} |
| 2049 |
echo '<p class="description">' . esc_html__( 'All original order fields remain editable below, grouped by task instead of one long flat form.', 'ebook-store' ) . '</p>'; |
| 2050 |
echo '</div>'; |
| 2051 |
echo '</div>'; |
| 2052 |
echo '</section>'; |
| 2053 |
|
| 2054 |
echo '<div class="ebook-order-sections">'; |
| 2055 |
foreach ($sections as $section_id => $section) { |
| 2056 |
$open_attr = !empty($section['open']) ? ' open' : ''; |
| 2057 |
echo '<details class="ebook-order-section"' . $open_attr . '>'; |
| 2058 |
echo '<summary>' . esc_html($section['title']) . '</summary>'; |
| 2059 |
if (!empty($section['description'])) { |
| 2060 |
echo '<p class="ebook-order-section__description">' . esc_html($section['description']) . '</p>'; |
| 2061 |
} |
| 2062 |
echo '<div class="ebook-order-grid">'; |
| 2063 |
foreach ($section['fields'] as $field_key => $config) { |
| 2064 |
$value = isset($field_values[$field_key]) ? $field_values[$field_key] : ''; |
| 2065 |
ebook_store_render_order_field($field_key, $config, $value); |
| 2066 |
} |
| 2067 |
echo '</div>'; |
| 2068 |
echo '</details>'; |
| 2069 |
} |
| 2070 |
echo '</div>'; // sections |
| 2071 |
echo '</div>'; // meta box wrapper/shell |
| 2072 |
|
| 2073 |
echo '<script> |
| 2074 |
(function(){ |
| 2075 |
document.addEventListener("click", function(event){ |
| 2076 |
var target = event.target; |
| 2077 |
if (!target.classList.contains("ebook-order-copy-btn")) { |
| 2078 |
return; |
| 2079 |
} |
| 2080 |
var selector = target.getAttribute("data-copy-target"); |
| 2081 |
if (!selector) { |
| 2082 |
return; |
| 2083 |
} |
| 2084 |
var input = document.querySelector(selector); |
| 2085 |
if (!input) { |
| 2086 |
return; |
| 2087 |
} |
| 2088 |
input.focus(); |
| 2089 |
input.select(); |
| 2090 |
try { |
| 2091 |
document.execCommand("copy"); |
| 2092 |
var originalText = target.textContent; |
| 2093 |
var successText = target.getAttribute("data-copied-text") || "' . esc_js(__('Copied!', 'ebook-store')) . '"; |
| 2094 |
target.textContent = successText; |
| 2095 |
target.classList.add("is-copied"); |
| 2096 |
setTimeout(function(){ |
| 2097 |
target.textContent = originalText; |
| 2098 |
target.classList.remove("is-copied"); |
| 2099 |
}, 2000); |
| 2100 |
} catch(e) {} |
| 2101 |
}); |
| 2102 |
})(); |
| 2103 |
</script>'; |
| 2104 |
} |
| 2105 |
|
| 2106 |
|
| 2107 |
function save_custom_meta_data_order($id) { |
| 2108 |
|
| 2109 |
/* --- security verification --- */ |
| 2110 |
if(!wp_verify_nonce(@$_POST['ebook_order_nonce'], plugin_basename(__FILE__))) { |
| 2111 |
return $id; |
| 2112 |
} // end if |
| 2113 |
|
| 2114 |
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { |
| 2115 |
return $id; |
| 2116 |
} // end if |
| 2117 |
|
| 2118 |
// 'edit_post' is the correct meta capability for these custom post types. |
| 2119 |
// The old code checked 'edit_page' in BOTH branches, so authorisation for |
| 2120 |
// ebooks and orders was decided by page permissions. |
| 2121 |
if ( ! current_user_can( 'edit_post', $id ) ) { |
| 2122 |
return $id; |
| 2123 |
} |
| 2124 |
/* - end security verification - */ |
| 2125 |
if (!isset($_POST['order']) || !is_array($_POST['order'])) { |
| 2126 |
return $id; |
| 2127 |
} |
| 2128 |
|
| 2129 |
$field_definitions = ebook_store_get_order_field_definitions(); |
| 2130 |
|
| 2131 |
foreach ($_POST['order'] as $k => $v) { |
| 2132 |
$config = isset($field_definitions[$k]) ? $field_definitions[$k] : array(); |
| 2133 |
$sanitized_value = ebook_store_sanitize_order_field_value($v, $config); |
| 2134 |
update_post_meta($id, $k, $sanitized_value); |
| 2135 |
} |
| 2136 |
} |
| 2137 |
|
| 2138 |
function ebook_code_box() { |
| 2139 |
wp_reset_postdata(); |
| 2140 |
$post_id = get_the_ID(); |
| 2141 |
// absint() matters here: the value is echoed inside an HTML attribute, and on |
| 2142 |
// PHP 8 a string like "1'><script>" compares > 0 as true. |
| 2143 |
$requested_post = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : 0; |
| 2144 |
if ( $requested_post > 0 ) { |
| 2145 |
$post_id = $requested_post; |
| 2146 |
} |
| 2147 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 2148 |
$locale = ebook_store_get_locale_strings(); |
| 2149 |
$formats_locale = ebook_store_get_format_labels(); |
| 2150 |
$output = '<input id="ebook_code" onClick="this.select()" readonly type="text" size="29" value=\'[ebook_store ebook_id="' . $post_id . '"]\' /> ' . __('Copy and paste this code in your post or page where you want the order form to appear.', 'ebook-store'); |
| 2151 |
echo function_exists( 'ebook_store_translate_markup' ) ? ebook_store_translate_markup( $output ) : $output; |
| 2152 |
} |
| 2153 |
function ebook_wp_custom_attachment() { |
| 2154 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 2155 |
$locale = ebook_store_get_locale_strings(); |
| 2156 |
$formats_locale = ebook_store_get_format_labels(); |
| 2157 |
$ebook_id = get_the_ID(); |
| 2158 |
|
| 2159 |
// 'fields' => 'ids' keeps this to one lean query instead of hydrating every |
| 2160 |
// ebook (and its whole meta table) just to build an <option> list. |
| 2161 |
$ebook_ids = get_posts( array( 'post_type' => 'ebook', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'orderby' => 'title', 'order' => 'ASC' ) ); |
| 2162 |
$ebook_meta = get_post_meta($ebook_id, 'ebook', true); |
| 2163 |
if (!is_array($ebook_meta)) { |
| 2164 |
$ebook_meta = array(); |
| 2165 |
} |
| 2166 |
|
| 2167 |
if (!isset($ebook_meta['ebook_bonus']) || !is_array($ebook_meta['ebook_bonus'])) { |
| 2168 |
$ebook_meta['ebook_bonus'] = array(); |
| 2169 |
} |
| 2170 |
|
| 2171 |
$ebook_bonus_options = '<option value="">' . esc_html__('Please select', 'ebook-store') . '</option>'; |
| 2172 |
foreach ( $ebook_ids as $bonus_id ) { |
| 2173 |
if ( (int) $bonus_id === (int) $ebook_id ) { |
| 2174 |
continue; |
| 2175 |
} |
| 2176 |
$selected = in_array( $bonus_id, $ebook_meta['ebook_bonus'], true ) ? ' selected="selected"' : ''; |
| 2177 |
$title = get_the_title( $bonus_id ); |
| 2178 |
$title = $title !== '' ? $title : __( '(no title)', 'ebook-store' ); |
| 2179 |
$ebook_bonus_options .= '<option value="' . esc_attr( $bonus_id ) . '"' . $selected . '>' . esc_html( $title ) . '</option>'; |
| 2180 |
} |
| 2181 |
|
| 2182 |
wp_enqueue_script('jquery-ui-datepicker'); |
| 2183 |
// The jQuery UI datepicker styling comes from core rather than a 2010-era |
| 2184 |
// stylesheet on Google's CDN, which also leaked every admin's IP to Google. |
| 2185 |
wp_enqueue_style( 'wp-jquery-ui-dialog' ); |
| 2186 |
wp_enqueue_script( 'ebook_store_settings', plugins_url( '/js/ebook_store_settings.js' , __FILE__ ), array(), '1.0.0', true ); |
| 2187 |
|
| 2188 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 2189 |
|
| 2190 |
$img = get_post_meta($ebook_id, 'ebook_wp_custom_attachment', true); |
| 2191 |
$img = is_array($img) ? $img : array(); |
| 2192 |
|
| 2193 |
$img_side_photo = get_post_meta($ebook_id, 'ebook_wp_custom_attachment_side_photo', true); |
| 2194 |
$img_side_photo = is_array($img_side_photo) ? $img_side_photo : array(); |
| 2195 |
|
| 2196 |
$img_cover = get_post_meta($ebook_id, 'ebook_wp_custom_attachment_cover', true); |
| 2197 |
$img_cover = is_array($img_cover) ? $img_cover : array(); |
| 2198 |
|
| 2199 |
$preview = get_post_meta($ebook_id, 'ebook_wp_custom_attachment_preview', true); |
| 2200 |
$preview = is_array($preview) ? $preview : array(); |
| 2201 |
|
| 2202 |
wp_nonce_field(plugin_basename(__FILE__), 'ebook_wp_custom_attachment_nonce'); |
| 2203 |
|
| 2204 |
$selected_authors = isset($ebook_meta['ebook_author']) && is_array($ebook_meta['ebook_author']) ? $ebook_meta['ebook_author'] : array(); |
| 2205 |
$selected_publishers = isset($ebook_meta['ebook_publisher']) && is_array($ebook_meta['ebook_publisher']) ? $ebook_meta['ebook_publisher'] : array(); |
| 2206 |
|
| 2207 |
$ebook_authors = ''; |
| 2208 |
foreach ( get_posts( array( 'post_type' => 'ebook_author', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'orderby' => 'title', 'order' => 'ASC' ) ) as $author_id ) { |
| 2209 |
$selected = in_array( $author_id, $selected_authors, true ) ? ' selected' : ''; |
| 2210 |
$ebook_authors .= '<option value="' . esc_attr( $author_id ) . '"' . $selected . '>' . esc_html( get_the_title( $author_id ) ) . '</option>'; |
| 2211 |
} |
| 2212 |
|
| 2213 |
$ebook_publishers = ''; |
| 2214 |
foreach ( get_posts( array( 'post_type' => 'ebook_publisher', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'orderby' => 'title', 'order' => 'ASC' ) ) as $publisher_id ) { |
| 2215 |
$selected = in_array( $publisher_id, $selected_publishers, true ) ? ' selected' : ''; |
| 2216 |
$ebook_publishers .= '<option value="' . esc_attr( $publisher_id ) . '"' . $selected . '>' . esc_html( get_the_title( $publisher_id ) ) . '</option>'; |
| 2217 |
} |
| 2218 |
|
| 2219 |
$currencies = array( |
| 2220 |
'' => '-- Optional --', |
| 2221 |
'USD' => 'US Dollar', |
| 2222 |
'EUR' => 'Euro', |
| 2223 |
'ILS' => 'Israeli New Sheqel', |
| 2224 |
'INR' => 'Indian Rupee', |
| 2225 |
'GBP' => 'Pounds Sterling', |
| 2226 |
'AUD' => 'Australian Dollar', |
| 2227 |
'CAD' => 'Canadian Dollar', |
| 2228 |
'JPY' => 'Japan Yen', |
| 2229 |
'NZD' => 'New Zealand Dollar', |
| 2230 |
'CHF' => 'Swiss Franc', |
| 2231 |
'HKD' => 'Hong Kong Dollar', |
| 2232 |
'SGD' => 'Singapore Dollar', |
| 2233 |
'SEK' => 'Sweden Krona', |
| 2234 |
'DKK' => 'Danish Krone', |
| 2235 |
'PLN' => 'New Zloty', |
| 2236 |
'NOK' => 'Norwegian Krone', |
| 2237 |
'HUF' => 'Forint', |
| 2238 |
'MXN' => 'Mexican Pesos', |
| 2239 |
'CZK' => 'Czech Koruna', |
| 2240 |
'BRL' => 'Brazilian Real', |
| 2241 |
'TWD' => 'Taiwan New Dollar', |
| 2242 |
'TRY' => 'Turkish Lira', |
| 2243 |
'MYR' => 'Malaysian Ringgit', |
| 2244 |
'ZAR' => 'South African Rands', |
| 2245 |
'NGN' => 'Nigerian Naira', |
| 2246 |
'RON' => 'Romanian leu', |
| 2247 |
'THB' => 'Thai Baht' |
| 2248 |
); |
| 2249 |
|
| 2250 |
$selected_currency = isset($ebook_meta['paypal_currency']) ? $ebook_meta['paypal_currency'] : esc_attr(get_option('paypal_currency','USD')); |
| 2251 |
$paypal_currency_options = ''; |
| 2252 |
foreach ($currencies as $currency => $name) { |
| 2253 |
$is_selected = $currency === $selected_currency ? ' selected' : ''; |
| 2254 |
$paypal_currency_options .= '<option value="' . esc_attr($currency) . '"' . $is_selected . '>' . esc_html($name) . '</option>'; |
| 2255 |
} |
| 2256 |
$form_templates = ebook_store_get_form_templates(); |
| 2257 |
$selected_form_template = isset($ebook_meta['form_template']) ? sanitize_key($ebook_meta['form_template']) : ''; |
| 2258 |
$selected_form_template = ebook_store_resolve_form_template($selected_form_template); |
| 2259 |
if ($selected_form_template === '' || !isset($form_templates[$selected_form_template])) { |
| 2260 |
$selected_form_template = ebook_store_resolve_form_template(''); |
| 2261 |
} |
| 2262 |
$form_template_options = ''; |
| 2263 |
foreach ($form_templates as $template_key => $template_config) { |
| 2264 |
$template_label = isset($template_config['label']) ? $template_config['label'] : $template_key; |
| 2265 |
$form_template_options .= '<option value="' . esc_attr($template_key) . '"' . selected($selected_form_template, $template_key, false) . '>' . esc_html($template_label) . '</option>'; |
| 2266 |
} |
| 2267 |
|
| 2268 |
$embed_shortcode = sprintf('[ebook_store ebook_id="%d"]', $ebook_id); |
| 2269 |
$buy_shortcode = sprintf('[ebook_store_buy ebook_id="%d"]', $ebook_id); |
| 2270 |
$max_upload_size = ini_get('upload_max_filesize') . 'B'; |
| 2271 |
$license_key = get_option('ebook_store_license_key'); |
| 2272 |
$upgrade_text = ''; |
| 2273 |
if ($license_key == '') { |
| 2274 |
$upgrade_text = 'Supported file types in free version are: <b style="color:green;">PDF</b>, to use <b style="color:red;">EPUB</b>, <b style="color:red;">MOBI</b>, <b style="color:red;">MP3</b>, <b style="color:red;">TXT</b> and <b style="color:red;">ZIP</b> you can get the full version from here: <a target="_blank" href="https://www.shopfiles.com/index.php/products/wordpress-ebook-store">Upgrade Ebook Store</a>, no data will be lost upon upgrade.'; |
| 2275 |
} |
| 2276 |
|
| 2277 |
$conversion_hint = ''; |
| 2278 |
if (empty($img['url'])) { |
| 2279 |
$conversion_hint = sprintf( |
| 2280 |
__('<strong>Need help converting files?</strong> Try <a target="_blank" href="%s">Calibre</a>, a free desktop tool for generating every major ebook format.', 'ebook-store'), |
| 2281 |
'http://calibre-ebook.com/download' |
| 2282 |
); |
| 2283 |
} |
| 2284 |
|
| 2285 |
$cover_size = ''; |
| 2286 |
if (!empty($img_cover['file']) && file_exists($img_cover['file'])) { |
| 2287 |
$cover_size = humanFileSize(filesize($img_cover['file'])); |
| 2288 |
} |
| 2289 |
|
| 2290 |
$side_size = ''; |
| 2291 |
if (!empty($img_side_photo['file']) && file_exists($img_side_photo['file'])) { |
| 2292 |
$side_size = humanFileSize(filesize($img_side_photo['file'])); |
| 2293 |
} |
| 2294 |
|
| 2295 |
$donate_or_download = isset($ebook_meta['donate_or_download']) ? $ebook_meta['donate_or_download'] : 'paid'; |
| 2296 |
$extendhtml = apply_filters('ebook_store_form_extend', get_the_ID()); |
| 2297 |
$sales_index = ebook_store_get_ebook_sales_index(); |
| 2298 |
$current_sales = isset($sales_index[$ebook_id]) ? $sales_index[$ebook_id] : array( |
| 2299 |
'orders' => 0, |
| 2300 |
'total' => 0.0, |
| 2301 |
'last_7d' => 0.0, |
| 2302 |
'last_30d' => 0.0, |
| 2303 |
'latest_order_gmt' => '', |
| 2304 |
); |
| 2305 |
$current_price = isset($ebook_meta['ebook_price']) ? (float) $ebook_meta['ebook_price'] : 0.0; |
| 2306 |
$current_currency = $selected_currency !== '' ? $selected_currency : esc_attr(get_option('paypal_currency','USD')); |
| 2307 |
$current_status = get_post_status($ebook_id); |
| 2308 |
$current_title = get_the_title($ebook_id); |
| 2309 |
if ($current_title === '') { |
| 2310 |
$current_title = __('Untitled ebook', 'ebook-store'); |
| 2311 |
} |
| 2312 |
$current_permalink = $ebook_id ? get_permalink($ebook_id) : ''; |
| 2313 |
$current_latest_order = $current_sales['latest_order_gmt'] !== '' ? human_time_diff(strtotime($current_sales['latest_order_gmt'] . ' UTC'), current_time('timestamp', true)) : ''; |
| 2314 |
$delivery_mode_label = __('Paid download', 'ebook-store'); |
| 2315 |
if ($donate_or_download === 'free') { |
| 2316 |
$delivery_mode_label = __('Free download', 'ebook-store'); |
| 2317 |
} elseif ($donate_or_download === 'donate') { |
| 2318 |
$delivery_mode_label = __('Donate to download', 'ebook-store'); |
| 2319 |
} |
| 2320 |
$template_label = isset($form_templates[$selected_form_template]['label']) ? $form_templates[$selected_form_template]['label'] : ucfirst($selected_form_template); |
| 2321 |
$cover_preview_url = !empty($img_cover['url']) ? $img_cover['url'] : ''; |
| 2322 |
$cover_preview_note = $cover_preview_url ? basename($cover_preview_url) : __('No cover uploaded yet', 'ebook-store'); |
| 2323 |
$template_override_settings = ebook_store_get_ebook_template_override_settings($ebook_id); |
| 2324 |
$enable_custom_confirmation_page = !empty($template_override_settings['enable_custom_confirmation_page']); |
| 2325 |
$enable_custom_delivery_email = !empty($template_override_settings['enable_custom_delivery_email']); |
| 2326 |
$custom_confirmation_page_content = isset($template_override_settings['confirmation_page_content']) ? $template_override_settings['confirmation_page_content'] : ''; |
| 2327 |
$custom_delivery_email_subject = isset($template_override_settings['delivery_email_subject']) ? $template_override_settings['delivery_email_subject'] : ''; |
| 2328 |
$custom_delivery_email_text = isset($template_override_settings['delivery_email_text']) ? $template_override_settings['delivery_email_text'] : ''; |
| 2329 |
|
| 2330 |
ob_start(); |
| 2331 |
?> |
| 2332 |
<script>var ebook_store_license_key = '<?php echo esc_js($license_key); ?>'; </script> |
| 2333 |
<div class="ebook-admin-panel ebook-admin-panel--workspace"> |
| 2334 |
<section class="ebook-editor-hero"> |
| 2335 |
<div class="ebook-editor-hero__cover"> |
| 2336 |
<div class="ebook-editor-hero__cover-frame <?php echo $cover_preview_url ? 'has-image' : 'is-empty'; ?>"> |
| 2337 |
<?php if ($cover_preview_url) : ?> |
| 2338 |
<img src="<?php echo esc_url($cover_preview_url); ?>" alt="<?php echo esc_attr($current_title); ?>"> |
| 2339 |
<?php else : ?> |
| 2340 |
<span class="ebook-editor-hero__cover-placeholder"><?php esc_html_e('Cover preview', 'ebook-store'); ?></span> |
| 2341 |
<?php endif; ?> |
| 2342 |
</div> |
| 2343 |
<span class="ebook-editor-hero__cover-meta"><?php echo esc_html($cover_preview_note); ?></span> |
| 2344 |
</div> |
| 2345 |
<div class="ebook-editor-hero__content"> |
| 2346 |
<span class="ebook-editor-hero__eyebrow"><?php esc_html_e('Ebook editor', 'ebook-store'); ?></span> |
| 2347 |
<h2 class="ebook-editor-hero__title"><?php echo esc_html($current_title); ?></h2> |
| 2348 |
<p class="ebook-editor-hero__summary"><?php esc_html_e('Set up pricing, delivery, files, and storefront media in one cleaner product workflow. Everything below keeps the existing save behavior, but the layout is optimized for faster publishing.', 'ebook-store'); ?></p> |
| 2349 |
<div class="ebook-editor-hero__badges"> |
| 2350 |
<span class="ebook-editor-badge"><?php esc_html_e('Status', 'ebook-store'); ?> <strong><?php echo esc_html($current_status ? $current_status : __('draft', 'ebook-store')); ?></strong></span> |
| 2351 |
<span class="ebook-editor-badge"><?php esc_html_e('Mode', 'ebook-store'); ?> <strong><?php echo esc_html($delivery_mode_label); ?></strong></span> |
| 2352 |
<span class="ebook-editor-badge"><?php esc_html_e('Template', 'ebook-store'); ?> <strong><?php echo esc_html($template_label); ?></strong></span> |
| 2353 |
<span class="ebook-editor-badge"><?php esc_html_e('Price', 'ebook-store'); ?> <strong><?php echo esc_html(ebook_store_order_money_display($current_price, $current_currency)); ?></strong></span> |
| 2354 |
<span class="ebook-editor-badge"><?php esc_html_e('Orders', 'ebook-store'); ?> <strong><?php echo esc_html(number_format_i18n($current_sales['orders'])); ?></strong></span> |
| 2355 |
</div> |
| 2356 |
</div> |
| 2357 |
<div class="ebook-editor-hero__side"> |
| 2358 |
<div class="ebook-editor-hero__stat"> |
| 2359 |
<span><?php esc_html_e('Revenue', 'ebook-store'); ?></span> |
| 2360 |
<strong><?php echo esc_html(ebook_store_order_money_display($current_sales['total'], $current_currency)); ?></strong> |
| 2361 |
<small><?php echo esc_html(sprintf(__('Last 30 days: %s', 'ebook-store'), ebook_store_order_money_display($current_sales['last_30d'], $current_currency))); ?></small> |
| 2362 |
</div> |
| 2363 |
<div class="ebook-editor-hero__stat"> |
| 2364 |
<span><?php esc_html_e('Latest order', 'ebook-store'); ?></span> |
| 2365 |
<strong><?php echo esc_html($current_latest_order ? sprintf(__('%s ago', 'ebook-store'), $current_latest_order) : __('No orders yet', 'ebook-store')); ?></strong> |
| 2366 |
<small><?php printf(esc_html__('Upload max: %s', 'ebook-store'), esc_html($max_upload_size)); ?></small> |
| 2367 |
</div> |
| 2368 |
<?php if ($current_permalink) : ?> |
| 2369 |
<a class="button button-secondary" href="<?php echo esc_url($current_permalink); ?>" target="_blank" rel="noopener"><?php esc_html_e('View ebook page', 'ebook-store'); ?></a> |
| 2370 |
<?php endif; ?> |
| 2371 |
</div> |
| 2372 |
</section> |
| 2373 |
|
| 2374 |
<div class="ebook-editor-layout"> |
| 2375 |
<div class="ebook-editor-layout__main"> |
| 2376 |
<section class="ebook-card ebook-card--summary"> |
| 2377 |
<div class="ebook-card__header"> |
| 2378 |
<h3><?php esc_html_e('Embed & launch tools', 'ebook-store'); ?></h3> |
| 2379 |
<p><?php esc_html_e('Copy the order form or buy-button shortcode and jump straight into placement on your content pages.', 'ebook-store'); ?></p> |
| 2380 |
</div> |
| 2381 |
<div class="ebook-card__body"> |
| 2382 |
<div class="ebook-code-grid"> |
| 2383 |
<div class="ebook-code-block"> |
| 2384 |
<label for="ebook_code"><?php esc_html_e('Order form shortcode', 'ebook-store'); ?></label> |
| 2385 |
<div class="ebook-code-field"> |
| 2386 |
<input id="ebook_code" onClick="this.select()" readonly type="text" class="regular-text code" value="<?php echo esc_attr($embed_shortcode); ?>" /> |
| 2387 |
<button type="button" class="button button-secondary ebook-code__copy" data-copy-target="#ebook_code"><?php esc_html_e('Copy', 'ebook-store'); ?></button> |
| 2388 |
<span class="ebook-code__status" aria-live="polite"></span> |
| 2389 |
</div> |
| 2390 |
</div> |
| 2391 |
<div class="ebook-code-block"> |
| 2392 |
<label for="ebook_code_buy"><?php esc_html_e('Buy button shortcode', 'ebook-store'); ?></label> |
| 2393 |
<div class="ebook-code-field"> |
| 2394 |
<input id="ebook_code_buy" onClick="this.select()" readonly type="text" class="regular-text code" value="<?php echo esc_attr($buy_shortcode); ?>" /> |
| 2395 |
<button type="button" class="button button-secondary ebook-code__copy" data-copy-target="#ebook_code_buy"><?php esc_html_e('Copy', 'ebook-store'); ?></button> |
| 2396 |
<span class="ebook-code__status" aria-live="polite"></span> |
| 2397 |
</div> |
| 2398 |
</div> |
| 2399 |
</div> |
| 2400 |
<ul class="ebook-inline-meta"> |
| 2401 |
<li><?php esc_html_e('Use the main shortcode when you want the full order form to render on a page or post.', 'ebook-store'); ?></li> |
| 2402 |
<li><?php esc_html_e('Use the buy-button shortcode when you only need a compact call to action.', 'ebook-store'); ?></li> |
| 2403 |
</ul> |
| 2404 |
<?php if ($conversion_hint) : ?> |
| 2405 |
<div class="ebook-inline-hint"><?php echo wp_kses_post($conversion_hint); ?></div> |
| 2406 |
<?php endif; ?> |
| 2407 |
<?php if ($upgrade_text) : ?> |
| 2408 |
<div class="ebook-alert ebook-alert--upgrade"><?php echo wp_kses_post($upgrade_text); ?></div> |
| 2409 |
<?php endif; ?> |
| 2410 |
</div> |
| 2411 |
</section> |
| 2412 |
|
| 2413 |
<section class="ebook-card ebook-card--delivery"> |
| 2414 |
<div class="ebook-card__header"> |
| 2415 |
<h3><?php esc_html_e('Pricing & delivery', 'ebook-store'); ?></h3> |
| 2416 |
<p><?php esc_html_e('Decide whether this title is paid, free, or donation-based, then set the selling price buyers should see.', 'ebook-store'); ?></p> |
| 2417 |
</div> |
| 2418 |
<div class="ebook-card__body"> |
| 2419 |
<div class="ebook-field-grid ebook-field-grid--tight"> |
| 2420 |
<div class="ebook-field"> |
| 2421 |
<label for="ebook_price"><?php esc_html_e('Price', 'ebook-store'); ?> <span class="required">*</span></label> |
| 2422 |
<input id="ebook_price" name="ebook[ebook_price]" placeholder="0.00" min="0" type="number" step="any" value="<?php echo isset($ebook_meta['ebook_price']) ? esc_attr($ebook_meta['ebook_price']) : ''; ?>"> |
| 2423 |
<p class="description"><?php esc_html_e('For paid mode, zero is automatically adjusted to a minimum amount on save.', 'ebook-store'); ?></p> |
| 2424 |
</div> |
| 2425 |
<div class="ebook-field"> |
| 2426 |
<label for="ebook_paypal_currency"><?php esc_html_e('Currency', 'ebook-store'); ?></label> |
| 2427 |
<select id="ebook_paypal_currency" name="ebook[paypal_currency]"> |
| 2428 |
<?php echo $paypal_currency_options; ?> |
| 2429 |
</select> |
| 2430 |
<p class="description"><?php esc_html_e('Leave this aligned with your store currency unless a title needs a specific override.', 'ebook-store'); ?></p> |
| 2431 |
</div> |
| 2432 |
<div class="ebook-field"> |
| 2433 |
<label for="ebook_form_template"><?php esc_html_e('Order form template', 'ebook-store'); ?></label> |
| 2434 |
<select id="ebook_form_template" name="ebook[form_template]"> |
| 2435 |
<?php echo $form_template_options; ?> |
| 2436 |
</select> |
| 2437 |
<p class="description"><?php esc_html_e('This becomes the default frontend template for shortcode embeds of this ebook. Shortcode `template=\"...\"` still overrides it.', 'ebook-store'); ?></p> |
| 2438 |
</div> |
| 2439 |
</div> |
| 2440 |
<div class="ebook-delivery-options"> |
| 2441 |
<label class="ebook-delivery-option"> |
| 2442 |
<input name="ebook[donate_or_download]" type="radio" value="paid" <?php checked($donate_or_download === 'paid' || $donate_or_download === ''); ?>> |
| 2443 |
<div class="ebook-delivery-option__content"> |
| 2444 |
<span class="ebook-delivery-option__title"><?php esc_html_e('Paid download', 'ebook-store'); ?></span> |
| 2445 |
<span class="ebook-delivery-option__description"><?php esc_html_e('Route buyers through checkout, confirm payment, and then release the file securely.', 'ebook-store'); ?></span> |
| 2446 |
</div> |
| 2447 |
</label> |
| 2448 |
<label class="ebook-delivery-option goPro2"> |
| 2449 |
<input class="goPro2" name="ebook[donate_or_download]" type="radio" value="free" <?php checked($donate_or_download, 'free'); ?>> |
| 2450 |
<div class="ebook-delivery-option__content"> |
| 2451 |
<span class="ebook-delivery-option__title"><?php esc_html_e('Free download', 'ebook-store'); ?></span> |
| 2452 |
<span class="ebook-delivery-option__description"><?php esc_html_e('Skip payment entirely and hand off the file immediately after the visitor requests it.', 'ebook-store'); ?></span> |
| 2453 |
</div> |
| 2454 |
</label> |
| 2455 |
<label class="ebook-delivery-option goPro2"> |
| 2456 |
<input class="goPro2" name="ebook[donate_or_download]" type="radio" value="donate" <?php checked($donate_or_download, 'donate'); ?>> |
| 2457 |
<div class="ebook-delivery-option__content"> |
| 2458 |
<span class="ebook-delivery-option__title"><?php esc_html_e('Donate to download', 'ebook-store'); ?></span> |
| 2459 |
<span class="ebook-delivery-option__description"><?php esc_html_e('Allow supporters to choose their own amount while still using the checkout flow.', 'ebook-store'); ?></span> |
| 2460 |
</div> |
| 2461 |
</label> |
| 2462 |
</div> |
| 2463 |
</div> |
| 2464 |
</section> |
| 2465 |
|
| 2466 |
<section class="ebook-card ebook-card--files"> |
| 2467 |
<div class="ebook-card__header"> |
| 2468 |
<h3><?php esc_html_e('Files & formats', 'ebook-store'); ?></h3> |
| 2469 |
<p><?php esc_html_e('Upload the core PDF first, then add optional EPUB, MOBI, ZIP, audio, or video formats for broader device support.', 'ebook-store'); ?></p> |
| 2470 |
</div> |
| 2471 |
<div class="ebook-card__body ebook-card__body--flush"> |
| 2472 |
<?php do_action('ebook_store_file_formats_form', $ebook_id); ?> |
| 2473 |
</div> |
| 2474 |
</section> |
| 2475 |
</div> |
| 2476 |
|
| 2477 |
<aside class="ebook-editor-layout__sidebar"> |
| 2478 |
<section class="ebook-card ebook-card--media"> |
| 2479 |
<div class="ebook-card__header"> |
| 2480 |
<h3><?php esc_html_e('Media & preview', 'ebook-store'); ?></h3> |
| 2481 |
<p><?php esc_html_e('Manage the storefront visuals buyers see first: cover, spine image, and sample preview file.', 'ebook-store'); ?></p> |
| 2482 |
</div> |
| 2483 |
<div class="ebook-card__body"> |
| 2484 |
<div class="ebook-media-grid"> |
| 2485 |
<div class="ebook-media-field"> |
| 2486 |
<span class="ebook-media-label"><?php esc_html_e('Cover image', 'ebook-store'); ?></span> |
| 2487 |
<div class="ebook_store_image_wrapper"> |
| 2488 |
<div class="ebook_store_image_preview cover-image <?php echo !empty($img_cover['url']) ? 'has-image' : 'empty'; ?>"> |
| 2489 |
<?php if (!empty($img_cover['url'])) : ?> |
| 2490 |
<img src="<?php echo esc_url($img_cover['url']); ?>" alt="<?php echo esc_attr(basename($img_cover['url'])); ?>"> |
| 2491 |
<div class="remove-image"><span class="dashicons dashicons-no-alt"></span></div> |
| 2492 |
<?php if ($cover_size) : ?> |
| 2493 |
<div class="image-size"><?php echo esc_html($cover_size); ?></div> |
| 2494 |
<?php endif; ?> |
| 2495 |
<?php endif; ?> |
| 2496 |
</div> |
| 2497 |
<div class="ebook-media-controls"> |
| 2498 |
<input name="ebook_wp_custom_attachment_cover" type="file" accept="image/*"> |
| 2499 |
<span class="image-upload-hint"><?php esc_html_e('Recommended size: 180 x 260px', 'ebook-store'); ?></span> |
| 2500 |
<?php if (!empty($img_cover['url'])) : ?> |
| 2501 |
<p class="ebook-media-current"><?php printf(esc_html__('Current file: %s', 'ebook-store'), '<a href="' . esc_url($img_cover['url']) . '" target="_blank">' . esc_html(basename($img_cover['url'])) . '</a>'); ?></p> |
| 2502 |
<?php endif; ?> |
| 2503 |
</div> |
| 2504 |
</div> |
| 2505 |
</div> |
| 2506 |
<div class="ebook-media-field"> |
| 2507 |
<span class="ebook-media-label"><?php esc_html_e('Side image', 'ebook-store'); ?></span> |
| 2508 |
<div class="ebook_store_image_wrapper"> |
| 2509 |
<div class="ebook_store_image_preview side-image <?php echo !empty($img_side_photo['url']) ? 'has-image' : 'empty'; ?>"> |
| 2510 |
<?php if (!empty($img_side_photo['url'])) : ?> |
| 2511 |
<img src="<?php echo esc_url($img_side_photo['url']); ?>" alt="<?php echo esc_attr(basename($img_side_photo['url'])); ?>"> |
| 2512 |
<div class="remove-image"><span class="dashicons dashicons-no-alt"></span></div> |
| 2513 |
<?php if ($side_size) : ?> |
| 2514 |
<div class="image-size"><?php echo esc_html($side_size); ?></div> |
| 2515 |
<?php endif; ?> |
| 2516 |
<?php endif; ?> |
| 2517 |
</div> |
| 2518 |
<div class="ebook-media-controls"> |
| 2519 |
<input name="ebook_wp_custom_attachment_side_photo" type="file" accept="image/*"> |
| 2520 |
<span class="image-upload-hint"><?php esc_html_e('Recommended size: 20 x 260px', 'ebook-store'); ?></span> |
| 2521 |
<?php if (!empty($img_side_photo['url'])) : ?> |
| 2522 |
<p class="ebook-media-current"><?php printf(esc_html__('Current file: %s', 'ebook-store'), '<a href="' . esc_url($img_side_photo['url']) . '" target="_blank">' . esc_html(basename($img_side_photo['url'])) . '</a>'); ?></p> |
| 2523 |
<?php endif; ?> |
| 2524 |
</div> |
| 2525 |
</div> |
| 2526 |
</div> |
| 2527 |
<div class="ebook-media-field"> |
| 2528 |
<label for="ebook_wp_custom_attachment_preview"><?php esc_html_e('Preview file', 'ebook-store'); ?></label> |
| 2529 |
<?php if (!empty($preview['url'])) : ?> |
| 2530 |
<p class="ebook-media-current"> |
| 2531 |
<?php |
| 2532 |
printf( |
| 2533 |
esc_html__('Uploaded: %s', 'ebook-store'), |
| 2534 |
'<a href="' . esc_url($preview['url']) . '" target="_blank">' . esc_html(basename($preview['url'])) . '</a>' |
| 2535 |
); |
| 2536 |
?> |
| 2537 |
<label class="ebook-inline-delete" title="<?php esc_attr_e('Delete', 'ebook-store'); ?>"> |
| 2538 |
<input type="checkbox" name="ebook_store_delete_preview"> |
| 2539 |
<span class="dashicons dashicons-trash"></span> |
| 2540 |
<span class="screen-reader-text"><?php esc_html_e('Delete preview file', 'ebook-store'); ?></span> |
| 2541 |
</label> |
| 2542 |
</p> |
| 2543 |
<?php else : ?> |
| 2544 |
<p class="ebook-media-current muted"><?php esc_html_e('No preview file uploaded yet.', 'ebook-store'); ?></p> |
| 2545 |
<?php endif; ?> |
| 2546 |
<input type="file" id="ebook_wp_custom_attachment_preview" name="ebook_wp_custom_attachment_preview" value="" size="25"> |
| 2547 |
</div> |
| 2548 |
</div> |
| 2549 |
</div> |
| 2550 |
</section> |
| 2551 |
|
| 2552 |
<section class="ebook-card ebook-card--additional"> |
| 2553 |
<div class="ebook-card__header"> |
| 2554 |
<h3><?php esc_html_e('Catalog metadata', 'ebook-store'); ?></h3> |
| 2555 |
<p><?php esc_html_e('Add publication details and relationships so the catalog stays complete and easier to browse.', 'ebook-store'); ?></p> |
| 2556 |
</div> |
| 2557 |
<div class="ebook-card__body"> |
| 2558 |
<div class="ebook-field-grid"> |
| 2559 |
<div class="ebook-field"> |
| 2560 |
<label for="ebook_date"><?php esc_html_e('Publication date', 'ebook-store'); ?></label> |
| 2561 |
<input id="ebook_date" name="ebook[ebook_date]" type="text" value="<?php echo isset($ebook_meta['ebook_date']) ? esc_attr($ebook_meta['ebook_date']) : ''; ?>"> |
| 2562 |
</div> |
| 2563 |
<div class="ebook-field"> |
| 2564 |
<label for="ebook_pages"><?php esc_html_e('Pages', 'ebook-store'); ?></label> |
| 2565 |
<input id="ebook_pages" name="ebook[ebook_pages]" type="number" value="<?php echo isset($ebook_meta['ebook_pages']) ? esc_attr($ebook_meta['ebook_pages']) : ''; ?>"> |
| 2566 |
</div> |
| 2567 |
</div> |
| 2568 |
<div class="ebook-field"> |
| 2569 |
<label for="ebook_author_select"><?php esc_html_e('Author', 'ebook-store'); ?> (<?php esc_html_e('optional', 'ebook-store'); ?>)</label> |
| 2570 |
<select id="ebook_author_select" name="ebook[ebook_author][]" multiple class="ebook-multiselect" size="5"> |
| 2571 |
<option value="0"><?php esc_html_e('None', 'ebook-store'); ?></option> |
| 2572 |
<?php echo $ebook_authors; ?> |
| 2573 |
</select> |
| 2574 |
</div> |
| 2575 |
<div class="ebook-field"> |
| 2576 |
<label for="ebook_publisher_select"><?php esc_html_e('Publisher', 'ebook-store'); ?> (<?php esc_html_e('optional', 'ebook-store'); ?>)</label> |
| 2577 |
<select id="ebook_publisher_select" name="ebook[ebook_publisher][]" multiple class="ebook-multiselect" size="5"> |
| 2578 |
<option value="0"><?php esc_html_e('None', 'ebook-store'); ?></option> |
| 2579 |
<?php echo $ebook_publishers; ?> |
| 2580 |
</select> |
| 2581 |
</div> |
| 2582 |
<div class="ebook-field goPro2"> |
| 2583 |
<label for="ebook_bonus_select"><?php esc_html_e('Bonus ebook', 'ebook-store'); ?></label> |
| 2584 |
<select id="ebook_bonus_select" class="goPro2 ebook-multiselect" multiple size="6" name="ebook[ebook_bonus][]"> |
| 2585 |
<?php echo $ebook_bonus_options; ?> |
| 2586 |
</select> |
| 2587 |
<p class="description"><?php esc_html_e('Optional related downloads bundled with the main title.', 'ebook-store'); ?></p> |
| 2588 |
</div> |
| 2589 |
<?php if ($extendhtml != get_the_ID()) : ?> |
| 2590 |
<div class="ebook-card__extension"> |
| 2591 |
<?php echo $extendhtml; ?> |
| 2592 |
</div> |
| 2593 |
<?php endif; ?> |
| 2594 |
</div> |
| 2595 |
</section> |
| 2596 |
</aside> |
| 2597 |
</div> |
| 2598 |
<section class="ebook-card ebook-card--template-overrides"> |
| 2599 |
<div class="ebook-card__header"> |
| 2600 |
<h3><?php esc_html_e('Confirmation overrides', 'ebook-store'); ?></h3> |
| 2601 |
<p><?php esc_html_e('By default this ebook uses the confirmation page and delivery email from Settings > Templates. Enable an override below only when this specific ebook needs different post-purchase content.', 'ebook-store'); ?></p> |
| 2602 |
</div> |
| 2603 |
<div class="ebook-card__body"> |
| 2604 |
<div class="ebook-template-override-grid"> |
| 2605 |
<div class="ebook-template-override-card"> |
| 2606 |
<div class="ebook-field"> |
| 2607 |
<span class="ebook-template-override-card__label"><?php esc_html_e('Enable custom confirmation page', 'ebook-store'); ?></span> |
| 2608 |
<div class="ebook-template-override-radios" role="radiogroup" aria-label="<?php esc_attr_e('Enable custom confirmation page', 'ebook-store'); ?>"> |
| 2609 |
<label class="ebook-template-override-radio"> |
| 2610 |
<input type="radio" name="ebook_store_enable_custom_confirmation_page" value="0" <?php checked(!$enable_custom_confirmation_page); ?>> |
| 2611 |
<span><?php esc_html_e('Use Settings > Templates default', 'ebook-store'); ?></span> |
| 2612 |
</label> |
| 2613 |
<label class="ebook-template-override-radio"> |
| 2614 |
<input type="radio" name="ebook_store_enable_custom_confirmation_page" value="1" <?php checked($enable_custom_confirmation_page); ?>> |
| 2615 |
<span><?php esc_html_e('Enable for this ebook', 'ebook-store'); ?></span> |
| 2616 |
</label> |
| 2617 |
</div> |
| 2618 |
<p class="description"><?php esc_html_e('If enabled, this content replaces the global Thank You page content for this ebook only.', 'ebook-store'); ?></p> |
| 2619 |
</div> |
| 2620 |
<div class="ebook-template-override-fields" data-override-fields="confirmation" <?php echo !$enable_custom_confirmation_page ? 'hidden' : ''; ?>> |
| 2621 |
<div class="ebook-field"> |
| 2622 |
<label for="ebook_store_custom_confirmation_page_content_editor"><?php esc_html_e('Custom confirmation page content', 'ebook-store'); ?></label> |
| 2623 |
<?php |
| 2624 |
wp_editor( |
| 2625 |
$custom_confirmation_page_content, |
| 2626 |
'ebook_store_custom_confirmation_page_content_editor', |
| 2627 |
array( |
| 2628 |
'textarea_name' => 'ebook_store_custom_confirmation_page_content', |
| 2629 |
'textarea_rows' => 10, |
| 2630 |
) |
| 2631 |
); |
| 2632 |
?> |
| 2633 |
</div> |
| 2634 |
</div> |
| 2635 |
</div> |
| 2636 |
<div class="ebook-template-override-card"> |
| 2637 |
<div class="ebook-field"> |
| 2638 |
<span class="ebook-template-override-card__label"><?php esc_html_e('Enable custom delivery email', 'ebook-store'); ?></span> |
| 2639 |
<div class="ebook-template-override-radios" role="radiogroup" aria-label="<?php esc_attr_e('Enable custom delivery email', 'ebook-store'); ?>"> |
| 2640 |
<label class="ebook-template-override-radio"> |
| 2641 |
<input type="radio" name="ebook_store_enable_custom_delivery_email" value="0" <?php checked(!$enable_custom_delivery_email); ?>> |
| 2642 |
<span><?php esc_html_e('Use Settings > Templates default', 'ebook-store'); ?></span> |
| 2643 |
</label> |
| 2644 |
<label class="ebook-template-override-radio"> |
| 2645 |
<input type="radio" name="ebook_store_enable_custom_delivery_email" value="1" <?php checked($enable_custom_delivery_email); ?>> |
| 2646 |
<span><?php esc_html_e('Enable for this ebook', 'ebook-store'); ?></span> |
| 2647 |
</label> |
| 2648 |
</div> |
| 2649 |
<p class="description"><?php esc_html_e('If enabled, this subject and message replace the global email delivery template for this ebook only.', 'ebook-store'); ?></p> |
| 2650 |
</div> |
| 2651 |
<div class="ebook-template-override-fields" data-override-fields="email" <?php echo !$enable_custom_delivery_email ? 'hidden' : ''; ?>> |
| 2652 |
<div class="ebook-field"> |
| 2653 |
<label for="ebook_store_custom_delivery_email_subject"><?php esc_html_e('Custom delivery email subject', 'ebook-store'); ?></label> |
| 2654 |
<input type="text" id="ebook_store_custom_delivery_email_subject" name="ebook_store_custom_delivery_email_subject" value="<?php echo esc_attr($custom_delivery_email_subject); ?>"> |
| 2655 |
</div> |
| 2656 |
<div class="ebook-field"> |
| 2657 |
<label for="ebook_store_custom_delivery_email_text_editor"><?php esc_html_e('Custom delivery email content', 'ebook-store'); ?></label> |
| 2658 |
<?php |
| 2659 |
wp_editor( |
| 2660 |
$custom_delivery_email_text, |
| 2661 |
'ebook_store_custom_delivery_email_text_editor', |
| 2662 |
array( |
| 2663 |
'textarea_name' => 'ebook_store_custom_delivery_email_text', |
| 2664 |
'textarea_rows' => 10, |
| 2665 |
) |
| 2666 |
); |
| 2667 |
?> |
| 2668 |
</div> |
| 2669 |
</div> |
| 2670 |
</div> |
| 2671 |
</div> |
| 2672 |
</div> |
| 2673 |
</section> |
| 2674 |
</div> |
| 2675 |
<script> |
| 2676 |
jQuery(document).ready(function() { |
| 2677 |
jQuery('#ebook_date').datepicker({ |
| 2678 |
dateFormat : 'dd-mm-yy' |
| 2679 |
}); |
| 2680 |
jQuery('#wp-admin-bar-view').hide(); |
| 2681 |
jQuery('input[type=file]').show(); |
| 2682 |
|
| 2683 |
function ebookStoreToggleOverrideFields(groupName, targetSelector) { |
| 2684 |
var enabled = jQuery('input[name="' + groupName + '"]:checked').val() === '1'; |
| 2685 |
var $target = jQuery(targetSelector); |
| 2686 |
if (!$target.length) { |
| 2687 |
return; |
| 2688 |
} |
| 2689 |
|
| 2690 |
$target.find('input, textarea, select, button').prop('disabled', !enabled); |
| 2691 |
if (enabled) { |
| 2692 |
$target.prop('hidden', false).stop(true, true).slideDown(160); |
| 2693 |
} else { |
| 2694 |
$target.stop(true, true).slideUp(160, function() { |
| 2695 |
$target.prop('hidden', true); |
| 2696 |
}); |
| 2697 |
} |
| 2698 |
} |
| 2699 |
|
| 2700 |
function ebookStoreRefreshOverrideRadioState(groupName) { |
| 2701 |
var $inputs = jQuery('input[name="' + groupName + '"]'); |
| 2702 |
$inputs.each(function() { |
| 2703 |
var $input = jQuery(this); |
| 2704 |
$input.closest('.ebook-template-override-radio').toggleClass('is-selected', $input.is(':checked')); |
| 2705 |
}); |
| 2706 |
} |
| 2707 |
|
| 2708 |
ebookStoreToggleOverrideFields('ebook_store_enable_custom_confirmation_page', '[data-override-fields="confirmation"]'); |
| 2709 |
ebookStoreToggleOverrideFields('ebook_store_enable_custom_delivery_email', '[data-override-fields="email"]'); |
| 2710 |
ebookStoreRefreshOverrideRadioState('ebook_store_enable_custom_confirmation_page'); |
| 2711 |
ebookStoreRefreshOverrideRadioState('ebook_store_enable_custom_delivery_email'); |
| 2712 |
|
| 2713 |
jQuery('input[name="ebook_store_enable_custom_confirmation_page"]').on('change', function() { |
| 2714 |
ebookStoreRefreshOverrideRadioState('ebook_store_enable_custom_confirmation_page'); |
| 2715 |
ebookStoreToggleOverrideFields('ebook_store_enable_custom_confirmation_page', '[data-override-fields="confirmation"]'); |
| 2716 |
}); |
| 2717 |
|
| 2718 |
jQuery('input[name="ebook_store_enable_custom_delivery_email"]').on('change', function() { |
| 2719 |
ebookStoreRefreshOverrideRadioState('ebook_store_enable_custom_delivery_email'); |
| 2720 |
ebookStoreToggleOverrideFields('ebook_store_enable_custom_delivery_email', '[data-override-fields="email"]'); |
| 2721 |
}); |
| 2722 |
}); |
| 2723 |
</script> |
| 2724 |
<?php |
| 2725 |
$editor_output = ob_get_clean(); |
| 2726 |
echo function_exists( 'ebook_store_translate_markup' ) ? ebook_store_translate_markup( $editor_output ) : $editor_output; |
| 2727 |
} // end ebook_wp_custom_attachment |
| 2728 |
|
| 2729 |
function ebook_store_get_default_template_content_settings() { |
| 2730 |
$QSWPOptions = new QSWPOptions(); |
| 2731 |
|
| 2732 |
return array( |
| 2733 |
'confirmation_page_content' => get_option('thankyou_page', $QSWPOptions->thankyou_page), |
| 2734 |
'delivery_email_subject' => get_option('email_delivery_subject', $QSWPOptions->email_delivery_subject), |
| 2735 |
'delivery_email_text' => get_option('email_delivery_text', $QSWPOptions->email_delivery_text), |
| 2736 |
); |
| 2737 |
} |
| 2738 |
|
| 2739 |
function ebook_store_get_ebook_template_override_settings($ebook_id) { |
| 2740 |
$ebook_id = absint($ebook_id); |
| 2741 |
$defaults = ebook_store_get_default_template_content_settings(); |
| 2742 |
$custom_confirmation_page_content = $ebook_id > 0 ? (string) get_post_meta($ebook_id, 'ebook_store_custom_confirmation_page_content', true) : ''; |
| 2743 |
$custom_delivery_email_subject = $ebook_id > 0 ? (string) get_post_meta($ebook_id, 'ebook_store_custom_delivery_email_subject', true) : ''; |
| 2744 |
$custom_delivery_email_text = $ebook_id > 0 ? (string) get_post_meta($ebook_id, 'ebook_store_custom_delivery_email_text', true) : ''; |
| 2745 |
|
| 2746 |
return array( |
| 2747 |
'enable_custom_confirmation_page' => $ebook_id > 0 && (string) get_post_meta($ebook_id, 'ebook_store_enable_custom_confirmation_page', true) === '1', |
| 2748 |
'enable_custom_delivery_email' => $ebook_id > 0 && (string) get_post_meta($ebook_id, 'ebook_store_enable_custom_delivery_email', true) === '1', |
| 2749 |
'confirmation_page_content' => $custom_confirmation_page_content !== '' ? $custom_confirmation_page_content : $defaults['confirmation_page_content'], |
| 2750 |
'delivery_email_subject' => $custom_delivery_email_subject !== '' ? $custom_delivery_email_subject : $defaults['delivery_email_subject'], |
| 2751 |
'delivery_email_text' => $custom_delivery_email_text !== '' ? $custom_delivery_email_text : $defaults['delivery_email_text'], |
| 2752 |
'default_confirmation_page_content' => $defaults['confirmation_page_content'], |
| 2753 |
'default_delivery_email_subject' => $defaults['delivery_email_subject'], |
| 2754 |
'default_delivery_email_text' => $defaults['delivery_email_text'], |
| 2755 |
); |
| 2756 |
} |
| 2757 |
|
| 2758 |
function ebook_store_get_effective_confirmation_page_content($ebook_id) { |
| 2759 |
$settings = ebook_store_get_ebook_template_override_settings($ebook_id); |
| 2760 |
|
| 2761 |
if (!empty($settings['enable_custom_confirmation_page']) && trim((string) $settings['confirmation_page_content']) !== '') { |
| 2762 |
return (string) $settings['confirmation_page_content']; |
| 2763 |
} |
| 2764 |
|
| 2765 |
return (string) $settings['default_confirmation_page_content']; |
| 2766 |
} |
| 2767 |
|
| 2768 |
function ebook_store_get_effective_delivery_email_template($ebook_id) { |
| 2769 |
$settings = ebook_store_get_ebook_template_override_settings($ebook_id); |
| 2770 |
|
| 2771 |
if (!empty($settings['enable_custom_delivery_email'])) { |
| 2772 |
return array( |
| 2773 |
'subject' => trim((string) $settings['delivery_email_subject']) !== '' ? (string) $settings['delivery_email_subject'] : (string) $settings['default_delivery_email_subject'], |
| 2774 |
'text' => trim((string) $settings['delivery_email_text']) !== '' ? (string) $settings['delivery_email_text'] : (string) $settings['default_delivery_email_text'], |
| 2775 |
); |
| 2776 |
} |
| 2777 |
|
| 2778 |
return array( |
| 2779 |
'subject' => (string) $settings['default_delivery_email_subject'], |
| 2780 |
'text' => (string) $settings['default_delivery_email_text'], |
| 2781 |
); |
| 2782 |
} |
| 2783 |
|
| 2784 |
function ebook_set_upload_dir( $upload ) { |
| 2785 |
//wp_error(print_r($upload,true)); |
| 2786 |
// Override the year / month being based on the post publication date, if year/month organization is enabled |
| 2787 |
if ( get_option( 'uploads_use_yearmonth_folders' ) ) { |
| 2788 |
// Generate the yearly and monthly dirs |
| 2789 |
$time = current_time( 'mysql' ); |
| 2790 |
$y = substr( $time, 0, 4 ); |
| 2791 |
$m = substr( $time, 5, 2 ); |
| 2792 |
$upload['subdir'] = "/$y/$m"; |
| 2793 |
} |
| 2794 |
|
| 2795 |
$upload['subdir'] = '/ebooks' . $upload['subdir']; |
| 2796 |
$upload['path'] = $upload['basedir'] . $upload['subdir']; |
| 2797 |
$upload['url'] = $upload['baseurl'] . $upload['subdir']; |
| 2798 |
// The master files a customer paid for live here. Protection is written |
| 2799 |
// unconditionally: it used to be REMOVED whenever the WooCommerce PDF reader |
| 2800 |
// option was on, which published every paid ebook at a guessable URL. |
| 2801 |
ebook_store_protect_uploads_dir( $upload['basedir'] . '/ebooks' ); |
| 2802 |
|
| 2803 |
return $upload; |
| 2804 |
} |
| 2805 |
|
| 2806 |
/** |
| 2807 |
* Make an upload directory unreadable over HTTP. |
| 2808 |
* |
| 2809 |
* Writes both the modern mod_authz_core directive and the 2.2-era fallback, plus |
| 2810 |
* an index.php so a server with directory indexes turned on still shows nothing. |
| 2811 |
* |
| 2812 |
* This is Apache/LiteSpeed only. On nginx the equivalent location block has to |
| 2813 |
* be added to the server config, which is why the download handler streams files |
| 2814 |
* through PHP rather than relying on the directory rules alone. |
| 2815 |
* |
| 2816 |
* @param string $dir Absolute path. |
| 2817 |
*/ |
| 2818 |
function ebook_store_protect_uploads_dir( $dir ) { |
| 2819 |
if ( $dir === '' ) { |
| 2820 |
return; |
| 2821 |
} |
| 2822 |
|
| 2823 |
if ( ! is_dir( $dir ) && ! wp_mkdir_p( $dir ) ) { |
| 2824 |
return; |
| 2825 |
} |
| 2826 |
|
| 2827 |
$htaccess = $dir . '/.htaccess'; |
| 2828 |
|
| 2829 |
if ( ! file_exists( $htaccess ) ) { |
| 2830 |
@file_put_contents( |
| 2831 |
$htaccess, |
| 2832 |
"Options -Indexes\n" |
| 2833 |
. "<IfModule mod_authz_core.c>\n\tRequire all denied\n</IfModule>\n" |
| 2834 |
. "<IfModule !mod_authz_core.c>\n\tOrder allow,deny\n\tDeny from all\n</IfModule>\n" |
| 2835 |
); |
| 2836 |
} |
| 2837 |
|
| 2838 |
if ( ! file_exists( $dir . '/index.php' ) ) { |
| 2839 |
@file_put_contents( $dir . '/index.php', "<?php\n// Silence is golden.\n" ); |
| 2840 |
} |
| 2841 |
|
| 2842 |
// A previous version renamed the rules out of the way instead of deleting |
| 2843 |
// them; clean that up so the directory is not left half protected. |
| 2844 |
$backup = $dir . '/.htaccess-backup'; |
| 2845 |
if ( file_exists( $backup ) ) { |
| 2846 |
@unlink( $backup ); |
| 2847 |
} |
| 2848 |
} |
| 2849 |
|
| 2850 |
/** |
| 2851 |
* Ensure the protected upload directories exist and are locked down. |
| 2852 |
* |
| 2853 |
* Runs on activation and on upgrade, so protection no longer depends on somebody |
| 2854 |
* happening to save an ebook. |
| 2855 |
*/ |
| 2856 |
function ebook_store_ensure_protected_dirs() { |
| 2857 |
$uploads = wp_upload_dir(); |
| 2858 |
|
| 2859 |
if ( ! empty( $uploads['error'] ) ) { |
| 2860 |
return; |
| 2861 |
} |
| 2862 |
|
| 2863 |
ebook_store_protect_uploads_dir( trailingslashit( $uploads['basedir'] ) . 'ebooks' ); |
| 2864 |
ebook_store_protect_uploads_dir( trailingslashit( $uploads['basedir'] ) . 'ebook_store_data' ); |
| 2865 |
ebook_store_protect_uploads_dir( trailingslashit( $uploads['basedir'] ) . 'ebook-store-cache' ); |
| 2866 |
ebook_store_protect_uploads_dir( trailingslashit( $uploads['basedir'] ) . 'ebook-store-logs' ); |
| 2867 |
} |
| 2868 |
function ebook_set_upload_dir_preview( $upload ) { |
| 2869 |
|
| 2870 |
// Override the year / month being based on the post publication date, if year/month organization is enabled |
| 2871 |
if ( get_option( 'uploads_use_yearmonth_folders' ) ) { |
| 2872 |
// Generate the yearly and monthly dirs |
| 2873 |
$time = current_time( 'mysql' ); |
| 2874 |
$y = substr( $time, 0, 4 ); |
| 2875 |
$m = substr( $time, 5, 2 ); |
| 2876 |
$upload['subdir'] = "/$y/$m"; |
| 2877 |
} |
| 2878 |
|
| 2879 |
$upload['subdir'] = '/ebooks_misc' . $upload['subdir']; |
| 2880 |
$upload['path'] = $upload['basedir'] . $upload['subdir']; |
| 2881 |
$upload['url'] = $upload['baseurl'] . $upload['subdir']; |
| 2882 |
return $upload; |
| 2883 |
} |
| 2884 |
function ebook_store_destroy_image_resource($image) { |
| 2885 |
if (is_resource($image)) { |
| 2886 |
imagedestroy($image); |
| 2887 |
return; |
| 2888 |
} |
| 2889 |
|
| 2890 |
if (class_exists('GdImage') && $image instanceof GdImage) { |
| 2891 |
imagedestroy($image); |
| 2892 |
} |
| 2893 |
} |
| 2894 |
function ebook_store_prepare_cover_for_upload($tmp_name) { |
| 2895 |
if (!file_exists($tmp_name)) { |
| 2896 |
return false; |
| 2897 |
} |
| 2898 |
|
| 2899 |
$image = @imagecreatefromjpeg($tmp_name); |
| 2900 |
if (!$image) { |
| 2901 |
$image = @imagecreatefrompng($tmp_name); |
| 2902 |
} |
| 2903 |
if (!$image) { |
| 2904 |
$image = @imagecreatefromgif($tmp_name); |
| 2905 |
} |
| 2906 |
|
| 2907 |
$size = @getimagesize($tmp_name); |
| 2908 |
if (!$image || !$size) { |
| 2909 |
ebook_store_destroy_image_resource($image); |
| 2910 |
return false; |
| 2911 |
} |
| 2912 |
|
| 2913 |
list($width, $height) = $size; |
| 2914 |
|
| 2915 |
if ($width != 180 || $height != 260) { |
| 2916 |
$image_p = imagecreatetruecolor(180, 260); |
| 2917 |
imagecopyresampled($image_p, $image, 0, 0, 0, 0, 180, 260, $width, $height); |
| 2918 |
imagejpeg($image_p, $tmp_name, 100); |
| 2919 |
imagedestroy($image_p); |
| 2920 |
} |
| 2921 |
|
| 2922 |
ebook_store_destroy_image_resource($image); |
| 2923 |
return true; |
| 2924 |
} |
| 2925 |
function ebook_store_generate_cover_from_pdf($post_id) { |
| 2926 |
$existing_cover = get_post_meta($post_id, 'ebook_wp_custom_attachment_cover', true); |
| 2927 |
if (is_array($existing_cover) && !empty($existing_cover['url'])) { |
| 2928 |
return; |
| 2929 |
} |
| 2930 |
if (!class_exists('Imagick')) { |
| 2931 |
return; |
| 2932 |
} |
| 2933 |
|
| 2934 |
$pdf_meta = get_post_meta($post_id, 'ebook_wp_custom_attachment_pdf', true); |
| 2935 |
if (!is_array($pdf_meta) || empty($pdf_meta['file'])) { |
| 2936 |
$pdf_meta = get_post_meta($post_id, 'ebook_wp_custom_attachment', true); |
| 2937 |
} |
| 2938 |
if (!is_array($pdf_meta) || empty($pdf_meta['file'])) { |
| 2939 |
return; |
| 2940 |
} |
| 2941 |
|
| 2942 |
$pdf_path = $pdf_meta['file']; |
| 2943 |
if (!file_exists($pdf_path)) { |
| 2944 |
return; |
| 2945 |
} |
| 2946 |
|
| 2947 |
try { |
| 2948 |
$imagick = new Imagick(); |
| 2949 |
$imagick->setResolution(150, 150); |
| 2950 |
$imagick->readImage($pdf_path . '[0]'); |
| 2951 |
$imagick->setImageFormat('jpg'); |
| 2952 |
$imagick->setImageCompression(Imagick::COMPRESSION_JPEG); |
| 2953 |
$imagick->setImageCompressionQuality(90); |
| 2954 |
$image_blob = $imagick->getImageBlob(); |
| 2955 |
$imagick->clear(); |
| 2956 |
$imagick->destroy(); |
| 2957 |
} catch (Exception $e) { |
| 2958 |
error_log('Ebook Store: Unable to auto-create cover image. ' . $e->getMessage()); |
| 2959 |
return; |
| 2960 |
} |
| 2961 |
|
| 2962 |
$temp_file = wp_tempnam('ebook-store-cover'); |
| 2963 |
if (!$temp_file) { |
| 2964 |
return; |
| 2965 |
} |
| 2966 |
file_put_contents($temp_file, $image_blob); |
| 2967 |
|
| 2968 |
ebook_store_prepare_cover_for_upload($temp_file); |
| 2969 |
|
| 2970 |
$base_name = basename($pdf_path); |
| 2971 |
$base_name = preg_replace('/\.[^.]+$/', '', $base_name); |
| 2972 |
if ($base_name === '' || $base_name === null) { |
| 2973 |
$base_name = 'ebook-cover'; |
| 2974 |
} |
| 2975 |
$cover_filename = $base_name . '-autocover.jpg'; |
| 2976 |
|
| 2977 |
$upload = wp_upload_bits($cover_filename, null, file_get_contents($temp_file)); |
| 2978 |
@unlink($temp_file); |
| 2979 |
|
| 2980 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 2981 |
error_log('Ebook Store: Auto cover upload error. ' . $upload['error']); |
| 2982 |
return; |
| 2983 |
} |
| 2984 |
|
| 2985 |
add_post_meta($post_id, 'ebook_wp_custom_attachment_cover', $upload, true); |
| 2986 |
update_post_meta($post_id, 'ebook_wp_custom_attachment_cover', $upload); |
| 2987 |
} |
| 2988 |
/** |
| 2989 |
* Is this upload one of the types the field accepts? |
| 2990 |
* |
| 2991 |
* Every upload branch in save_custom_meta_data() used to compute the allowed |
| 2992 |
* types and then test `if(1)`, so the check never ran and the else-branch that |
| 2993 |
* rejects the file was dead code. |
| 2994 |
* |
| 2995 |
* @param array $file One entry from $_FILES. |
| 2996 |
* @param array $supported_types Allowed MIME types. |
| 2997 |
* @return bool |
| 2998 |
*/ |
| 2999 |
function ebook_store_upload_type_is_allowed( $file, $supported_types ) { |
| 3000 |
if ( empty( $file['name'] ) ) { |
| 3001 |
return false; |
| 3002 |
} |
| 3003 |
|
| 3004 |
$supported_types = (array) $supported_types; |
| 3005 |
|
| 3006 |
$ext = strtolower( (string) pathinfo( $file['name'], PATHINFO_EXTENSION ) ); |
| 3007 |
|
| 3008 |
if ( $ext === '' ) { |
| 3009 |
return false; |
| 3010 |
} |
| 3011 |
|
| 3012 |
// Cover images and previews come through this same function, so the |
| 3013 |
// content-aware branch below is scoped to the ebook formats it understands. |
| 3014 |
$ebook_formats = array( 'pdf', 'epub', 'mobi', 'txt', 'zip', 'mp3', 'mp4' ); |
| 3015 |
|
| 3016 |
if ( in_array( $ext, $ebook_formats, true ) ) { |
| 3017 |
// The extension must be one this site is allowed to upload — the same |
| 3018 |
// gate the chunked uploader applies, so Pro formats stay gated on both |
| 3019 |
// paths. |
| 3020 |
if ( function_exists( 'ebook_store_get_allowed_upload_formats' ) |
| 3021 |
&& ! in_array( $ext, (array) ebook_store_get_allowed_upload_formats(), true ) ) { |
| 3022 |
return false; |
| 3023 |
} |
| 3024 |
|
| 3025 |
// Judge the contents rather than the declared MIME type. |
| 3026 |
// |
| 3027 |
// This used to lean on wp_check_filetype_and_ext(), which returns |
| 3028 |
// ext => false, type => false for a genuine .epub: fileinfo reports |
| 3029 |
// application/epub+zip while WordPress's map said otherwise, and the |
| 3030 |
// mismatch made it refuse the file. Every valid ePub was rejected with |
| 3031 |
// "that file type is not allowed here". The content check is both |
| 3032 |
// friendlier to real books and stricter about disguised ones. |
| 3033 |
if ( ! empty( $file['tmp_name'] ) && is_readable( $file['tmp_name'] ) && function_exists( 'ebook_store_validate_uploaded_ebook' ) ) { |
| 3034 |
return ! is_wp_error( ebook_store_validate_uploaded_ebook( $file['tmp_name'], $ext ) ); |
| 3035 |
} |
| 3036 |
} |
| 3037 |
|
| 3038 |
// Images, previews, or no readable temp file to inspect: match the declared |
| 3039 |
// type against the field's list, and still reject a claimed extension that |
| 3040 |
// disagrees with the real contents. |
| 3041 |
$checked = function_exists( 'wp_check_filetype_and_ext' ) && ! empty( $file['tmp_name'] ) && is_uploaded_file( $file['tmp_name'] ) |
| 3042 |
? wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] ) |
| 3043 |
: wp_check_filetype( basename( $file['name'] ) ); |
| 3044 |
|
| 3045 |
$type = ! empty( $checked['type'] ) ? $checked['type'] : ''; |
| 3046 |
|
| 3047 |
if ( $type === '' || ! empty( $checked['proper_filename'] ) ) { |
| 3048 |
return false; |
| 3049 |
} |
| 3050 |
|
| 3051 |
return in_array( $type, $supported_types, true ); |
| 3052 |
} |
| 3053 |
|
| 3054 |
/** |
| 3055 |
* Which ebook file formats may this site upload? |
| 3056 |
* |
| 3057 |
* PDF is the free format. The other formats are the flagship Pro feature, and |
| 3058 |
* until now the only thing stopping a free install from using them was a CSS |
| 3059 |
* class and a disabled attribute — the save handler accepted them regardless. |
| 3060 |
* |
| 3061 |
* @return array |
| 3062 |
*/ |
| 3063 |
function ebook_store_get_allowed_upload_formats() { |
| 3064 |
$formats = array( 'pdf' ); |
| 3065 |
|
| 3066 |
if ( ebook_store_has_pro_license() ) { |
| 3067 |
$formats = array_merge( $formats, array( 'mobi', 'txt', 'epub', 'zip', 'mp3', 'mp4' ) ); |
| 3068 |
} |
| 3069 |
|
| 3070 |
return apply_filters( 'ebook_store_allowed_upload_formats', $formats ); |
| 3071 |
} |
| 3072 |
|
| 3073 |
function save_custom_meta_data($id) { |
| 3074 |
add_filter( 'upload_dir', 'ebook_set_upload_dir' ); |
| 3075 |
/* --- security verification --- */ |
| 3076 |
if(!wp_verify_nonce(@$_POST['ebook_wp_custom_attachment_nonce'], plugin_basename(__FILE__))) { |
| 3077 |
return $id; |
| 3078 |
} // end if |
| 3079 |
|
| 3080 |
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { |
| 3081 |
return $id; |
| 3082 |
} // end if |
| 3083 |
|
| 3084 |
// 'edit_post' is the correct meta capability for these custom post types. |
| 3085 |
// The old code checked 'edit_page' in BOTH branches, so authorisation for |
| 3086 |
// ebooks and orders was decided by page permissions. |
| 3087 |
if ( ! current_user_can( 'edit_post', $id ) ) { |
| 3088 |
return $id; |
| 3089 |
} |
| 3090 |
/* - end security verification - */ |
| 3091 |
if (strpos($_POST['content'], '[ebook_store') !== false) { |
| 3092 |
wp_die("The embed code [ebook_store id=XX] is not meant to be in the ebook description, it must be in the post or page where you want the ebook order form to appear. Click Back, correct that and try again."); |
| 3093 |
} |
| 3094 |
if ($_POST['ebook']['donate_or_download'] != 'free' && $_POST['ebook']['ebook_price'] == 0) { |
| 3095 |
$_POST['ebook']['ebook_price'] = 0.01; |
| 3096 |
} |
| 3097 |
if ( isset( $_POST['ebook']['form_template'] ) ) { |
| 3098 |
$_POST['ebook']['form_template'] = ebook_store_resolve_form_template( $_POST['ebook']['form_template'] ); |
| 3099 |
} else { |
| 3100 |
$_POST['ebook']['form_template'] = ebook_store_resolve_form_template( '' ); |
| 3101 |
} |
| 3102 |
// "Free" and "Donate" delivery, and bonus books, are Pro features. They |
| 3103 |
// were previously gated only by a CSS class, so a free install could set |
| 3104 |
// them by posting the form directly. |
| 3105 |
if ( ! ebook_store_has_pro_license() ) { |
| 3106 |
if ( isset( $_POST['ebook']['donate_or_download'] ) && $_POST['ebook']['donate_or_download'] !== 'paid' ) { |
| 3107 |
$_POST['ebook']['donate_or_download'] = 'paid'; |
| 3108 |
} |
| 3109 |
$_POST['ebook_bonus'] = array(); |
| 3110 |
} |
| 3111 |
|
| 3112 |
update_post_meta($id, 'ebook', $_POST['ebook']); |
| 3113 |
update_post_meta($id, 'ebook_bonus', isset( $_POST['ebook_bonus'] ) ? (array) $_POST['ebook_bonus'] : array()); |
| 3114 |
update_post_meta($id, 'ebook_store_enable_custom_confirmation_page', ( isset( $_POST['ebook_store_enable_custom_confirmation_page'] ) && (string) wp_unslash( $_POST['ebook_store_enable_custom_confirmation_page'] ) === '1' ) ? '1' : '0'); |
| 3115 |
update_post_meta($id, 'ebook_store_enable_custom_delivery_email', ( isset( $_POST['ebook_store_enable_custom_delivery_email'] ) && (string) wp_unslash( $_POST['ebook_store_enable_custom_delivery_email'] ) === '1' ) ? '1' : '0'); |
| 3116 |
update_post_meta($id, 'ebook_store_custom_confirmation_page_content', isset( $_POST['ebook_store_custom_confirmation_page_content'] ) ? wp_kses_post( wp_unslash( $_POST['ebook_store_custom_confirmation_page_content'] ) ) : ''); |
| 3117 |
update_post_meta($id, 'ebook_store_custom_delivery_email_subject', isset( $_POST['ebook_store_custom_delivery_email_subject'] ) ? sanitize_text_field( wp_unslash( $_POST['ebook_store_custom_delivery_email_subject'] ) ) : ''); |
| 3118 |
update_post_meta($id, 'ebook_store_custom_delivery_email_text', isset( $_POST['ebook_store_custom_delivery_email_text'] ) ? wp_kses_post( wp_unslash( $_POST['ebook_store_custom_delivery_email_text'] ) ) : ''); |
| 3119 |
|
| 3120 |
// Make sure the file array isn't empty |
| 3121 |
|
| 3122 |
if(!empty($_FILES['ebook_wp_custom_attachment_pdf']['name'])) { |
| 3123 |
$_FILES['ebook_wp_custom_attachment'] = $_FILES['ebook_wp_custom_attachment_pdf']; |
| 3124 |
// Setup the array of supported file types. In this case, it's just PDF. |
| 3125 |
$supported_types = array('application/pdf','application/x-mobipocket-ebook','application/epub+zip','application/zip','application/octet-stream'); |
| 3126 |
|
| 3127 |
// Get the file type of the upload |
| 3128 |
$arr_file_type = wp_check_filetype(basename($_FILES['ebook_wp_custom_attachment']['name'])); |
| 3129 |
$uploaded_type = $arr_file_type['type']; |
| 3130 |
|
| 3131 |
// Check if the type is supported. If not, throw an error. |
| 3132 |
if ( ebook_store_upload_type_is_allowed( $_FILES['ebook_wp_custom_attachment'], $supported_types ) ) { |
| 3133 |
|
| 3134 |
// Use the WordPress API to upload the file |
| 3135 |
$upload = wp_upload_bits($_FILES['ebook_wp_custom_attachment']['name'], null, file_get_contents($_FILES['ebook_wp_custom_attachment']['tmp_name'])); |
| 3136 |
|
| 3137 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 3138 |
wp_die( esc_html( sprintf( /* translators: %s: error message from WordPress. */ __( 'The file could not be uploaded: %s', 'ebook-store' ), $upload['error'] ) ) ); |
| 3139 |
} else { |
| 3140 |
add_post_meta($id, 'ebook_wp_custom_attachment', $upload,true); |
| 3141 |
update_post_meta($id, 'ebook_wp_custom_attachment', wp_slash($upload)); |
| 3142 |
update_post_meta($id, 'ebook_wp_custom_attachment_pdf', wp_slash($upload)); |
| 3143 |
} // end if/else |
| 3144 |
|
| 3145 |
} else { |
| 3146 |
wp_die( esc_html__( 'That file type is not allowed here. Check the list of accepted formats next to the upload field and try again.', 'ebook-store' ) ); |
| 3147 |
} // end if/else |
| 3148 |
|
| 3149 |
} // end if |
| 3150 |
$loop = array( 'mobi', 'txt', 'epub', 'zip', 'mp3', 'mp4' ); |
| 3151 |
$allowed_formats = ebook_store_get_allowed_upload_formats(); |
| 3152 |
foreach ($loop as $l) { |
| 3153 |
// Enforced here, on the server, rather than only in the browser. |
| 3154 |
if ( ! in_array( $l, $allowed_formats, true ) ) { |
| 3155 |
continue; |
| 3156 |
} |
| 3157 |
if(!empty($_FILES['ebook_wp_custom_attachment_' . $l]['name'])) { |
| 3158 |
|
| 3159 |
//wp_die(print_r($_FILES['ebook_wp_custom_attachment_' . $l],true)); |
| 3160 |
// Setup the array of supported file types. In this case, it's just PDF. |
| 3161 |
$supported_types = array('application/pdf','application/x-mobipocket-ebook','application/epub+zip','application/zip','application/octet-stream'); |
| 3162 |
|
| 3163 |
// Get the file type of the upload |
| 3164 |
$arr_file_type = wp_check_filetype(basename($_FILES['ebook_wp_custom_attachment_' . $l]['name'])); |
| 3165 |
$uploaded_type = $arr_file_type['type']; |
| 3166 |
|
| 3167 |
// Check if the type is supported. If not, throw an error. |
| 3168 |
if ( ebook_store_upload_type_is_allowed( $_FILES['ebook_wp_custom_attachment_' . $l], $supported_types ) ) { |
| 3169 |
|
| 3170 |
// Use the WordPress API to upload the file |
| 3171 |
$upload = wp_upload_bits($_FILES['ebook_wp_custom_attachment_' . $l]['name'], null, file_get_contents($_FILES['ebook_wp_custom_attachment_' . $l]['tmp_name'])); |
| 3172 |
//wp_die(print_r($upload,true)); |
| 3173 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 3174 |
wp_die( esc_html( sprintf( /* translators: %s: error message from WordPress. */ __( 'The file could not be uploaded: %s', 'ebook-store' ), $upload['error'] ) ) ); |
| 3175 |
} else { |
| 3176 |
add_post_meta($id, 'ebook_wp_custom_attachment_' . $l, wp_slash($upload), true); |
| 3177 |
update_post_meta($id, 'ebook_wp_custom_attachment_' . $l, wp_slash($upload)); |
| 3178 |
} // end if/else |
| 3179 |
|
| 3180 |
} else { |
| 3181 |
wp_die( esc_html__( 'That file type is not allowed here. Check the list of accepted formats next to the upload field and try again.', 'ebook-store' ) ); |
| 3182 |
} // end if/else |
| 3183 |
|
| 3184 |
} // end if |
| 3185 |
} |
| 3186 |
//separate loop for deleting ebooks |
| 3187 |
$loop = array('pdf','mobi','txt','epub','zip', 'mp3', 'mp4'); |
| 3188 |
foreach ($loop as $l) { |
| 3189 |
$li = 'ebook_store_delete_' . $l; |
| 3190 |
if (isset($_REQUEST[$li])) { |
| 3191 |
$file_meta = get_post_meta($id, 'ebook_wp_custom_attachment_' . $l, true); |
| 3192 |
unlink($file_meta['file']); |
| 3193 |
delete_post_meta($id, 'ebook_wp_custom_attachment_' . $l); |
| 3194 |
if ($l == 'pdf') { |
| 3195 |
delete_post_meta($id, 'ebook_wp_custom_attachment'); |
| 3196 |
} |
| 3197 |
} |
| 3198 |
} |
| 3199 |
//wp_die(print_r($_REQUEST)); |
| 3200 |
if ($_REQUEST['ebook_store_delete_preview'] == 'on') { |
| 3201 |
//wp_die('delete preview'); |
| 3202 |
delete_post_meta($id, 'ebook_wp_custom_attachment_preview'); |
| 3203 |
} |
| 3204 |
//separate loop for deleting ebooks - end |
| 3205 |
add_filter( 'upload_dir', 'ebook_set_upload_dir_preview' ); |
| 3206 |
|
| 3207 |
if(!empty($_FILES['ebook_wp_custom_attachment_preview']['name'])) { |
| 3208 |
|
| 3209 |
// Setup the array of supported file types. In this case, it's just PDF. |
| 3210 |
$supported_types = array('application/pdf','application/x-mobipocket-ebook','application/epub+zip','application/zip','application/octet-stream'); |
| 3211 |
|
| 3212 |
// Get the file type of the upload |
| 3213 |
$arr_file_type = wp_check_filetype(basename($_FILES['ebook_wp_custom_attachment_preview']['name'])); |
| 3214 |
$uploaded_type = $arr_file_type['type']; |
| 3215 |
|
| 3216 |
// Check if the type is supported. If not, throw an error. |
| 3217 |
if ( ebook_store_upload_type_is_allowed( $_FILES['ebook_wp_custom_attachment_preview'], $supported_types ) ) { |
| 3218 |
|
| 3219 |
// Use the WordPress API to upload the file |
| 3220 |
$upload = wp_upload_bits($_FILES['ebook_wp_custom_attachment_preview']['name'], null, file_get_contents($_FILES['ebook_wp_custom_attachment_preview']['tmp_name'])); |
| 3221 |
|
| 3222 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 3223 |
wp_die( esc_html( sprintf( /* translators: %s: error message from WordPress. */ __( 'The file could not be uploaded: %s', 'ebook-store' ), $upload['error'] ) ) ); |
| 3224 |
} else { |
| 3225 |
add_post_meta($id, 'ebook_wp_custom_attachment_preview', $upload); |
| 3226 |
update_post_meta($id, 'ebook_wp_custom_attachment_preview', $upload); |
| 3227 |
} // end if/else |
| 3228 |
|
| 3229 |
} else { |
| 3230 |
wp_die( esc_html__( 'That file type is not allowed here. Check the list of accepted formats next to the upload field and try again.', 'ebook-store' ) ); |
| 3231 |
} // end if/else |
| 3232 |
|
| 3233 |
} // end if |
| 3234 |
|
| 3235 |
|
| 3236 |
//check the side photo |
| 3237 |
if(!empty($_FILES['ebook_wp_custom_attachment_side_photo']['name'])) { |
| 3238 |
|
| 3239 |
// Setup the array of supported file types. In this case, it's just PDF. |
| 3240 |
$supported_types = array('image/jpeg','image/gif','image/png','image/svg+xml'); |
| 3241 |
|
| 3242 |
// Get the file type of the upload |
| 3243 |
$arr_file_type = wp_check_filetype(basename($_FILES['ebook_wp_custom_attachment_side_photo']['name'])); |
| 3244 |
$uploaded_type = $arr_file_type['type']; |
| 3245 |
|
| 3246 |
// Check if the type is supported. If not, throw an error. |
| 3247 |
if ( ebook_store_upload_type_is_allowed( $_FILES['ebook_wp_custom_attachment_side_photo'], $supported_types ) ) { |
| 3248 |
|
| 3249 |
// Use the WordPress API to upload the file |
| 3250 |
$upload = wp_upload_bits($_FILES['ebook_wp_custom_attachment_side_photo']['name'], null, file_get_contents($_FILES['ebook_wp_custom_attachment_side_photo']['tmp_name'])); |
| 3251 |
|
| 3252 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 3253 |
wp_die( esc_html( sprintf( /* translators: %s: error message from WordPress. */ __( 'The file could not be uploaded: %s', 'ebook-store' ), $upload['error'] ) ) ); |
| 3254 |
} else { |
| 3255 |
add_post_meta($id, 'ebook_wp_custom_attachment_side_photo', $upload); |
| 3256 |
update_post_meta($id, 'ebook_wp_custom_attachment_side_photo', $upload); |
| 3257 |
} // end if/else |
| 3258 |
|
| 3259 |
} else { |
| 3260 |
wp_die( esc_html__( 'That file type is not allowed here. Check the list of accepted formats next to the upload field and try again.', 'ebook-store' ) ); |
| 3261 |
} // end if/else |
| 3262 |
|
| 3263 |
} // end if |
| 3264 |
//cover photo |
| 3265 |
if(!empty($_FILES['ebook_wp_custom_attachment_cover']['name'])) { |
| 3266 |
$filename = $_FILES['ebook_wp_custom_attachment_cover']['tmp_name']; |
| 3267 |
// Setup the array of supported file types. In this case, it's just PDF. |
| 3268 |
$supported_types = array('image/jpeg','image/gif','image/png','image/svg+xml'); |
| 3269 |
|
| 3270 |
// Get the file type of the upload |
| 3271 |
$arr_file_type = wp_check_filetype(basename($_FILES['ebook_wp_custom_attachment_cover']['name'])); |
| 3272 |
$uploaded_type = $arr_file_type['type']; |
| 3273 |
// Check if the type is supported. If not, throw an error. |
| 3274 |
if ( ebook_store_upload_type_is_allowed( $_FILES['ebook_wp_custom_attachment_cover'], $supported_types ) ) { |
| 3275 |
|
| 3276 |
// Use the WordPress API to upload the file |
| 3277 |
$img_tmp_name = $_FILES['ebook_wp_custom_attachment_cover']['tmp_name']; |
| 3278 |
ebook_store_prepare_cover_for_upload($img_tmp_name); |
| 3279 |
$upload = wp_upload_bits($_FILES['ebook_wp_custom_attachment_cover']['name'], null, file_get_contents($_FILES['ebook_wp_custom_attachment_cover']['tmp_name'])); |
| 3280 |
|
| 3281 |
if(isset($upload['error']) && $upload['error'] != 0) { |
| 3282 |
wp_die( esc_html( sprintf( /* translators: %s: error message from WordPress. */ __( 'The file could not be uploaded: %s', 'ebook-store' ), $upload['error'] ) ) ); |
| 3283 |
} else { |
| 3284 |
add_post_meta($id, 'ebook_wp_custom_attachment_cover', $upload); |
| 3285 |
update_post_meta($id, 'ebook_wp_custom_attachment_cover', $upload); |
| 3286 |
} // end if/else |
| 3287 |
|
| 3288 |
} else { |
| 3289 |
wp_die( esc_html__( 'That file type is not allowed here. Check the list of accepted formats next to the upload field and try again.', 'ebook-store' ) ); |
| 3290 |
} // end if/else |
| 3291 |
|
| 3292 |
} // end if |
| 3293 |
|
| 3294 |
if(!empty($_FILES['ebook_wp_custom_attachment_pdf']['name'])) { |
| 3295 |
ebook_store_generate_cover_from_pdf($id); |
| 3296 |
} |
| 3297 |
|
| 3298 |
|
| 3299 |
|
| 3300 |
} // end save_custom_meta_data |
| 3301 |
|
| 3302 |
function ebook_update_edit_form() { |
| 3303 |
echo ' enctype="multipart/form-data"'; |
| 3304 |
} // end ebook_update_edit_form |
| 3305 |
function ebook_download_link($ebook_order, $free = false, $bonus = false, $bonus_ebook_id = null) { |
| 3306 |
$action_name = ($free == 0) ? 'download' : 'download_free'; |
| 3307 |
|
| 3308 |
$ebook_key = isset($ebook_order['ebook_key'][0]) ? esc_attr($ebook_order['ebook_key'][0]) : ''; |
| 3309 |
$md5_nonce = isset($ebook_order['md5_nonce'][0]) ? esc_attr($ebook_order['md5_nonce'][0]) : ''; |
| 3310 |
$ebook_id = isset($ebook_order['ebook'][0]) ? absint($ebook_order['ebook'][0]) : 0; |
| 3311 |
$order_id = isset($ebook_order['order_id'][0]) ? absint($ebook_order['order_id'][0]) : ( isset($ebook_order['order_id']) ? absint($ebook_order['order_id']) : 0 ); |
| 3312 |
|
| 3313 |
$args = array( |
| 3314 |
'ebook_key' => $ebook_key, |
| 3315 |
'action' => $action_name, |
| 3316 |
'md5_nonce' => $md5_nonce, |
| 3317 |
); |
| 3318 |
|
| 3319 |
if ( $order_id > 0 ) { |
| 3320 |
$args['order_id'] = $order_id; |
| 3321 |
} |
| 3322 |
|
| 3323 |
$link = add_query_arg($args, get_permalink($ebook_id)); |
| 3324 |
$link = remove_query_arg('p', $link); |
| 3325 |
|
| 3326 |
if ($action_name === 'download_free') { |
| 3327 |
$link = add_query_arg(array('p' => $ebook_id), $link); |
| 3328 |
} |
| 3329 |
|
| 3330 |
$post = get_post($ebook_id); |
| 3331 |
if ($post) { |
| 3332 |
$slug = sanitize_title($post->post_name); |
| 3333 |
$link = add_query_arg(array('ebook' => $slug), $link); |
| 3334 |
} |
| 3335 |
|
| 3336 |
if ($bonus > 0) { |
| 3337 |
$link = add_query_arg(array('type' => 'bonus'), $link); |
| 3338 |
$link = add_query_arg(array('ebook_id' => $bonus_ebook_id), $link); |
| 3339 |
$link = add_query_arg(array('order_id' => $ebook_order['order_id'][0]), $link); |
| 3340 |
} |
| 3341 |
|
| 3342 |
return esc_url($link); |
| 3343 |
} |
| 3344 |
function humanFileSize($size,$unit="") { |
| 3345 |
if( (!$unit && $size >= 1<<30) || $unit == "GB") |
| 3346 |
return number_format($size/(1<<30),2)."GB"; |
| 3347 |
if( (!$unit && $size >= 1<<20) || $unit == "MB") |
| 3348 |
return number_format($size/(1<<20),2)."MB"; |
| 3349 |
if( (!$unit && $size >= 1<<10) || $unit == "KB") |
| 3350 |
return number_format($size/(1<<10),2)."KB"; |
| 3351 |
return number_format($size)." bytes"; |
| 3352 |
} |
| 3353 |
|
| 3354 |
function ebook_store_get_file_format_labels() { |
| 3355 |
return array( |
| 3356 |
'pdf' => 'PDF', |
| 3357 |
'epub' => 'EPUB', |
| 3358 |
'mobi' => 'MOBI', |
| 3359 |
'txt' => 'TXT', |
| 3360 |
'zip' => 'ZIP', |
| 3361 |
'mp3' => 'MP3', |
| 3362 |
'mp4' => 'MP4', |
| 3363 |
); |
| 3364 |
} |
| 3365 |
|
| 3366 |
function ebook_store_get_ebook_file_assets( $ebook_id ) { |
| 3367 |
$formats = array( 'pdf', 'epub', 'mobi', 'txt', 'zip', 'mp3', 'mp4' ); |
| 3368 |
$assets = array(); |
| 3369 |
|
| 3370 |
foreach ( $formats as $format ) { |
| 3371 |
$meta_key = $format === 'pdf' ? 'ebook_wp_custom_attachment_pdf' : 'ebook_wp_custom_attachment_' . $format; |
| 3372 |
$file_meta = get_post_meta( $ebook_id, $meta_key, true ); |
| 3373 |
if ( $format === 'pdf' && ( ! is_array( $file_meta ) || empty( $file_meta['file'] ) ) ) { |
| 3374 |
$file_meta = get_post_meta( $ebook_id, 'ebook_wp_custom_attachment', true ); |
| 3375 |
} |
| 3376 |
|
| 3377 |
if ( ! is_array( $file_meta ) || empty( $file_meta['file'] ) ) { |
| 3378 |
continue; |
| 3379 |
} |
| 3380 |
|
| 3381 |
$assets[ $format ] = $file_meta; |
| 3382 |
} |
| 3383 |
|
| 3384 |
return $assets; |
| 3385 |
} |
| 3386 |
|
| 3387 |
function ebook_store_get_ebook_file_facts( $ebook_id ) { |
| 3388 |
$assets = ebook_store_get_ebook_file_assets( $ebook_id ); |
| 3389 |
$labels = ebook_store_get_file_format_labels(); |
| 3390 |
$formats = array(); |
| 3391 |
$total_size = 0; |
| 3392 |
|
| 3393 |
foreach ( $assets as $format => $asset ) { |
| 3394 |
$formats[] = isset( $labels[ $format ] ) ? $labels[ $format ] : strtoupper( $format ); |
| 3395 |
if ( ! empty( $asset['file'] ) && file_exists( $asset['file'] ) ) { |
| 3396 |
$total_size += (int) filesize( $asset['file'] ); |
| 3397 |
} |
| 3398 |
} |
| 3399 |
|
| 3400 |
if ( empty( $formats ) ) { |
| 3401 |
return array(); |
| 3402 |
} |
| 3403 |
|
| 3404 |
$facts = array(); |
| 3405 |
$facts[] = sprintf( __( 'Formats: %s', 'ebook-store' ), implode( ', ', $formats ) ); |
| 3406 |
|
| 3407 |
if ( $total_size > 0 ) { |
| 3408 |
$facts[] = sprintf( __( 'Total download size: %s', 'ebook-store' ), humanFileSize( $total_size ) ); |
| 3409 |
} |
| 3410 |
|
| 3411 |
$facts[] = sprintf( |
| 3412 |
/* translators: %d is the number of included files */ |
| 3413 |
_n( '%d included file', '%d included files', count( $formats ), 'ebook-store' ), |
| 3414 |
count( $formats ) |
| 3415 |
); |
| 3416 |
|
| 3417 |
return $facts; |
| 3418 |
} |
| 3419 |
|
| 3420 |
function ebook_store_buy( $atts ){ |
| 3421 |
return ebook_store($atts, true); |
| 3422 |
} |
| 3423 |
function ebook_store_wp_super_cache_check() { |
| 3424 |
// error_reporting(E_ALL); |
| 3425 |
// ini_set('display_errors',true); |
| 3426 |
|
| 3427 |
if (file_exists(WP_PLUGIN_DIR . '/wp-super-cache/wp-cache.php')) { |
| 3428 |
if (strpos($_SERVER['QUERY_STRING'], 'action=thank_you') || strpos($_SERVER['QUERY_STRING'], 'ebook') || strpos($_SERVER['QUERY_STRING'], 'ipn')) { |
| 3429 |
define('DONOTCACHEPAGE',true); |
| 3430 |
} |
| 3431 |
} |
| 3432 |
|
| 3433 |
} |
| 3434 |
|
| 3435 |
/** |
| 3436 |
* Should the plugin attempt to recover an order from the gateway for this key? |
| 3437 |
* |
| 3438 |
* The thank-you page recovery path runs for any anonymous GET of |
| 3439 |
* ?ebook_key=<anything>&action=thank_you. Each attempt made a live gateway API |
| 3440 |
* call and wrote a log line, so it doubled as an unauthenticated way to burn |
| 3441 |
* the store's API rate limit and fill its disk. One attempt per key per five |
| 3442 |
* minutes is more than enough for a real buyer refreshing the page. |
| 3443 |
* |
| 3444 |
* @param string $ebook_key Order token from the request. |
| 3445 |
* @return bool |
| 3446 |
*/ |
| 3447 |
function ebook_store_can_attempt_order_recovery( $ebook_key ) { |
| 3448 |
$ebook_key = preg_replace( '/[^a-zA-Z0-9]/', '', (string) $ebook_key ); |
| 3449 |
|
| 3450 |
if ( $ebook_key === '' ) { |
| 3451 |
return false; |
| 3452 |
} |
| 3453 |
|
| 3454 |
$key = 'ebook_store_recover_' . md5( $ebook_key ); |
| 3455 |
|
| 3456 |
if ( get_transient( $key ) ) { |
| 3457 |
return false; |
| 3458 |
} |
| 3459 |
|
| 3460 |
set_transient( $key, 1, 5 * MINUTE_IN_SECONDS ); |
| 3461 |
|
| 3462 |
return true; |
| 3463 |
} |
| 3464 |
|
| 3465 |
function ebook_store_get_form_templates() { |
| 3466 |
$templates = ebook_store_get_core_form_templates(); |
| 3467 |
|
| 3468 |
foreach ( ebook_store_get_custom_form_templates() as $template_key => $template_config ) { |
| 3469 |
$templates[ (string) $template_key ] = $template_config; |
| 3470 |
} |
| 3471 |
|
| 3472 |
return apply_filters( 'ebook_store_form_templates', $templates ); |
| 3473 |
} |
| 3474 |
|
| 3475 |
function ebook_store_resolve_form_template( $requested = '' ) { |
| 3476 |
$templates = ebook_store_get_form_templates(); |
| 3477 |
$requested = sanitize_key( (string) $requested ); |
| 3478 |
|
| 3479 |
if ( $requested === '' ) { |
| 3480 |
$requested = sanitize_key( (string) get_option( 'ebook_store_form_template', '2026' ) ); |
| 3481 |
} |
| 3482 |
|
| 3483 |
if ( $requested === 'current' ) { |
| 3484 |
$requested = 'legacy'; |
| 3485 |
} |
| 3486 |
|
| 3487 |
if ( ! isset( $templates[ $requested ] ) ) { |
| 3488 |
$requested = '2026'; |
| 3489 |
} |
| 3490 |
|
| 3491 |
return apply_filters( 'ebook_store_resolved_form_template', $requested, $templates ); |
| 3492 |
} |
| 3493 |
|
| 3494 |
function ebook_store_get_gateway_button_default_text( $gateway ) { |
| 3495 |
$defaults = array( |
| 3496 |
'paypal' => __( 'Buy With PayPal (%%price%%)', 'ebook-store' ), |
| 3497 |
'stripe' => __( 'Buy With Stripe (%%price%%)', 'ebook-store' ), |
| 3498 |
'adyen' => __( 'Buy With Adyen (%%price%%)', 'ebook-store' ), |
| 3499 |
); |
| 3500 |
|
| 3501 |
$gateway = sanitize_key( (string) $gateway ); |
| 3502 |
|
| 3503 |
return isset( $defaults[ $gateway ] ) ? $defaults[ $gateway ] : __( 'Buy (%%price%%)', 'ebook-store' ); |
| 3504 |
} |
| 3505 |
|
| 3506 |
function ebook_store_get_gateway_button_text( $gateway, $language = '' ) { |
| 3507 |
$gateway = sanitize_key( (string) $gateway ); |
| 3508 |
$default_text = ebook_store_get_gateway_button_default_text( $gateway ); |
| 3509 |
$option_value = get_option( $gateway . '_button_text', array() ); |
| 3510 |
$option_value = is_array( $option_value ) ? $option_value : array(); |
| 3511 |
|
| 3512 |
if ( $language === '' ) { |
| 3513 |
if ( function_exists( 'ebook_store_get_selected_language_code' ) ) { |
| 3514 |
$language = ebook_store_get_selected_language_code(); |
| 3515 |
} |
| 3516 |
|
| 3517 |
if ( $language === '' || $language === '0' ) { |
| 3518 |
$language = substr( get_locale(), 0, 2 ); |
| 3519 |
} |
| 3520 |
} |
| 3521 |
|
| 3522 |
$language = sanitize_key( (string) $language ); |
| 3523 |
|
| 3524 |
if ( $language !== '' && $language !== '0' && ! empty( $option_value[ $language ] ) ) { |
| 3525 |
return (string) $option_value[ $language ]; |
| 3526 |
} |
| 3527 |
|
| 3528 |
if ( ! empty( $option_value[0] ) ) { |
| 3529 |
return (string) $option_value[0]; |
| 3530 |
} |
| 3531 |
|
| 3532 |
if ( ! empty( $option_value['0'] ) ) { |
| 3533 |
return (string) $option_value['0']; |
| 3534 |
} |
| 3535 |
|
| 3536 |
return $default_text; |
| 3537 |
} |
| 3538 |
|
| 3539 |
function ebook_store_ga4_is_enabled() { |
| 3540 |
return function_exists( 'ebook_store_has_pro_license' ) |
| 3541 |
&& ebook_store_has_pro_license() |
| 3542 |
&& (string) get_option( 'ebook_store_google_analytics_enabled', '0' ) === '1' |
| 3543 |
&& trim( (string) get_option( 'ebook_store_google_analytics_measurement_id', '' ) ) !== ''; |
| 3544 |
} |
| 3545 |
|
| 3546 |
function ebook_store_ga4_get_measurement_id() { |
| 3547 |
return trim( (string) get_option( 'ebook_store_google_analytics_measurement_id', '' ) ); |
| 3548 |
} |
| 3549 |
|
| 3550 |
function ebook_store_ga4_get_api_secret() { |
| 3551 |
return trim( (string) get_option( 'ebook_store_google_analytics_api_secret', '' ) ); |
| 3552 |
} |
| 3553 |
|
| 3554 |
function ebook_store_ga4_enqueue_client() { |
| 3555 |
static $did_enqueue = false; |
| 3556 |
|
| 3557 |
if ( $did_enqueue || ! ebook_store_ga4_is_enabled() || is_admin() ) { |
| 3558 |
return; |
| 3559 |
} |
| 3560 |
|
| 3561 |
$measurement_id = ebook_store_ga4_get_measurement_id(); |
| 3562 |
if ( $measurement_id === '' ) { |
| 3563 |
return; |
| 3564 |
} |
| 3565 |
|
| 3566 |
$did_enqueue = true; |
| 3567 |
|
| 3568 |
wp_enqueue_script( |
| 3569 |
'ebook-store-ga4', |
| 3570 |
'https://www.googletagmanager.com/gtag/js?id=' . rawurlencode( $measurement_id ), |
| 3571 |
array(), |
| 3572 |
null, |
| 3573 |
false |
| 3574 |
); |
| 3575 |
|
| 3576 |
$bootstrap = 'window.dataLayer=window.dataLayer||[];window.gtag=window.gtag||function(){dataLayer.push(arguments);};gtag("js",new Date());gtag("config",' . wp_json_encode( $measurement_id ) . ',{"send_page_view":false});window.ebookStoreGa4=window.ebookStoreGa4||{};window.ebookStoreGa4.measurementId=' . wp_json_encode( $measurement_id ) . ';window.ebookStoreGa4.track=function(name,params){if(typeof window.gtag!=="function"){return;}window.gtag("event",name,params||{});};document.addEventListener("click",function(event){var trigger=event.target.closest("[data-ebook-store-payment-method]");if(!trigger||!window.ebookStoreGa4){return;}var paymentMethod=trigger.getAttribute("data-ebook-store-payment-method")||"";var currency=trigger.getAttribute("data-ebook-store-currency")||"";var value=parseFloat(trigger.getAttribute("data-ebook-store-value")||"0");var itemId=trigger.getAttribute("data-ebook-store-item-id")||"";var itemName=trigger.getAttribute("data-ebook-store-item-name")||"";var payload={currency:currency,value:isNaN(value)?0:value,payment_type:paymentMethod,items:[{item_id:itemId,item_name:itemName,price:isNaN(value)?0:value,quantity:1,item_category:"ebook"}]};if(trigger.hasAttribute("data-ebook-store-add-payment-info")){window.ebookStoreGa4.track("add_payment_info",payload);}if(trigger.hasAttribute("data-ebook-store-begin-checkout")){window.ebookStoreGa4.track("begin_checkout",payload);}});'; |
| 3577 |
|
| 3578 |
wp_add_inline_script( 'ebook-store-ga4', $bootstrap, 'after' ); |
| 3579 |
} |
| 3580 |
|
| 3581 |
function ebook_store_ga4_get_item_payload( $ebook_id, $price = null ) { |
| 3582 |
$ebook_id = absint( $ebook_id ); |
| 3583 |
if ( ! $ebook_id ) { |
| 3584 |
return array(); |
| 3585 |
} |
| 3586 |
|
| 3587 |
if ( null === $price ) { |
| 3588 |
$ebook_meta = get_post_meta( $ebook_id, 'ebook', true ); |
| 3589 |
$price = is_array( $ebook_meta ) && isset( $ebook_meta['ebook_price'] ) ? (float) ebook_store_price_plus_vat( $ebook_meta['ebook_price'] ) : 0.0; |
| 3590 |
} |
| 3591 |
|
| 3592 |
return array( |
| 3593 |
'item_id' => (string) $ebook_id, |
| 3594 |
'item_name' => get_the_title( $ebook_id ), |
| 3595 |
'price' => (float) $price, |
| 3596 |
'quantity' => 1, |
| 3597 |
'item_category' => 'ebook', |
| 3598 |
); |
| 3599 |
} |
| 3600 |
|
| 3601 |
function ebook_store_ga4_render_view_item_tracking( $items ) { |
| 3602 |
if ( ! ebook_store_ga4_is_enabled() || empty( $items ) || ! is_array( $items ) ) { |
| 3603 |
return ''; |
| 3604 |
} |
| 3605 |
|
| 3606 |
$payloads = array(); |
| 3607 |
foreach ( $items as $item ) { |
| 3608 |
$ebook_id = isset( $item['ebook_id'] ) ? absint( $item['ebook_id'] ) : 0; |
| 3609 |
if ( ! $ebook_id ) { |
| 3610 |
continue; |
| 3611 |
} |
| 3612 |
|
| 3613 |
$price = isset( $item['ebook']['ebook_price'] ) ? (float) ebook_store_price_plus_vat( $item['ebook']['ebook_price'] ) : 0.0; |
| 3614 |
$payloads[] = array( |
| 3615 |
'event_key' => 'ebook-store-view-item-' . $ebook_id, |
| 3616 |
'params' => array( |
| 3617 |
'currency' => isset( $item['paypal_currency'] ) && $item['paypal_currency'] !== '' ? (string) $item['paypal_currency'] : ebook_store_get_store_currency(), |
| 3618 |
'value' => $price, |
| 3619 |
'items' => array( ebook_store_ga4_get_item_payload( $ebook_id, $price ) ), |
| 3620 |
), |
| 3621 |
); |
| 3622 |
} |
| 3623 |
|
| 3624 |
if ( empty( $payloads ) ) { |
| 3625 |
return ''; |
| 3626 |
} |
| 3627 |
|
| 3628 |
return '<script>(function(){window.ebookStoreGa4Viewed=window.ebookStoreGa4Viewed||{};var payloads=' . wp_json_encode( $payloads ) . ';function fire(){if(!window.ebookStoreGa4||typeof window.ebookStoreGa4.track!=="function"){return;}payloads.forEach(function(entry){if(window.ebookStoreGa4Viewed[entry.event_key]){return;}window.ebookStoreGa4Viewed[entry.event_key]=true;window.ebookStoreGa4.track("view_item",entry.params);});}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",fire,{once:true});}else{fire();}})();</script>'; |
| 3629 |
} |
| 3630 |
|
| 3631 |
function ebook_store_ga4_get_order_client_id( $order_id, $order = array() ) { |
| 3632 |
$order_id = absint( $order_id ); |
| 3633 |
$stored = trim( (string) get_post_meta( $order_id, 'ebook_store_ga4_client_id', true ) ); |
| 3634 |
if ( $stored !== '' ) { |
| 3635 |
return $stored; |
| 3636 |
} |
| 3637 |
|
| 3638 |
$email = ''; |
| 3639 |
if ( is_array( $order ) ) { |
| 3640 |
if ( isset( $order['payer_email'][0] ) ) { |
| 3641 |
$email = (string) $order['payer_email'][0]; |
| 3642 |
} elseif ( isset( $order['payer_email'] ) ) { |
| 3643 |
$email = (string) $order['payer_email']; |
| 3644 |
} |
| 3645 |
} |
| 3646 |
|
| 3647 |
$hash = md5( $order_id . '|' . strtolower( trim( $email ) ) . '|' . home_url( '/' ) ); |
| 3648 |
$client_id = substr( $hash, 0, 10 ) . '.' . substr( $hash, 10, 10 ); |
| 3649 |
update_post_meta( $order_id, 'ebook_store_ga4_client_id', $client_id ); |
| 3650 |
|
| 3651 |
return $client_id; |
| 3652 |
} |
| 3653 |
|
| 3654 |
function ebook_store_ga4_send_measurement_protocol_event( $event_name, $params, $client_id ) { |
| 3655 |
if ( ! ebook_store_ga4_is_enabled() ) { |
| 3656 |
return false; |
| 3657 |
} |
| 3658 |
|
| 3659 |
$measurement_id = ebook_store_ga4_get_measurement_id(); |
| 3660 |
$api_secret = ebook_store_ga4_get_api_secret(); |
| 3661 |
$client_id = trim( (string) $client_id ); |
| 3662 |
|
| 3663 |
if ( $measurement_id === '' || $api_secret === '' || $client_id === '' ) { |
| 3664 |
return false; |
| 3665 |
} |
| 3666 |
|
| 3667 |
$response = wp_remote_post( |
| 3668 |
'https://www.google-analytics.com/mp/collect?measurement_id=' . rawurlencode( $measurement_id ) . '&api_secret=' . rawurlencode( $api_secret ), |
| 3669 |
array( |
| 3670 |
'timeout' => 4, |
| 3671 |
'blocking' => false, |
| 3672 |
'headers' => array( |
| 3673 |
'Content-Type' => 'application/json', |
| 3674 |
), |
| 3675 |
'body' => wp_json_encode( |
| 3676 |
array( |
| 3677 |
'client_id' => $client_id, |
| 3678 |
'events' => array( |
| 3679 |
array( |
| 3680 |
'name' => $event_name, |
| 3681 |
'params' => array_merge( |
| 3682 |
array( |
| 3683 |
'engagement_time_msec' => 1, |
| 3684 |
), |
| 3685 |
(array) $params |
| 3686 |
), |
| 3687 |
), |
| 3688 |
), |
| 3689 |
) |
| 3690 |
), |
| 3691 |
) |
| 3692 |
); |
| 3693 |
|
| 3694 |
return ! is_wp_error( $response ); |
| 3695 |
} |
| 3696 |
|
| 3697 |
function ebook_store_ga4_get_order_payload( $order_id ) { |
| 3698 |
$order_id = absint( $order_id ); |
| 3699 |
if ( ! $order_id || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 3700 |
return array(); |
| 3701 |
} |
| 3702 |
|
| 3703 |
$ebook_id = absint( get_post_meta( $order_id, 'ebook', true ) ); |
| 3704 |
$gross = (float) get_post_meta( $order_id, 'mc_gross', true ); |
| 3705 |
$currency = strtoupper( (string) get_post_meta( $order_id, 'mc_currency', true ) ); |
| 3706 |
$txn_id = trim( (string) get_post_meta( $order_id, 'txn_id', true ) ); |
| 3707 |
|
| 3708 |
if ( ! $ebook_id || $gross <= 0 ) { |
| 3709 |
return array(); |
| 3710 |
} |
| 3711 |
|
| 3712 |
return array( |
| 3713 |
'transaction_id' => $txn_id !== '' ? $txn_id : (string) $order_id, |
| 3714 |
'currency' => $currency !== '' ? $currency : ebook_store_get_store_currency(), |
| 3715 |
'value' => $gross, |
| 3716 |
'items' => array( ebook_store_ga4_get_item_payload( $ebook_id, $gross ) ), |
| 3717 |
); |
| 3718 |
} |
| 3719 |
|
| 3720 |
function ebook_store_ga4_track_purchase_for_order( $order_id ) { |
| 3721 |
$order_id = absint( $order_id ); |
| 3722 |
if ( ! $order_id || ! ebook_store_ga4_is_enabled() ) { |
| 3723 |
return false; |
| 3724 |
} |
| 3725 |
|
| 3726 |
if ( get_post_meta( $order_id, 'ebook_store_ga4_purchase_tracked', true ) ) { |
| 3727 |
return true; |
| 3728 |
} |
| 3729 |
|
| 3730 |
$payment_type = strtolower( trim( (string) get_post_meta( $order_id, 'payment_type', true ) ) ); |
| 3731 |
$txn_id = strtolower( trim( (string) get_post_meta( $order_id, 'txn_id', true ) ) ); |
| 3732 |
$gross = (float) get_post_meta( $order_id, 'mc_gross', true ); |
| 3733 |
|
| 3734 |
if ( $gross <= 0 || in_array( $payment_type, array( 'new_issue' ), true ) || strpos( $txn_id, 'bonus download' ) === 0 ) { |
| 3735 |
return false; |
| 3736 |
} |
| 3737 |
|
| 3738 |
$params = ebook_store_ga4_get_order_payload( $order_id ); |
| 3739 |
if ( empty( $params ) ) { |
| 3740 |
return false; |
| 3741 |
} |
| 3742 |
|
| 3743 |
$sent = ebook_store_ga4_send_measurement_protocol_event( |
| 3744 |
'purchase', |
| 3745 |
$params, |
| 3746 |
ebook_store_ga4_get_order_client_id( $order_id, ebook_get_order( 'order_id', $order_id ) ) |
| 3747 |
); |
| 3748 |
|
| 3749 |
if ( $sent ) { |
| 3750 |
update_post_meta( $order_id, 'ebook_store_ga4_purchase_tracked', current_time( 'mysql', true ) ); |
| 3751 |
} |
| 3752 |
|
| 3753 |
return $sent; |
| 3754 |
} |
| 3755 |
|
| 3756 |
function ebook_store_ga4_track_refund_for_order( $order_id ) { |
| 3757 |
$order_id = absint( $order_id ); |
| 3758 |
if ( ! $order_id || ! ebook_store_ga4_is_enabled() ) { |
| 3759 |
return false; |
| 3760 |
} |
| 3761 |
|
| 3762 |
if ( get_post_meta( $order_id, 'ebook_store_ga4_refund_tracked', true ) ) { |
| 3763 |
return true; |
| 3764 |
} |
| 3765 |
|
| 3766 |
$params = ebook_store_ga4_get_order_payload( $order_id ); |
| 3767 |
if ( empty( $params ) ) { |
| 3768 |
return false; |
| 3769 |
} |
| 3770 |
|
| 3771 |
$sent = ebook_store_ga4_send_measurement_protocol_event( |
| 3772 |
'refund', |
| 3773 |
$params, |
| 3774 |
ebook_store_ga4_get_order_client_id( $order_id, ebook_get_order( 'order_id', $order_id ) ) |
| 3775 |
); |
| 3776 |
|
| 3777 |
if ( $sent ) { |
| 3778 |
update_post_meta( $order_id, 'ebook_store_ga4_refund_tracked', current_time( 'mysql', true ) ); |
| 3779 |
} |
| 3780 |
|
| 3781 |
return $sent; |
| 3782 |
} |
| 3783 |
|
| 3784 |
function ebook_store_ga4_watch_order_status_changes( $meta_id, $object_id, $meta_key, $meta_value ) { |
| 3785 |
if ( 'payment_status' !== $meta_key || get_post_type( $object_id ) !== 'ebook_order' ) { |
| 3786 |
return; |
| 3787 |
} |
| 3788 |
|
| 3789 |
$current_status = trim( (string) $meta_value ); |
| 3790 |
$previous_status = trim( (string) get_post_meta( $object_id, 'ebook_store_ga4_last_known_status', true ) ); |
| 3791 |
update_post_meta( $object_id, 'ebook_store_ga4_last_known_status', $current_status ); |
| 3792 |
|
| 3793 |
if ( $current_status === '' || $current_status === $previous_status ) { |
| 3794 |
return; |
| 3795 |
} |
| 3796 |
|
| 3797 |
if ( ebook_store_is_negative_order_status( $current_status ) && ! ebook_store_is_negative_order_status( $previous_status ) ) { |
| 3798 |
ebook_store_ga4_track_refund_for_order( $object_id ); |
| 3799 |
} |
| 3800 |
} |
| 3801 |
add_action( 'added_post_meta', 'ebook_store_ga4_watch_order_status_changes', 10, 4 ); |
| 3802 |
add_action( 'updated_post_meta', 'ebook_store_ga4_watch_order_status_changes', 10, 4 ); |
| 3803 |
|
| 3804 |
function ebook_store_render_form_template( $template, $context ) { |
| 3805 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 3806 |
$locale = ebook_store_get_locale_strings(); |
| 3807 |
$formats_locale = ebook_store_get_format_labels(); |
| 3808 |
$templates = ebook_store_get_form_templates(); |
| 3809 |
if ( ! isset( $templates[ $template ] ) ) { |
| 3810 |
$template = 'modern'; |
| 3811 |
} |
| 3812 |
|
| 3813 |
$template_file = isset( $templates[ $template ]['file'] ) ? $templates[ $template ]['file'] : ''; |
| 3814 |
if ( ! $template_file || ! file_exists( $template_file ) ) { |
| 3815 |
return ''; |
| 3816 |
} |
| 3817 |
|
| 3818 |
$context = is_array( $context ) ? $context : array(); |
| 3819 |
|
| 3820 |
ob_start(); |
| 3821 |
include $template_file; |
| 3822 |
$template_output = ob_get_clean(); |
| 3823 |
return function_exists( 'ebook_store_translate_markup' ) ? ebook_store_translate_markup( $template_output ) : $template_output; |
| 3824 |
} |
| 3825 |
|
| 3826 |
function ebook_store( $atts, $buyNowOnly = false ){ |
| 3827 |
if (!wp_script_is('jquery', 'enqueued')) { |
| 3828 |
wp_enqueue_script('jquery'); |
| 3829 |
} |
| 3830 |
define('DONOTCACHEPAGE',true); |
| 3831 |
//@error_reporting(E_ALL); |
| 3832 |
//ini_set('display_errors',1); |
| 3833 |
if (is_array($atts) == false) { |
| 3834 |
$atts = array('ebook_id' => $atts); |
| 3835 |
} |
| 3836 |
$atts = shortcode_atts( |
| 3837 |
array( |
| 3838 |
'ebook_id' => 0, |
| 3839 |
'template' => '', |
| 3840 |
), |
| 3841 |
$atts, |
| 3842 |
'ebook_store' |
| 3843 |
); |
| 3844 |
try { |
| 3845 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 3846 |
$locale = ebook_store_get_locale_strings(); |
| 3847 |
$formats_locale = ebook_store_get_format_labels(); |
| 3848 |
|
| 3849 |
global $ebook_store_messages; |
| 3850 |
global $woocommerce; |
| 3851 |
|
| 3852 |
if (is_array($ebook_store_messages) and count($ebook_store_messages) > 0) { |
| 3853 |
foreach ($ebook_store_messages as $ebook_store_messages => $message) { |
| 3854 |
echo $message; |
| 3855 |
} |
| 3856 |
} |
| 3857 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 3858 |
ebook_store_ga4_enqueue_client(); |
| 3859 |
//wp_enqueue_script( 'ebook_store_settings', plugins_url( '/js/ebook_store_settings.js' , __FILE__ ), array(), '1.0.0', true ); |
| 3860 |
$post_id = $atts['ebook_id']; |
| 3861 |
$template = ''; |
| 3862 |
if ( ! empty( $atts['template'] ) ) { |
| 3863 |
$template = ebook_store_resolve_form_template( $atts['template'] ); |
| 3864 |
} |
| 3865 |
|
| 3866 |
if (@$_REQUEST['ebook_key'] != false && @$_REQUEST['action'] == 'thank_you') { |
| 3867 |
|
| 3868 |
add_action( 'wp_footer', 'ebook_store_deregister_embeds' ); |
| 3869 |
if ($_REQUEST['ebook_key'] == -1) { |
| 3870 |
$wp_query_statement = array ( |
| 3871 |
'post_type' => 'ebook_order', |
| 3872 |
'meta_key' => 'user_id', |
| 3873 |
'meta_value' => get_current_user_id(), |
| 3874 |
'orderby' => 'date', |
| 3875 |
'posts_per_page' => -1, |
| 3876 |
'order' => 'DESC' ); |
| 3877 |
// print_r($wp_query_statement); |
| 3878 |
// die(); |
| 3879 |
$loop = new WP_Query($wp_query_statement); |
| 3880 |
while ( $loop->have_posts() ) { |
| 3881 |
$loop->the_post(); |
| 3882 |
$ebook_key = get_post_meta(get_the_ID(), 'ebook_key', true); |
| 3883 |
$_REQUEST['ebook_key'] = $ebook_key; |
| 3884 |
$uri = esc_url_raw($_SERVER['REQUEST_URI']); |
| 3885 |
$uri = remove_query_arg('ebook_key',$uri); |
| 3886 |
die("<script> if (window.location != window.parent.location) { window.parent.location = '".add_query_arg('ebook_key',$ebook_key)."'; } else { window.location = '".add_query_arg('ebook_key',$ebook_key)."'; }</script>"); |
| 3887 |
echo get_the_ID() . '<br />'; |
| 3888 |
@$toReset = 1; |
| 3889 |
//break; |
| 3890 |
} |
| 3891 |
die(); |
| 3892 |
if (@$toReset) { |
| 3893 |
wp_reset_postdata(); |
| 3894 |
} |
| 3895 |
|
| 3896 |
} |
| 3897 |
$content = ebook_store_get_effective_confirmation_page_content( 0 ); |
| 3898 |
|
| 3899 |
$ebook_order = ebook_get_order('ebook_key', $_REQUEST['ebook_key']); |
| 3900 |
if ( |
| 3901 |
( ! $ebook_order || isset( $ebook_order['ebook'] ) == false ) |
| 3902 |
&& get_option( 'stripe_integration_enabled', 0 ) |
| 3903 |
&& ebook_store_can_attempt_order_recovery( isset( $_REQUEST['ebook_key'] ) ? $_REQUEST['ebook_key'] : '' ) |
| 3904 |
) { |
| 3905 |
$recovered_order_id = 0; |
| 3906 |
$stripe_session_id = isset( $_REQUEST['stripe_session_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['stripe_session_id'] ) ) : ''; |
| 3907 |
if ( $stripe_session_id !== '' ) { |
| 3908 |
$recovered_order_id = ebook_store_recover_stripe_order_by_session_id( $stripe_session_id ); |
| 3909 |
} else { |
| 3910 |
$recovered_order_id = ebook_store_recover_stripe_order_by_ebook_key( |
| 3911 |
sanitize_text_field( wp_unslash( $_REQUEST['ebook_key'] ) ) |
| 3912 |
); |
| 3913 |
} |
| 3914 |
if ( is_wp_error( $recovered_order_id ) ) { |
| 3915 |
ebook_store_log_stripe_event( |
| 3916 |
'Stripe thank-you recovery skipped', |
| 3917 |
array( |
| 3918 |
'ebook_key' => sanitize_text_field( wp_unslash( $_REQUEST['ebook_key'] ) ), |
| 3919 |
'stripe_session_id' => $stripe_session_id, |
| 3920 |
'error' => $recovered_order_id->get_error_message(), |
| 3921 |
) |
| 3922 |
); |
| 3923 |
} elseif ( $recovered_order_id ) { |
| 3924 |
$ebook_order = ebook_get_order( 'ebook_key', $_REQUEST['ebook_key'] ); |
| 3925 |
} |
| 3926 |
} |
| 3927 |
if ( |
| 3928 |
( ! $ebook_order || isset( $ebook_order['ebook'] ) == false ) |
| 3929 |
&& get_option( 'adyen_integration_enabled', 0 ) |
| 3930 |
&& ebook_store_can_attempt_order_recovery( isset( $_REQUEST['ebook_key'] ) ? $_REQUEST['ebook_key'] : '' ) |
| 3931 |
) { |
| 3932 |
$recovered_order_id = ebook_store_recover_adyen_order_by_ebook_key( |
| 3933 |
sanitize_text_field( wp_unslash( $_REQUEST['ebook_key'] ) ) |
| 3934 |
); |
| 3935 |
if ( is_wp_error( $recovered_order_id ) ) { |
| 3936 |
ebook_store_log_adyen_event( |
| 3937 |
'Adyen thank-you recovery skipped', |
| 3938 |
array( |
| 3939 |
'ebook_key' => sanitize_text_field( wp_unslash( $_REQUEST['ebook_key'] ) ), |
| 3940 |
'error' => $recovered_order_id->get_error_message(), |
| 3941 |
) |
| 3942 |
); |
| 3943 |
} elseif ( $recovered_order_id ) { |
| 3944 |
$ebook_order = ebook_get_order( 'ebook_key', $_REQUEST['ebook_key'] ); |
| 3945 |
} |
| 3946 |
} |
| 3947 |
//wp_die(var_dump($ebook_order)); |
| 3948 |
$time = strtotime(@$ebook_order['payment_date'][0]); |
| 3949 |
//wp_die("$time-".time().'='.$time - time() ); |
| 3950 |
//print_r($ebook_order); |
| 3951 |
if (@$ebook_order['item_name'][0] == '') { |
| 3952 |
$ebook_order['item_name'][0] = get_the_title(@$ebook_order['ebook'][0]); |
| 3953 |
} |
| 3954 |
|
| 3955 |
//wp_die('ebook order var ' . var_export($ebook_order,true)); |
| 3956 |
if (!$ebook_order || @strtotime($ebook_order['payment_date'][0]) == 0 || isset($ebook_order['ebook']) == false) { // || time() < @strtotime($ebook_order['payment_date'][0]) + 25 || |
| 3957 |
return '<h4>' . $locale['confirmation'] . '</h4>' . '<script> window.setTimeout(\'location.reload()\', 5000); |
| 3958 |
</script>'; |
| 3959 |
} |
| 3960 |
$ebook_order_ebook_id = isset($ebook_order['ebook'][0]) ? absint($ebook_order['ebook'][0]) : 0; |
| 3961 |
$content = ebook_store_get_effective_confirmation_page_content($ebook_order_ebook_id); |
| 3962 |
$file = get_post_meta($ebook_order['ebook'][0],'ebook_wp_custom_attachment',true); |
| 3963 |
//wp_die(print_r($file,true)); |
| 3964 |
if (@!$file) { |
| 3965 |
$file = array(); |
| 3966 |
} |
| 3967 |
$ebook_order['downloadlink'][0] = ebook_download_link($ebook_order); |
| 3968 |
$ebook_order['downloadlink'][0] = str_replace('#038;','&',$ebook_order['downloadlink'][0]); |
| 3969 |
$ebook_order['downloadlink'][0] = str_replace('&&','&',$ebook_order['downloadlink'][0]); |
| 3970 |
//<a href="%%downloadlink%%" target="_blank" rel="noopener">%%item_name%%</a> (%%filesize%%) |
| 3971 |
@$ebook_order['pdf_reader'][0] = ebook_pdf_reader($ebook_order); |
| 3972 |
@$ebook_order['download_links'][0] = implode("<br />",ebook_download_links($ebook_order)); |
| 3973 |
@$ebook_order['ebook_bonus'][0] = (implode("<br />",ebook_download_links_bonus($ebook_order)) != '' ? implode("<br />",ebook_download_links_bonus($ebook_order)) : __('None','ebook-store')); |
| 3974 |
@$ebook_order['filesize'][0] = humanFileSize(filesize($file['file'])); |
| 3975 |
// $ebook_order['downloadlink_html'][0] = '<a href="'.ebook_download_link($ebook_order).'" target="_blank" rel="noopener">'.$ebook_order['item_name'][0].'</a> ('.$ebook_order['filesize'][0].')'; |
| 3976 |
//print_r($ebook_order); |
| 3977 |
//file_put_contents(__DIR__ . '/debug.log', 'downloadlink: ' . $ebook_order['downloadlink'][0] . "\n", FILE_APPEND); |
| 3978 |
$ebookObj = new EbookStoreEbook(@$ebook_order['ebook'][0]); |
| 3979 |
// This is a plugin order, not a WooCommerce one: hand link() the ebook_key |
| 3980 |
// credentials so the non-PDF format links resolve to the download path |
| 3981 |
// that this order is actually authorised for. |
| 3982 |
$ebookObj->ebook_key = (strlen(@$ebook_order['ebook_key'][0]) == 32 ? @$ebook_order['ebook_key'][0] : @$ebook_order['ebook_key_array'][0]); |
| 3983 |
$ebookObj->md5_nonce = @$ebook_order['md5_nonce'][0]; |
| 3984 |
$ebookObj->order_id = isset($ebook_order['order_id'][0]) ? absint($ebook_order['order_id'][0]) : 0; |
| 3985 |
$ebookObj->setLink['pdf'] = $ebook_order['downloadlink'][0]; |
| 3986 |
$ebook_order['downloadlink_html'][0] = $ebookObj->format_links(); |
| 3987 |
//$ebook_order['downloadlink_html'][0] = $ebookObj->format_links(); |
| 3988 |
|
| 3989 |
if (@$ebook_order['password'] == '') { |
| 3990 |
$ebook_order['password'] = $ebook_order['payer_email']; |
| 3991 |
} |
| 3992 |
if (get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_1', true) != '') { |
| 3993 |
$ebook_order['ebook_store_alternative_location_1'] = array(0 => '<p style="color:gray;">Download to Your Device: (This will take a very long time and requires some technical knowledge but we have provided an instructions page)</p><a href="' . get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_1', true) . '" target="_blank"><h1 style="color:red;"">Add to Your Device</h1> |
| 3994 |
</a>'); |
| 3995 |
|
| 3996 |
} |
| 3997 |
if (get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_2', true) != '') { |
| 3998 |
$ebook_order['ebook_store_alternative_location_2'] = array(0 => '<a href="' . get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_2', true) . '" target="_blank"> |
| 3999 |
<h1 style="color:red;">View Book Online Now</h1> |
| 4000 |
</a>'); |
| 4001 |
} |
| 4002 |
|
| 4003 |
if (get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_3', true) != '') { |
| 4004 |
$ebook_order['ebook_store_alternative_location_3'] = array(0 => '<a href="' . get_post_meta($ebook_order['ebook'][0],'ebook_store_alternative_location_3', true) . '" target="_blank"> |
| 4005 |
<h1 style="color:red;">Download PDF</h1> |
| 4006 |
</a>'); |
| 4007 |
} |
| 4008 |
foreach ($ebook_order as $k => $arr) { |
| 4009 |
$content = str_replace('%%' . $k . '%%', $arr[0], $content); |
| 4010 |
} |
| 4011 |
return apply_filters('the_content',$content); |
| 4012 |
//return 'test 123' . $content; |
| 4013 |
} |
| 4014 |
|
| 4015 |
$template_items = array(); |
| 4016 |
$args = array( 'post_type' => 'ebook', 'posts_per_page' => -1, 'p' => @$atts['ebook_id'] ); |
| 4017 |
$loop = new WP_Query( $args ); |
| 4018 |
|
| 4019 |
while ( $loop->have_posts() ) : $loop->the_post(); |
| 4020 |
/*the_title(); |
| 4021 |
echo '<div class="entry-content">'; |
| 4022 |
the_content(); |
| 4023 |
echo '</div>';*/ |
| 4024 |
$ebook = get_post_meta(get_the_ID(), 'ebook', true); |
| 4025 |
if ( $template === '' && is_array( $ebook ) && ! empty( $ebook['form_template'] ) ) { |
| 4026 |
$template = ebook_store_resolve_form_template( $ebook['form_template'] ); |
| 4027 |
} |
| 4028 |
$ebook['ebook_price'] = floatval($ebook['ebook_price']); |
| 4029 |
//error_log(var_export($ebook,true)); |
| 4030 |
$ebook_key = md5(NONCE_KEY . get_the_ID() . $ebook['ebook_price'] . mt_rand(1,100000)); |
| 4031 |
//$ebook['ebook_price'] = is_float($ebook['ebook_price']) ? $ebook['ebook_price'] : 0.0; |
| 4032 |
$md5_nonce = md5(mt_rand(1,9999) . NONCE_KEY . get_the_ID() . @number_format($ebook['ebook_price'], 2, '.', ',')); |
| 4033 |
//$custom = get_the_ID() . '|' . $md5_nonce; |
| 4034 |
$custom = get_the_ID() . '|' . md5(NONCE_KEY . get_the_ID() . @number_format($ebook['ebook_price'], 2, '.', ',')); |
| 4035 |
|
| 4036 |
if (get_option('ebook_store_wp_affiliate_integration') == 1) { /* && file_exists('../affiliates-manager/config.php')*/ |
| 4037 |
$parameters = apply_filters('ebook_store_payment_gateway_parameters', array('custom' => $custom)); |
| 4038 |
} |
| 4039 |
|
| 4040 |
$c = new Currencies(); |
| 4041 |
$img = get_post_meta(get_the_ID(), 'ebook_wp_custom_attachment', true); |
| 4042 |
$preview = get_post_meta(get_the_ID(), 'ebook_wp_custom_attachment_preview', true); |
| 4043 |
if (is_array($preview) == false) { $preview = array(); } |
| 4044 |
if (@$preview['url'] != '') { |
| 4045 |
if (get_option( 'ebook_store_no_viewerjs_previews', false ) == false) { |
| 4046 |
@$preview['url'] = plugins_url('',__FILE__) . '/includes/ViewerJS/index.html#' . $preview['url']; |
| 4047 |
} |
| 4048 |
} |
| 4049 |
|
| 4050 |
$cover = get_post_meta(get_the_ID(),'ebook_wp_custom_attachment_cover',true); |
| 4051 |
$side = get_post_meta(get_the_ID(),'ebook_wp_custom_attachment_side_photo',true); |
| 4052 |
if (!$side) { |
| 4053 |
$side = $cover; |
| 4054 |
} |
| 4055 |
//style="background-url:(' . $side['url'] . ');" |
| 4056 |
if (is_array(@$ebook['ebook_publisher'])) { |
| 4057 |
$publishers = array(); |
| 4058 |
foreach ($ebook['ebook_publisher'] as $p => $pp) { |
| 4059 |
$publishers[] = get_the_title($pp); |
| 4060 |
} |
| 4061 |
} |
| 4062 |
if (is_array(@$ebook['ebook_author'])) { |
| 4063 |
$authors = array(); |
| 4064 |
foreach ($ebook['ebook_author'] as $p => $pp) { |
| 4065 |
$authors[] = get_the_title($pp); |
| 4066 |
} |
| 4067 |
} |
| 4068 |
@$publishers = is_array($publishers) ? $publishers : array(); |
| 4069 |
@$authors = is_array($authors) ? $authors : array(); |
| 4070 |
$publishers = @implode(", ", $publishers); |
| 4071 |
$authors = @implode(", ", $authors); |
| 4072 |
/* div class front, where is it?*/ |
| 4073 |
|
| 4074 |
if ( $img == '' && ! ebook_store_has_pro_license() && current_user_can( 'edit_post', get_the_ID() ) ) { |
| 4075 |
printf( |
| 4076 |
'<div class="ebook-store-notice"><h3>%1$s</h3><p><a href="%2$s">%3$s</a></p></div>', |
| 4077 |
esc_html__( 'This ebook has no file attached yet, so there is nothing for buyers to download.', 'ebook-store' ), |
| 4078 |
esc_url( get_edit_post_link( get_the_ID() ) ), |
| 4079 |
esc_html__( 'Open the ebook and upload a file', 'ebook-store' ) |
| 4080 |
); |
| 4081 |
} |
| 4082 |
$paypal_currency = ( ! empty( $ebook['paypal_currency'] ) ? strtoupper( trim( (string) $ebook['paypal_currency'] ) ) : ebook_store_get_store_currency() ); |
| 4083 |
if ( ! preg_match( '/^[A-Z]{3}$/', $paypal_currency ) ) { |
| 4084 |
$paypal_currency = ebook_store_get_store_currency(); |
| 4085 |
} |
| 4086 |
$md5rand = md5(rand(1,10000) . microtime()); |
| 4087 |
$payment_methods = array(); |
| 4088 |
$paypal_ready = ebook_store_is_paypal_enabled() && trim( (string) get_option( 'paypal_account', '' ) ) !== ''; |
| 4089 |
|
| 4090 |
$formatted_paypal_price = $c->getSymbol($paypal_currency) . @number_format(ebook_store_price_plus_vat($ebook['ebook_price']),2); |
| 4091 |
$buyNowLinkText = str_replace( '%%price%%', $formatted_paypal_price, ebook_store_get_gateway_button_text( 'paypal' ) ); |
| 4092 |
$buyNowLinkOnClick = 'document.getElementById(\'' . $md5rand . '\').submit(); return false;'; |
| 4093 |
$buyNowLinkHref = ''; |
| 4094 |
$form = ''; |
| 4095 |
$show_primary_buy_link = false; |
| 4096 |
$primary_payment_method = ''; |
| 4097 |
if ($ebook['ebook_price'] === 0 || $ebook['donate_or_download'] == 'free') { |
| 4098 |
$buyNowLinkText = $locale['download']; |
| 4099 |
$buyNowLinkOnClickOriginal = ebook_download_link(array('ebook' => array(0 => get_the_ID()), 'ebook_key' => array(0 => $ebook_key), 'md5_nonce' => array(0 => $md5_nonce)),1); |
| 4100 |
$buyNowLinkOnClick = "window.location = '" . $buyNowLinkOnClickOriginal . "'; return false;"; |
| 4101 |
$buyNowLinkHref = $buyNowLinkOnClickOriginal; |
| 4102 |
$show_primary_buy_link = true; |
| 4103 |
} else if ($ebook['ebook_price'] === 0 || $ebook['donate_or_download'] == 'donate') { |
| 4104 |
$buyNowLinkText = $locale['download']; |
| 4105 |
$ebook['ebook_price'] = 0; |
| 4106 |
$show_primary_buy_link = true; |
| 4107 |
} else if ( $paypal_ready ) { |
| 4108 |
$payment_methods['paypal'] = array( |
| 4109 |
'key' => 'paypal', |
| 4110 |
'label' => 'PayPal', |
| 4111 |
'href' => '#', |
| 4112 |
'onclick' => $buyNowLinkOnClick, |
| 4113 |
'price' => $formatted_paypal_price, |
| 4114 |
); |
| 4115 |
$show_primary_buy_link = true; |
| 4116 |
$primary_payment_method = 'paypal'; |
| 4117 |
} |
| 4118 |
|
| 4119 |
if ($img == '' && get_option( 'ebook_store_license_key') == '') { |
| 4120 |
$buyNowLinkOnClick = 'alert(\'There is no ebook file uploaded. Please upload a file first in the Ebook Store plugin.\');'; |
| 4121 |
} |
| 4122 |
if (get_option('formEnabled') == 1) { |
| 4123 |
$buyNowLinkOnClickOriginal = $buyNowLinkOnClick; |
| 4124 |
$buyNowLinkOnClick = "ebook_store_popup(function () {" . $buyNowLinkOnClick . "}, this);"; |
| 4125 |
wp_register_style( 'ebook_store_tinglecss', plugins_url('/includes/tingle-master/dist/tingle.min.css', __FILE__) ); |
| 4126 |
wp_enqueue_style( 'ebook_store_tinglecss' ); |
| 4127 |
wp_enqueue_script( 'ebook_store_site_modal', plugins_url( '/includes/tingle-master/dist/tingle.min.js' , __FILE__ ), array(), '1.0.0', true ); |
| 4128 |
wp_enqueue_script( 'ebook_store_site', plugins_url( '/js/ebook_store_site.js' , __FILE__ ), array(), '1.0.0', true ); |
| 4129 |
|
| 4130 |
} |
| 4131 |
|
| 4132 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 4133 |
$checkoutPage = esc_attr(get_option('ebook_store_checkout_page')); |
| 4134 |
if (!$checkoutPage) { |
| 4135 |
$checkoutPage = $post_id; |
| 4136 |
} |
| 4137 |
|
| 4138 |
$bookpost = get_post(get_the_ID()); |
| 4139 |
$add_to_cart_label = ! empty( $locale['addtocart'] ) ? $locale['addtocart'] : __( 'Add to cart', 'ebook-store' ); |
| 4140 |
$woocommerce_product_id = ebook_store_get_wc_product_reference_for_ebook( get_the_ID() ); |
| 4141 |
if ($woocommerce_product_id > 0 && class_exists('woocommerce') && get_option('ebook_store_woocommerce_integration')) { |
| 4142 |
$woocommerce_checkout_url = add_query_arg(array('woocommerce_product_id' => $woocommerce_product_id), get_permalink(get_the_ID())); |
| 4143 |
$addToCartButton = '<a href="' . esc_url( $woocommerce_checkout_url ) . '" class="add_to_cart_link ebook-store-add-to-cart-link" data-ebook-store-payment-method="woocommerce" data-ebook-store-begin-checkout="1" data-ebook-store-item-id="' . esc_attr( get_the_ID() ) . '" data-ebook-store-item-name="' . esc_attr( $bookpost->post_title ) . '" data-ebook-store-currency="' . esc_attr( $paypal_currency ) . '" data-ebook-store-value="' . esc_attr( (string) ebook_store_price_plus_vat($ebook['ebook_price']) ) . '">' . esc_html( $add_to_cart_label ) . '</a>'; |
| 4144 |
} |
| 4145 |
if (!is_array($cover)) { |
| 4146 |
$cover = array(); |
| 4147 |
} |
| 4148 |
//$extraButtons[] = array(); |
| 4149 |
|
| 4150 |
// if (!is_array(@$extraButtons)) { |
| 4151 |
// @$extraButtons = array(); |
| 4152 |
// } |
| 4153 |
if (!is_array($side)) { |
| 4154 |
$side = array(); |
| 4155 |
} |
| 4156 |
@$extraButtons = apply_filters('ebook_store_extra_buttons',$extraButtons, $ebook['ebook_price'], $bookpost->post_title, $cover['url'], $md5_nonce, $post_id); |
| 4157 |
|
| 4158 |
// Add Stripe button if enabled |
| 4159 |
if (get_option('stripe_integration_enabled', 0)) { |
| 4160 |
$button_text = ebook_store_get_gateway_button_text( 'stripe' ); |
| 4161 |
|
| 4162 |
// Only show Stripe button if price is greater than 0 |
| 4163 |
|
| 4164 |
if ($ebook['ebook_price'] > 0) { |
| 4165 |
try { |
| 4166 |
// Generate ebook_key here, using same method as PayPal |
| 4167 |
$stripe_md5_nonce = wp_create_nonce('ebook_download_' . $post_id); |
| 4168 |
$stripe_ebook_key = $ebook_key; |
| 4169 |
|
| 4170 |
// Create Stripe checkout session with the pre-generated key |
| 4171 |
$stripe_session_url = ebook_store_create_stripe_checkout( |
| 4172 |
get_the_ID(), |
| 4173 |
ebook_store_price_plus_vat($ebook['ebook_price']), |
| 4174 |
$bookpost->post_title, |
| 4175 |
$ebook_key // Pass the pre-generated key |
| 4176 |
); |
| 4177 |
|
| 4178 |
// Replace %%price%% token with the formatted price |
| 4179 |
$c = new Currencies; |
| 4180 |
$paypal_currency = ebook_store_get_store_currency(); |
| 4181 |
$formatted_price = $c->getSymbol($paypal_currency) . number_format( ebook_store_price_plus_vat($ebook['ebook_price']), 2 ); |
| 4182 |
$display_text = str_replace('%%price%%', $formatted_price, $button_text); |
| 4183 |
|
| 4184 |
// Add Stripe button to extra buttons using the processed button text |
| 4185 |
$extraButtons[] = sprintf( |
| 4186 |
'<a href="%s" class="stripe-button" data-ebook-store-payment-method="stripe" data-ebook-store-add-payment-info="1" data-ebook-store-begin-checkout="1" data-ebook-store-item-id="%s" data-ebook-store-item-name="%s" data-ebook-store-currency="%s" data-ebook-store-value="%s">%s</a>', |
| 4187 |
esc_url($stripe_session_url), |
| 4188 |
esc_attr( get_the_ID() ), |
| 4189 |
esc_attr( $bookpost->post_title ), |
| 4190 |
esc_attr( $paypal_currency ), |
| 4191 |
esc_attr( (string) ebook_store_price_plus_vat($ebook['ebook_price']) ), |
| 4192 |
esc_html($display_text) |
| 4193 |
); |
| 4194 |
$payment_methods['stripe'] = array( |
| 4195 |
'key' => 'stripe', |
| 4196 |
'label' => 'Stripe', |
| 4197 |
'href' => $stripe_session_url, |
| 4198 |
'onclick' => '', |
| 4199 |
'price' => $formatted_price, |
| 4200 |
); |
| 4201 |
} catch (Exception $e) { |
| 4202 |
error_log('Stripe button creation failed: ' . $e->getMessage()); |
| 4203 |
} |
| 4204 |
} |
| 4205 |
} |
| 4206 |
|
| 4207 |
// Add Adyen button if enabled |
| 4208 |
if (get_option('adyen_integration_enabled', 0)) { |
| 4209 |
$button_text = ebook_store_get_gateway_button_text( 'adyen' ); |
| 4210 |
|
| 4211 |
if ($ebook['ebook_price'] > 0) { |
| 4212 |
try { |
| 4213 |
$adyen_checkout_url = ebook_store_create_adyen_payment_link( |
| 4214 |
get_the_ID(), |
| 4215 |
ebook_store_price_plus_vat($ebook['ebook_price']), |
| 4216 |
$bookpost->post_title, |
| 4217 |
$ebook_key |
| 4218 |
); |
| 4219 |
|
| 4220 |
if ( $adyen_checkout_url ) { |
| 4221 |
$c = new Currencies; |
| 4222 |
$adyen_currency = ebook_store_get_store_currency(); |
| 4223 |
$adyen_amount = (float) ebook_store_price_plus_vat($ebook['ebook_price']); |
| 4224 |
$formatted_price = $c->getSymbol($adyen_currency) . number_format( $adyen_amount, 2 ); |
| 4225 |
$display_text = str_replace('%%price%%', $formatted_price, $button_text); |
| 4226 |
|
| 4227 |
$extraButtons[] = sprintf( |
| 4228 |
'<a href="%s" class="stripe-button adyen-button" data-ebook-store-payment-method="adyen" data-ebook-store-add-payment-info="1" data-ebook-store-begin-checkout="1" data-ebook-store-item-id="%s" data-ebook-store-item-name="%s" data-ebook-store-currency="%s" data-ebook-store-value="%s">%s</a>', |
| 4229 |
esc_url($adyen_checkout_url), |
| 4230 |
esc_attr( get_the_ID() ), |
| 4231 |
esc_attr( $bookpost->post_title ), |
| 4232 |
esc_attr( $adyen_currency ), |
| 4233 |
esc_attr( (string) $adyen_amount ), |
| 4234 |
esc_html($display_text) |
| 4235 |
); |
| 4236 |
$payment_methods['adyen'] = array( |
| 4237 |
'key' => 'adyen', |
| 4238 |
'label' => 'Adyen', |
| 4239 |
'href' => $adyen_checkout_url, |
| 4240 |
'onclick' => '', |
| 4241 |
'price' => $formatted_price, |
| 4242 |
); |
| 4243 |
} |
| 4244 |
} catch (Exception $e) { |
| 4245 |
error_log('Adyen button creation failed: ' . $e->getMessage()); |
| 4246 |
} |
| 4247 |
} |
| 4248 |
} |
| 4249 |
|
| 4250 |
$extraButtons = is_array($extraButtons) ? $extraButtons : array(); |
| 4251 |
if ( $paypal_ready ) { |
| 4252 |
$form = '<form method="post" id="' . $md5rand . '" name="dmp_order_form" action="https://www' . (get_option('paypal_sandbox') != '' ? '.sandbox' : '') . '.paypal.com/cgi-bin/webscr"> |
| 4253 |
<input type="hidden" name="rm" value="0"> |
| 4254 |
<input type="hidden" name="discount_rate" value="0"> |
| 4255 |
<input type="hidden" name="cmd" value="_xclick"> |
| 4256 |
<input type="hidden" name="charset" value="utf-8"> |
| 4257 |
<input type="hidden" name="md5_nonce" value="' . $md5_nonce . '"> |
| 4258 |
<input type="hidden" name="lc" value="' . get_option('paypal_language') . '"> |
| 4259 |
<input type="hidden" name="no_shipping" value="' . get_option('ebook_store_require_shipping') . '"> |
| 4260 |
<input type="hidden" name="button_subtype" value="products"> |
| 4261 |
<input type="hidden" name="return" value="' . add_query_arg(array('ebook_key' => $ebook_key, 'action' => 'thank_you'),get_permalink($checkoutPage)) . '"> |
| 4262 |
<input type="hidden" name="cancel_return" value="' . (get_option('paypal_sandbox') == 1 ? add_query_arg(array('ebook_key' => $ebook_key, 'action' => 'thank_you'),get_permalink($checkoutPage)) : add_query_arg(array('ebook_key' => $ebook_key, 'action' => 'cancel'),get_permalink(get_option('ebook_store_cancel_page')))) . '"> |
| 4263 |
<input type="hidden" name="notify_url" value="' . add_query_arg(array('task' => 'ipn','ebook_key' => $ebook_key, 'md5_nonce' => $md5_nonce), home_url('/')) . '"> |
| 4264 |
<input type="hidden" name="item_name" value="' . $bookpost->post_title . '"> |
| 4265 |
<input type="hidden" name="item_number" value="1"> |
| 4266 |
<input type="hidden" name="tax_rate" value="' . ebook_store_get_vat_percent() . '"> |
| 4267 |
<input type="hidden" name="amount" value="' . $ebook['ebook_price'] . '"> |
| 4268 |
<input type="hidden" name="upload" value="1"> |
| 4269 |
<input type="hidden" name="custom" value="' . $custom .'"> |
| 4270 |
<input type="hidden" name="business" id="af69ae50c1be74757508c8f7fae10abd" value="' . get_option('paypal_account') . '"> |
| 4271 |
<input type="hidden" name="receiver_email" id="af69ae50c1be74757508c8f7fae10abd0xff" value="' . get_option('paypal_account') . '"> |
| 4272 |
<input type="hidden" name="currency_code" value="' . $paypal_currency . '"> |
| 4273 |
<input type="hidden" name="cbt" value="' . get_option('paypal_return_button_text') . '"> |
| 4274 |
<input type="hidden" name="no_note" value="1"> |
| 4275 |
</form>'; |
| 4276 |
$buyNowLinkHref = '#'; |
| 4277 |
} |
| 4278 |
|
| 4279 |
//$form .= var_export($bookpost, true); |
| 4280 |
if (@$_GET['task'] == 'direct_order') { |
| 4281 |
if ($ebook['ebook_price'] > 0 && $paypal_ready) { |
| 4282 |
die($form . |
| 4283 |
'<script> |
| 4284 |
document.getElementById("' . $md5rand . '").submit(); |
| 4285 |
</script>' |
| 4286 |
); |
| 4287 |
} else { |
| 4288 |
die($form . |
| 4289 |
'<script> |
| 4290 |
'.$buyNowLinkOnClick.' |
| 4291 |
jQuery("body").html(""); |
| 4292 |
</script>' |
| 4293 |
); |
| 4294 |
} |
| 4295 |
} |
| 4296 |
$date_text = (!empty($ebook['ebook_date']) && strtotime($ebook['ebook_date'])) ? date(get_option('date_format'), strtotime($ebook['ebook_date'])) : ''; |
| 4297 |
$pages_text = (!empty($ebook['ebook_pages']) && (int) $ebook['ebook_pages'] > 0) ? $ebook['ebook_pages'] . ' ' . $locale['pages'] : ''; |
| 4298 |
$preview_label = ! empty( $locale['preview'] ) ? $locale['preview'] : __( 'Preview', 'ebook-store' ); |
| 4299 |
$file_facts = ebook_store_get_ebook_file_facts( get_the_ID() ); |
| 4300 |
$template_items[] = array( |
| 4301 |
'ebook_id' => get_the_ID(), |
| 4302 |
'title' => $bookpost->post_title, |
| 4303 |
'content_html' => wpautop( do_shortcode( shortcode_unautop( get_the_content() ) ) ), |
| 4304 |
'content_raw' => get_the_content(), |
| 4305 |
'authors' => $authors, |
| 4306 |
'publishers' => $publishers, |
| 4307 |
'date_text' => $date_text, |
| 4308 |
'pages_text' => $pages_text, |
| 4309 |
'file_facts' => $file_facts, |
| 4310 |
'cover_url' => isset( $cover['url'] ) ? $cover['url'] : '', |
| 4311 |
'side_url' => isset( $side['url'] ) ? $side['url'] : '', |
| 4312 |
'preview_url' => isset( $preview['url'] ) ? $preview['url'] : '', |
| 4313 |
'add_to_cart_html' => isset( $addToCartButton ) ? $addToCartButton : '', |
| 4314 |
'extra_buttons_html' => implode( '', $extraButtons ), |
| 4315 |
'extra_buttons' => $extraButtons, |
| 4316 |
'payment_methods' => array_values( $payment_methods ), |
| 4317 |
'buy_link_text' => $buyNowLinkText, |
| 4318 |
'buy_link_onclick' => $buyNowLinkOnClick, |
| 4319 |
'buy_link_href' => $buyNowLinkHref, |
| 4320 |
'primary_payment_method' => $primary_payment_method, |
| 4321 |
'show_primary_buy_link' => $show_primary_buy_link ? 1 : 0, |
| 4322 |
'form_html' => $form, |
| 4323 |
'md5_nonce' => $md5_nonce, |
| 4324 |
'locale' => $locale, |
| 4325 |
'preview_label' => $preview_label, |
| 4326 |
'ebook' => $ebook, |
| 4327 |
'paypal_currency' => $paypal_currency, |
| 4328 |
'currency_symbol' => $c->getSymbol($paypal_currency), |
| 4329 |
'price_display' => $c->getSymbol($paypal_currency) . @number_format(ebook_store_price_plus_vat($ebook['ebook_price']),2), |
| 4330 |
'disable_cover_buy_now' => (int) get_option('ebook_store_disable_cover_buy_now',0), |
| 4331 |
'hide_buy_now' => (int) get_option('ebook_store_hide_buy_now',0), |
| 4332 |
'buy_link_font_size' => get_option('ebook_store_buy_link_font_size', 0.65), |
| 4333 |
'title_font_size' => get_option('ebook_store_title_font_size', 1.8), |
| 4334 |
'form_scale' => get_option('form_scale', 1), |
| 4335 |
); |
| 4336 |
endwhile; |
| 4337 |
wp_reset_postdata(); |
| 4338 |
if ( $template === '' ) { |
| 4339 |
$template = ebook_store_resolve_form_template( '' ); |
| 4340 |
} |
| 4341 |
|
| 4342 |
if ($buyNowOnly == true) { |
| 4343 |
if ( ! $show_primary_buy_link ) { |
| 4344 |
return ''; |
| 4345 |
} |
| 4346 |
$ga_attributes = ''; |
| 4347 |
if ( ebook_store_ga4_is_enabled() && $primary_payment_method !== '' ) { |
| 4348 |
$ga_attributes = ' data-ebook-store-payment-method="' . esc_attr( $primary_payment_method ) . '" data-ebook-store-add-payment-info="1" data-ebook-store-begin-checkout="1" data-ebook-store-item-id="' . esc_attr( $post_id ) . '" data-ebook-store-item-name="' . esc_attr( get_the_title( $post_id ) ) . '" data-ebook-store-currency="' . esc_attr( $paypal_currency ) . '" data-ebook-store-value="' . esc_attr( (string) ebook_store_price_plus_vat($ebook['ebook_price']) ) . '"'; |
| 4349 |
} |
| 4350 |
return $form . '<button title="' . esc_attr( $buyNowLinkText ) . '" aria-label="' . esc_attr( $buyNowLinkText ) . '"' . $ga_attributes . ' onClick="' . $buyNowLinkOnClick . '">' . esc_html( $buyNowLinkText ) . '</button>'; |
| 4351 |
} |
| 4352 |
if ( $template === 'legacy' ) { |
| 4353 |
wp_enqueue_style( 'ebookstore-bookblock', plugins_url('css/bookblock.css',__FILE__) ); |
| 4354 |
wp_enqueue_style( 'ebookstore-component', plugins_url('css/component.css',__FILE__) ); |
| 4355 |
wp_enqueue_script( 'ebookstore-modernizr', plugins_url('js/modernizr.custom.js',__FILE__), array(), null, false ); |
| 4356 |
wp_enqueue_script( 'ebookstore-bookblock', plugins_url('js/bookblock.min.js',__FILE__), array(), null, true ); |
| 4357 |
wp_enqueue_script( 'ebookstore-classie', plugins_url('js/classie.js',__FILE__), array(), null, true ); |
| 4358 |
wp_enqueue_script( 'ebookstore-bookshelf', plugins_url('js/bookshelf.js',__FILE__), array('ebookstore-bookblock','ebookstore-classie'), null, true ); |
| 4359 |
} |
| 4360 |
|
| 4361 |
$context = array( |
| 4362 |
'items' => $template_items, |
| 4363 |
'template' => $template, |
| 4364 |
'plugin_url' => plugins_url('', __FILE__), |
| 4365 |
'locale' => $locale, |
| 4366 |
); |
| 4367 |
|
| 4368 |
return ebook_store_render_form_template( $template, $context ) . ebook_store_ga4_render_view_item_tracking( $template_items ); |
| 4369 |
} catch ( Throwable $e ) { |
| 4370 |
error_log( |
| 4371 |
sprintf( |
| 4372 |
'Ebook Store render error for ebook %d: %s in %s:%d', |
| 4373 |
(int) $atts['ebook_id'], |
| 4374 |
$e->getMessage(), |
| 4375 |
$e->getFile(), |
| 4376 |
(int) $e->getLine() |
| 4377 |
) |
| 4378 |
); |
| 4379 |
|
| 4380 |
return '<div class="ebook-store-alert ebook-store-alert-error">' . esc_html__( 'The ebook order form could not be loaded.', 'ebook-store' ) . '</div>'; |
| 4381 |
} |
| 4382 |
} |
| 4383 |
function custom_enter_title_author( $input ) { |
| 4384 |
global $post_type; |
| 4385 |
|
| 4386 |
if ( is_admin() && 'ebook_author' == $post_type ) |
| 4387 |
return __( 'Enter Author Name' ); |
| 4388 |
|
| 4389 |
return $input; |
| 4390 |
} |
| 4391 |
|
| 4392 |
function custom_enter_title_publisher( $input ) { |
| 4393 |
global $post_type; |
| 4394 |
|
| 4395 |
if ( is_admin() && 'ebook_publisher' == $post_type ) |
| 4396 |
return __( 'Enter Publisher Name' ); |
| 4397 |
|
| 4398 |
return $input; |
| 4399 |
} |
| 4400 |
|
| 4401 |
class Currencies { |
| 4402 |
|
| 4403 |
public $currencies = array( |
| 4404 |
|
| 4405 |
'AUD' => array('name' => "Australian Dollar", 'symbol' => "A$", 'ASCII' => "A$"), |
| 4406 |
|
| 4407 |
'CAD' => array('name' => "Canadian Dollar", 'symbol' => "$", 'ASCII' => "$"), |
| 4408 |
|
| 4409 |
'CZK' => array('name' => "Czech Koruna", 'symbol' => "kr", 'ASCII' => "kr"), |
| 4410 |
|
| 4411 |
'DKK' => array('name' => "Danish Krone", 'symbol' => "Kr", 'ASCII' => ""), |
| 4412 |
|
| 4413 |
'EUR' => array('name' => "Euro", 'symbol' => "€", 'ASCII' => "€"), |
| 4414 |
|
| 4415 |
'HKD' => array('name' => "Hong Kong Dollar", 'symbol' => "$", 'ASCII' => "$"), |
| 4416 |
|
| 4417 |
'HUF' => array('name' => "Hungarian Forint", 'symbol' => "Ft", 'ASCII' => "Ft"), |
| 4418 |
|
| 4419 |
'ILS' => array('name' => "Israeli New Sheqel", 'symbol' => "₪", 'ASCII' => "₪"), |
| 4420 |
|
| 4421 |
'INR' => array('name' => "Indian Rupee", 'symbol' => "₹", 'ASCII' => "₹"), |
| 4422 |
|
| 4423 |
'JPY' => array('name' => "Japanese Yen", 'symbol' => "¥", 'ASCII' => "¥"), |
| 4424 |
|
| 4425 |
'MXN' => array('name' => "Mexican Peso", 'symbol' => "$", 'ASCII' => "$"), |
| 4426 |
|
| 4427 |
'NOK' => array('name' => "Norwegian Krone", 'symbol' => "Kr", 'ASCII' => ""), |
| 4428 |
|
| 4429 |
'NZD' => array('name' => "New Zealand Dollar", 'symbol' => "$", 'ASCII' => "$"), |
| 4430 |
|
| 4431 |
'PHP' => array('name' => "Philippine Peso", 'symbol' => "₱", 'ASCII' => ""), |
| 4432 |
|
| 4433 |
'PLN' => array('name' => "Polish Zloty", 'symbol' => "zł‚", 'ASCII' => "zł"), |
| 4434 |
|
| 4435 |
'GBP' => array('name' => "Pound Sterling", 'symbol' => "£", 'ASCII' => "£"), |
| 4436 |
|
| 4437 |
'SGD' => array('name' => "Singapore Dollar", 'symbol' => "$", 'ASCII' => "$"), |
| 4438 |
|
| 4439 |
'SEK' => array('name' => "Swedish Krona", 'symbol' => "kr", 'ASCII' => ""), |
| 4440 |
|
| 4441 |
'CHF' => array('name' => "Swiss Franc", 'symbol' => "CHF", 'ASCII' => "Fr"), |
| 4442 |
|
| 4443 |
'TWD' => array('name' => "Taiwan New Dollar", 'symbol' => "NT$", 'ASCII' => "NT$"), |
| 4444 |
|
| 4445 |
'THB' => array('name' => "Thai Baht", 'symbol' => "฿", 'ASCII' => "฿"), |
| 4446 |
|
| 4447 |
'USD' => array('name' => "U.S. Dollar", 'symbol' => "$", 'ASCII' => "$"), |
| 4448 |
|
| 4449 |
'BRL' => array('name' => "Brazilian Real", 'symbol' => "R$", 'ASCII' => "$"), |
| 4450 |
|
| 4451 |
'TRY' => array('name' => "Turkish Lira", 'symbol' => "TL", 'ASCII' => "TL"), |
| 4452 |
|
| 4453 |
'MYR' => array('name' => "Malaysian Ringgit", 'symbol' => "RM", 'ASCII' => "RM"), |
| 4454 |
|
| 4455 |
'NGN' => array('name' => "Nigerian naira", 'symbol' => "₦", 'ASCII' => "₦"), |
| 4456 |
|
| 4457 |
'RON' => array('name' => "Romanian Leu", 'symbol' => "Lei", 'ASCII' => "Lei"), |
| 4458 |
|
| 4459 |
'ZAR' => array('name' => "South African Rands", 'symbol' => "R", 'ASCII' => "R"), |
| 4460 |
); |
| 4461 |
|
| 4462 |
public function getSymbol($code = 'USD') { |
| 4463 |
|
| 4464 |
if (!empty($this->currencies[$code]['ASCII'])) { |
| 4465 |
|
| 4466 |
return (string) $this->currencies[$code]['ASCII']; |
| 4467 |
|
| 4468 |
} |
| 4469 |
|
| 4470 |
return (string) @$this->currencies[$code]['symbol']; |
| 4471 |
|
| 4472 |
} |
| 4473 |
|
| 4474 |
} |
| 4475 |
|
| 4476 |
function order_columns($columns) |
| 4477 |
{ |
| 4478 |
$columns = array( |
| 4479 |
'cb' => '<input type="checkbox" />', |
| 4480 |
'order_id' => __('Order', 'ebook-store'), |
| 4481 |
'title' => __('Buyer', 'ebook-store'), |
| 4482 |
'product' => __('Product', 'ebook-store'), |
| 4483 |
'amount' => __('Amount', 'ebook-store'), |
| 4484 |
'paypal' => __('Payment', 'ebook-store'), |
| 4485 |
'downloadlink' => __('Delivery', 'ebook-store'), |
| 4486 |
'formData' => __('Form Data', 'ebook-store'), |
| 4487 |
'date' => __('Date', 'ebook-store'), |
| 4488 |
); |
| 4489 |
return $columns; |
| 4490 |
} |
| 4491 |
function order_custom_columns($column) |
| 4492 |
{ |
| 4493 |
global $post; |
| 4494 |
$c = new Currencies(); |
| 4495 |
switch ($column) { |
| 4496 |
case 'order_id': |
| 4497 |
$payment_status = get_post_meta($post->ID,'payment_status',true); |
| 4498 |
$downloads = intval(get_post_meta($post->ID,'downloads',true)); |
| 4499 |
echo '<div class="ebook-order-list">'; |
| 4500 |
echo '<div class="ebook-order-list__title">#' . esc_html($post->ID) . '</div>'; |
| 4501 |
echo '<div class="ebook-order-list__badges">'; |
| 4502 |
echo ebook_store_render_order_badge( __('Status', 'ebook-store'), $payment_status ? $payment_status : __('Unknown', 'ebook-store'), ebook_store_order_status_class($payment_status) ); |
| 4503 |
echo ebook_store_render_order_badge( __('Downloads', 'ebook-store'), number_format_i18n($downloads) ); |
| 4504 |
echo '</div>'; |
| 4505 |
$txn_id = get_post_meta($post->ID,'txn_id',true); |
| 4506 |
if ($txn_id) { |
| 4507 |
echo '<span class="ebook-order-list__inline-code">' . esc_html($txn_id) . '</span>'; |
| 4508 |
} |
| 4509 |
echo '</div>'; |
| 4510 |
break; |
| 4511 |
case "title": |
| 4512 |
$first_name = trim((string)get_post_meta($post->ID,'first_name',true)); |
| 4513 |
$last_name = trim((string)get_post_meta($post->ID,'last_name',true)); |
| 4514 |
$buyer_name = trim($first_name . ' ' . $last_name); |
| 4515 |
$mail = trim((string)get_post_meta($post->ID,'payer_email',true)); |
| 4516 |
$country = trim((string)get_post_meta($post->ID,'residence_country',true)); |
| 4517 |
echo '<div class="ebook-order-list">'; |
| 4518 |
echo '<div class="ebook-order-list__title">' . esc_html($buyer_name !== '' ? $buyer_name : __('Unnamed buyer', 'ebook-store')) . '</div>'; |
| 4519 |
if ($mail !== '') { |
| 4520 |
echo '<div class="ebook-order-list__meta"><a href="mailto:' . esc_attr($mail) . '">' . esc_html($mail) . '</a></div>'; |
| 4521 |
} |
| 4522 |
echo '<div class="ebook-order-list__badges">'; |
| 4523 |
if ($country !== '') { |
| 4524 |
echo ebook_store_render_order_badge( __('Country', 'ebook-store'), $country ); |
| 4525 |
} |
| 4526 |
$payer_status = get_post_meta($post->ID,'payer_status',true); |
| 4527 |
if ($payer_status !== '') { |
| 4528 |
echo ebook_store_render_order_badge( __('Payer', 'ebook-store'), $payer_status, ebook_store_order_status_class($payer_status) ); |
| 4529 |
} |
| 4530 |
echo '</div>'; |
| 4531 |
echo '</div>'; |
| 4532 |
break; |
| 4533 |
case "product": |
| 4534 |
$ebook_id = intval(get_post_meta($post->ID,'ebook',true)); |
| 4535 |
$product_title = $ebook_id ? get_the_title($ebook_id) : ''; |
| 4536 |
$item_number = get_post_meta($post->ID,'item_number',true); |
| 4537 |
$quantity = get_post_meta($post->ID,'quantity',true); |
| 4538 |
echo '<div class="ebook-order-list">'; |
| 4539 |
if ($ebook_id && $product_title) { |
| 4540 |
echo '<div class="ebook-order-list__title"><a href="post.php?post=' . esc_attr($ebook_id) . '&action=edit">' . esc_html($product_title) . '</a></div>'; |
| 4541 |
} else { |
| 4542 |
echo '<div class="ebook-order-list__title">' . esc_html(get_post_meta($post->ID,'item_name',true)) . '</div>'; |
| 4543 |
} |
| 4544 |
echo '<div class="ebook-order-list__badges">'; |
| 4545 |
if ($quantity !== '') { |
| 4546 |
echo ebook_store_render_order_badge( __('Qty', 'ebook-store'), $quantity ); |
| 4547 |
} |
| 4548 |
if ($item_number !== '') { |
| 4549 |
echo ebook_store_render_order_badge( __('SKU', 'ebook-store'), $item_number ); |
| 4550 |
} |
| 4551 |
echo '</div>'; |
| 4552 |
echo '</div>'; |
| 4553 |
break; |
| 4554 |
case "amount": |
| 4555 |
$mc_currency = get_post_meta($post->ID,'mc_currency',true); |
| 4556 |
$mc_gross = get_post_meta($post->ID,'mc_gross',true); |
| 4557 |
$mc_fee = get_post_meta($post->ID,'mc_fee',true); |
| 4558 |
$total = floatval($mc_gross) - floatval($mc_fee); |
| 4559 |
echo '<div class="ebook-order-list">'; |
| 4560 |
echo '<div class="ebook-order-list__title">' . esc_html( ebook_store_order_money_display( $mc_gross, $mc_currency ) ) . '</div>'; |
| 4561 |
echo '<div class="ebook-order-list__stack">'; |
| 4562 |
echo '<span>' . esc_html__( 'Fee:', 'ebook-store' ) . ' ' . esc_html( ebook_store_order_money_display( $mc_fee, $mc_currency ) ) . '</span>'; |
| 4563 |
echo '<span>' . esc_html__( 'Net:', 'ebook-store' ) . ' ' . esc_html( ebook_store_order_money_display( $total, $mc_currency ) ) . '</span>'; |
| 4564 |
echo '</div>'; |
| 4565 |
echo '</div>'; |
| 4566 |
break; |
| 4567 |
case "paypal": |
| 4568 |
$mail = esc_attr(get_post_meta($post->ID,'payer_email',true)); |
| 4569 |
$payment_type = get_post_meta($post->ID,'payment_type',true); |
| 4570 |
$payment_status = get_post_meta($post->ID,'payment_status',true); |
| 4571 |
$refund_gateway = ebook_store_get_order_refund_gateway( $post->ID ); |
| 4572 |
echo '<div class="ebook-order-list">'; |
| 4573 |
echo '<div class="ebook-order-list__title">' . esc_html( $payment_type !== '' ? $payment_type : __('PayPal', 'ebook-store') ) . '</div>'; |
| 4574 |
echo '<div class="ebook-order-list__badges">'; |
| 4575 |
if ( $payment_status !== '' ) { |
| 4576 |
echo ebook_store_render_order_badge( __('Status', 'ebook-store'), $payment_status, ebook_store_order_status_class($payment_status) ); |
| 4577 |
} |
| 4578 |
$payer_status = get_post_meta($post->ID,'payer_status',true); |
| 4579 |
if ( $payer_status !== '' ) { |
| 4580 |
echo ebook_store_render_order_badge( __('Payer', 'ebook-store'), $payer_status ); |
| 4581 |
} |
| 4582 |
echo '</div>'; |
| 4583 |
if ($mail !== '') { |
| 4584 |
echo '<div class="ebook-order-list__meta"><a href="mailto:' . esc_attr($mail) . '">' . esc_html($mail) . '</a></div>'; |
| 4585 |
} |
| 4586 |
if ( $refund_gateway !== '' ) { |
| 4587 |
echo '<div class="ebook-order-list__actions">'; |
| 4588 |
echo '<a href="' . esc_url( ebook_store_get_order_refund_url( $post->ID ) ) . '" class="button button-small" onclick="return window.confirm(\'' . esc_js( __( 'Are you sure you want to refund this order?', 'ebook-store' ) ) . '\');">' . esc_html__( 'Refund', 'ebook-store' ) . '</a>'; |
| 4589 |
echo '</div>'; |
| 4590 |
} |
| 4591 |
echo '</div>'; |
| 4592 |
break; |
| 4593 |
case 'downloadlink': |
| 4594 |
$downloadlink = ebook_download_link( |
| 4595 |
array( |
| 4596 |
'ebook_key' => array( get_post_meta( $post->ID, 'ebook_key', true ) ), |
| 4597 |
'md5_nonce' => array( get_post_meta( $post->ID, 'md5_nonce', true ) ), |
| 4598 |
'ebook' => array( get_post_meta( $post->ID, 'ebook', true ) ), |
| 4599 |
'order_id' => array( $post->ID ), |
| 4600 |
) |
| 4601 |
); |
| 4602 |
$checkoutPage = esc_attr(get_option('ebook_store_checkout_page')); |
| 4603 |
$link = add_query_arg(array('ebook_key' => get_post_meta($post->ID,'ebook_key',true), 'action' => 'thank_you'),get_permalink($checkoutPage)); |
| 4604 |
$password = get_post_meta($post->ID,'password',true); |
| 4605 |
echo '<div class="ebook-order-list">'; |
| 4606 |
echo '<div class="ebook-order-list__actions">'; |
| 4607 |
if ($downloadlink != '') { |
| 4608 |
echo '<a href="' . esc_url($downloadlink) . '" class="button button-small" target="_blank" rel="noopener">' . esc_html__('Download', 'ebook-store') . '</a>'; |
| 4609 |
} else { |
| 4610 |
echo '<span class="ebook-order-list__empty">' . esc_html__('No direct link saved', 'ebook-store') . '</span>'; |
| 4611 |
} |
| 4612 |
echo '<a href="' . esc_url( ebook_store_get_order_reset_downloads_url( $post->ID ) ) . '" class="button button-small">' . esc_html__( 'Reset downloads', 'ebook-store' ) . '</a>'; |
| 4613 |
echo '<a href="' . esc_url( ebook_store_get_order_resend_delivery_url( $post->ID ) ) . '" class="button button-small">' . esc_html__( 'Re-send email', 'ebook-store' ) . '</a>'; |
| 4614 |
echo '<a href="' . esc_url($link) . '" class="button button-small" target="_blank" rel="noopener">' . esc_html__('Thank you page', 'ebook-store') . '</a>'; |
| 4615 |
echo '</div>'; |
| 4616 |
echo '<div class="ebook-order-list__badges">'; |
| 4617 |
$downloads = get_post_meta($post->ID,'downloads',true); |
| 4618 |
echo ebook_store_render_order_badge( __('Downloads', 'ebook-store'), $downloads !== '' ? $downloads : '0' ); |
| 4619 |
if ( $password !== '' ) { |
| 4620 |
echo ebook_store_render_order_badge( __('Password', 'ebook-store'), $password ); |
| 4621 |
} |
| 4622 |
echo '</div>'; |
| 4623 |
echo '</div>'; |
| 4624 |
break; |
| 4625 |
case 'formData': |
| 4626 |
$md5_nonce = get_post_meta($post->ID,'md5_nonce',true); |
| 4627 |
$data = get_post_meta($post->ID,'formData',true); |
| 4628 |
$data = json_decode($data, true); |
| 4629 |
if (is_array($data) && !empty($data)) { |
| 4630 |
unset($data['md5_nonce']); |
| 4631 |
echo '<div class="ebook-order-list ebook-order-list__pairs">'; |
| 4632 |
$count = 0; |
| 4633 |
foreach ($data as $key => $value) { |
| 4634 |
if ($count >= 5) { |
| 4635 |
break; |
| 4636 |
} |
| 4637 |
echo '<div class="ebook-order-list__pair"><strong>' . esc_html(ebook_store_prepare_order_display_value($key)) . '</strong><span>' . esc_html(ebook_store_prepare_order_display_value($value, __('Saved value', 'ebook-store'))) . '</span></div>'; |
| 4638 |
$count++; |
| 4639 |
} |
| 4640 |
if ($count === 0) { |
| 4641 |
echo '<span class="ebook-order-list__empty">' . esc_html__('No form fields saved', 'ebook-store') . '</span>'; |
| 4642 |
} |
| 4643 |
echo '</div>'; |
| 4644 |
} else { |
| 4645 |
echo '<span class="ebook-order-list__empty">' . esc_html__('No extra form data', 'ebook-store') . '</span>'; |
| 4646 |
} |
| 4647 |
|
| 4648 |
break; |
| 4649 |
} |
| 4650 |
} |
| 4651 |
function ebook_readfile_chunked( $file, $retbytes = TRUE ) { |
| 4652 |
$chunksize = 1 * (1024 * 1024); |
| 4653 |
$buffer = ''; |
| 4654 |
$cnt = 0; |
| 4655 |
$handle = fopen( $file, 'r' ); |
| 4656 |
header('Cache-Control: no-store'); |
| 4657 |
|
| 4658 |
if( $size = @filesize( $file ) ) header("Content-Length: " . $size ); |
| 4659 |
return readfile($file); |
| 4660 |
//if ob_clean / flush are executed on some hosts, the file is not properly downloaded. |
| 4661 |
|
| 4662 |
if( $size = @filesize( $file ) ) header("Content-Length: " . $size ); |
| 4663 |
//wp_die($file); |
| 4664 |
ob_clean(); |
| 4665 |
flush(); |
| 4666 |
return readfile($file); |
| 4667 |
|
| 4668 |
if ( $handle === FALSE ) return FALSE; |
| 4669 |
|
| 4670 |
while ( ! feof( $handle ) ) : |
| 4671 |
$buffer = fread( $handle, $chunksize ); |
| 4672 |
echo $buffer; |
| 4673 |
//ob_flush(); |
| 4674 |
//flush(); |
| 4675 |
|
| 4676 |
if ( $retbytes ) $cnt += strlen( $buffer ); |
| 4677 |
endwhile; |
| 4678 |
|
| 4679 |
$status = fclose( $handle ); |
| 4680 |
|
| 4681 |
if ( $retbytes AND $status ) return $cnt; |
| 4682 |
|
| 4683 |
return $status; |
| 4684 |
} |
| 4685 |
function ebook_get_file_ctype( $extension ) { |
| 4686 |
switch( $extension ): |
| 4687 |
case 'ac' : $ctype = "application/pkix-attr-cert"; break; |
| 4688 |
case 'adp' : $ctype = "audio/adpcm"; break; |
| 4689 |
case 'ai' : $ctype = "application/postscript"; break; |
| 4690 |
case 'aif' : $ctype = "audio/x-aiff"; break; |
| 4691 |
case 'aifc' : $ctype = "audio/x-aiff"; break; |
| 4692 |
case 'aiff' : $ctype = "audio/x-aiff"; break; |
| 4693 |
case 'air' : $ctype = "application/vnd.adobe.air-application-installer-package+zip"; break; |
| 4694 |
case 'apk' : $ctype = "application/vnd.android.package-archive"; break; |
| 4695 |
case 'asc' : $ctype = "application/pgp-signature"; break; |
| 4696 |
case 'atom' : $ctype = "application/atom+xml"; break; |
| 4697 |
case 'atomcat' : $ctype = "application/atomcat+xml"; break; |
| 4698 |
case 'atomsvc' : $ctype = "application/atomsvc+xml"; break; |
| 4699 |
case 'au' : $ctype = "audio/basic"; break; |
| 4700 |
case 'aw' : $ctype = "application/applixware"; break; |
| 4701 |
case 'avi' : $ctype = "video/x-msvideo"; break; |
| 4702 |
case 'bcpio' : $ctype = "application/x-bcpio"; break; |
| 4703 |
case 'bin' : $ctype = "application/octet-stream"; break; |
| 4704 |
case 'bmp' : $ctype = "image/bmp"; break; |
| 4705 |
case 'boz' : $ctype = "application/x-bzip2"; break; |
| 4706 |
case 'bpk' : $ctype = "application/octet-stream"; break; |
| 4707 |
case 'bz' : $ctype = "application/x-bzip"; break; |
| 4708 |
case 'bz2' : $ctype = "application/x-bzip2"; break; |
| 4709 |
case 'ccxml' : $ctype = "application/ccxml+xml"; break; |
| 4710 |
case 'cdmia' : $ctype = "application/cdmi-capability"; break; |
| 4711 |
case 'cdmic' : $ctype = "application/cdmi-container"; break; |
| 4712 |
case 'cdmid' : $ctype = "application/cdmi-domain"; break; |
| 4713 |
case 'cdmio' : $ctype = "application/cdmi-object"; break; |
| 4714 |
case 'cdmiq' : $ctype = "application/cdmi-queue"; break; |
| 4715 |
case 'cdf' : $ctype = "application/x-netcdf"; break; |
| 4716 |
case 'cer' : $ctype = "application/pkix-cert"; break; |
| 4717 |
case 'cgm' : $ctype = "image/cgm"; break; |
| 4718 |
case 'class' : $ctype = "application/octet-stream"; break; |
| 4719 |
case 'cpio' : $ctype = "application/x-cpio"; break; |
| 4720 |
case 'cpt' : $ctype = "application/mac-compactpro"; break; |
| 4721 |
case 'crl' : $ctype = "application/pkix-crl"; break; |
| 4722 |
case 'csh' : $ctype = "application/x-csh"; break; |
| 4723 |
case 'css' : $ctype = "text/css"; break; |
| 4724 |
case 'cu' : $ctype = "application/cu-seeme"; break; |
| 4725 |
case 'davmount' : $ctype = "application/davmount+xml"; break; |
| 4726 |
case 'dbk' : $ctype = "application/docbook+xml"; break; |
| 4727 |
case 'dcr' : $ctype = "application/x-director"; break; |
| 4728 |
case 'deploy' : $ctype = "application/octet-stream"; break; |
| 4729 |
case 'dif' : $ctype = "video/x-dv"; break; |
| 4730 |
case 'dir' : $ctype = "application/x-director"; break; |
| 4731 |
case 'dist' : $ctype = "application/octet-stream"; break; |
| 4732 |
case 'distz' : $ctype = "application/octet-stream"; break; |
| 4733 |
case 'djv' : $ctype = "image/vnd.djvu"; break; |
| 4734 |
case 'djvu' : $ctype = "image/vnd.djvu"; break; |
| 4735 |
case 'dll' : $ctype = "application/octet-stream"; break; |
| 4736 |
case 'dmg' : $ctype = "application/octet-stream"; break; |
| 4737 |
case 'dms' : $ctype = "application/octet-stream"; break; |
| 4738 |
case 'doc' : $ctype = "application/msword"; break; |
| 4739 |
case 'docx' : $ctype = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; break; |
| 4740 |
case 'dotx' : $ctype = "application/vnd.openxmlformats-officedocument.wordprocessingml.template"; break; |
| 4741 |
case 'dssc' : $ctype = "application/dssc+der"; break; |
| 4742 |
case 'dtd' : $ctype = "application/xml-dtd"; break; |
| 4743 |
case 'dump' : $ctype = "application/octet-stream"; break; |
| 4744 |
case 'dv' : $ctype = "video/x-dv"; break; |
| 4745 |
case 'dvi' : $ctype = "application/x-dvi"; break; |
| 4746 |
case 'dxr' : $ctype = "application/x-director"; break; |
| 4747 |
case 'ecma' : $ctype = "application/ecmascript"; break; |
| 4748 |
case 'elc' : $ctype = "application/octet-stream"; break; |
| 4749 |
case 'emma' : $ctype = "application/emma+xml"; break; |
| 4750 |
case 'eps' : $ctype = "application/postscript"; break; |
| 4751 |
case 'epub' : $ctype = "application/epub+zip"; break; |
| 4752 |
case 'etx' : $ctype = "text/x-setext"; break; |
| 4753 |
case 'exe' : $ctype = "application/octet-stream"; break; |
| 4754 |
case 'exi' : $ctype = "application/exi"; break; |
| 4755 |
case 'ez' : $ctype = "application/andrew-inset"; break; |
| 4756 |
case 'f4v' : $ctype = "video/x-f4v"; break; |
| 4757 |
case 'fli' : $ctype = "video/x-fli"; break; |
| 4758 |
case 'flv' : $ctype = "video/x-flv"; break; |
| 4759 |
case 'gif' : $ctype = "image/gif"; break; |
| 4760 |
case 'gml' : $ctype = "application/srgs"; break; |
| 4761 |
case 'gpx' : $ctype = "application/gml+xml"; break; |
| 4762 |
case 'gram' : $ctype = "application/gpx+xml"; break; |
| 4763 |
case 'grxml' : $ctype = "application/srgs+xml"; break; |
| 4764 |
case 'gtar' : $ctype = "application/x-gtar"; break; |
| 4765 |
case 'gxf' : $ctype = "application/gxf"; break; |
| 4766 |
case 'hdf' : $ctype = "application/x-hdf"; break; |
| 4767 |
case 'hqx' : $ctype = "application/mac-binhex40"; break; |
| 4768 |
case 'htm' : $ctype = "text/html"; break; |
| 4769 |
case 'html' : $ctype = "text/html"; break; |
| 4770 |
case 'ice' : $ctype = "x-conference/x-cooltalk"; break; |
| 4771 |
case 'ico' : $ctype = "image/x-icon"; break; |
| 4772 |
case 'ics' : $ctype = "text/calendar"; break; |
| 4773 |
case 'ief' : $ctype = "image/ief"; break; |
| 4774 |
case 'ifb' : $ctype = "text/calendar"; break; |
| 4775 |
case 'iges' : $ctype = "model/iges"; break; |
| 4776 |
case 'igs' : $ctype = "model/iges"; break; |
| 4777 |
case 'ink' : $ctype = "application/inkml+xml"; break; |
| 4778 |
case 'inkml' : $ctype = "application/inkml+xml"; break; |
| 4779 |
case 'ipfix' : $ctype = "application/ipfix"; break; |
| 4780 |
case 'jar' : $ctype = "application/java-archive"; break; |
| 4781 |
case 'jnlp' : $ctype = "application/x-java-jnlp-file"; break; |
| 4782 |
case 'jp2' : $ctype = "image/jp2"; break; |
| 4783 |
case 'jpe' : $ctype = "image/jpeg"; break; |
| 4784 |
case 'jpeg' : $ctype = "image/jpeg"; break; |
| 4785 |
case 'jpg' : $ctype = "image/jpeg"; break; |
| 4786 |
case 'js' : $ctype = "application/javascript"; break; |
| 4787 |
case 'json' : $ctype = "application/json"; break; |
| 4788 |
case 'jsonml' : $ctype = "application/jsonml+json"; break; |
| 4789 |
case 'kar' : $ctype = "audio/midi"; break; |
| 4790 |
case 'latex' : $ctype = "application/x-latex"; break; |
| 4791 |
case 'lha' : $ctype = "application/octet-stream"; break; |
| 4792 |
case 'lrf' : $ctype = "application/octet-stream"; break; |
| 4793 |
case 'lzh' : $ctype = "application/octet-stream"; break; |
| 4794 |
case 'lostxml' : $ctype = "application/lost+xml"; break; |
| 4795 |
case 'm3u' : $ctype = "audio/x-mpegurl"; break; |
| 4796 |
case 'm4a' : $ctype = "audio/mp4a-latm"; break; |
| 4797 |
case 'm4b' : $ctype = "audio/mp4a-latm"; break; |
| 4798 |
case 'm4p' : $ctype = "audio/mp4a-latm"; break; |
| 4799 |
case 'm4u' : $ctype = "video/vnd.mpegurl"; break; |
| 4800 |
case 'm4v' : $ctype = "video/x-m4v"; break; |
| 4801 |
case 'm21' : $ctype = "application/mp21"; break; |
| 4802 |
case 'ma' : $ctype = "application/mathematica"; break; |
| 4803 |
case 'mac' : $ctype = "image/x-macpaint"; break; |
| 4804 |
case 'mads' : $ctype = "application/mads+xml"; break; |
| 4805 |
case 'man' : $ctype = "application/x-troff-man"; break; |
| 4806 |
case 'mar' : $ctype = "application/octet-stream"; break; |
| 4807 |
case 'mathml' : $ctype = "application/mathml+xml"; break; |
| 4808 |
case 'mbox' : $ctype = "application/mbox"; break; |
| 4809 |
case 'me' : $ctype = "application/x-troff-me"; break; |
| 4810 |
case 'mesh' : $ctype = "model/mesh"; break; |
| 4811 |
case 'metalink' : $ctype = "application/metalink+xml"; break; |
| 4812 |
case 'meta4' : $ctype = "application/metalink4+xml"; break; |
| 4813 |
case 'mets' : $ctype = "application/mets+xml"; break; |
| 4814 |
case 'mid' : $ctype = "audio/midi"; break; |
| 4815 |
case 'midi' : $ctype = "audio/midi"; break; |
| 4816 |
case 'mif' : $ctype = "application/vnd.mif"; break; |
| 4817 |
case 'mods' : $ctype = "application/mods+xml"; break; |
| 4818 |
case 'mov' : $ctype = "video/quicktime"; break; |
| 4819 |
case 'movie' : $ctype = "video/x-sgi-movie"; break; |
| 4820 |
case 'm1v' : $ctype = "video/mpeg"; break; |
| 4821 |
case 'm2v' : $ctype = "video/mpeg"; break; |
| 4822 |
case 'mp2' : $ctype = "audio/mpeg"; break; |
| 4823 |
case 'mp2a' : $ctype = "audio/mpeg"; break; |
| 4824 |
case 'mp21' : $ctype = "application/mp21"; break; |
| 4825 |
case 'mp3' : $ctype = "audio/mpeg"; break; |
| 4826 |
case 'mp3a' : $ctype = "audio/mpeg"; break; |
| 4827 |
case 'mp4' : $ctype = "video/mp4"; break; |
| 4828 |
case 'mp4s' : $ctype = "application/mp4"; break; |
| 4829 |
case 'mpe' : $ctype = "video/mpeg"; break; |
| 4830 |
case 'mpeg' : $ctype = "video/mpeg"; break; |
| 4831 |
case 'mpg' : $ctype = "video/mpeg"; break; |
| 4832 |
case 'mpg4' : $ctype = "video/mpeg"; break; |
| 4833 |
case 'mpga' : $ctype = "audio/mpeg"; break; |
| 4834 |
case 'mrc' : $ctype = "application/marc"; break; |
| 4835 |
case 'mrcx' : $ctype = "application/marcxml+xml"; break; |
| 4836 |
case 'ms' : $ctype = "application/x-troff-ms"; break; |
| 4837 |
case 'mscml' : $ctype = "application/mediaservercontrol+xml"; break; |
| 4838 |
case 'msh' : $ctype = "model/mesh"; break; |
| 4839 |
case 'mxf' : $ctype = "application/mxf"; break; |
| 4840 |
case 'mxu' : $ctype = "video/vnd.mpegurl"; break; |
| 4841 |
case 'nc' : $ctype = "application/x-netcdf"; break; |
| 4842 |
case 'oda' : $ctype = "application/oda"; break; |
| 4843 |
case 'oga' : $ctype = "application/ogg"; break; |
| 4844 |
case 'ogg' : $ctype = "application/ogg"; break; |
| 4845 |
case 'ogx' : $ctype = "application/ogg"; break; |
| 4846 |
case 'omdoc' : $ctype = "application/omdoc+xml"; break; |
| 4847 |
case 'onetoc' : $ctype = "application/onenote"; break; |
| 4848 |
case 'onetoc2' : $ctype = "application/onenote"; break; |
| 4849 |
case 'onetmp' : $ctype = "application/onenote"; break; |
| 4850 |
case 'onepkg' : $ctype = "application/onenote"; break; |
| 4851 |
case 'opf' : $ctype = "application/oebps-package+xml"; break; |
| 4852 |
case 'oxps' : $ctype = "application/oxps"; break; |
| 4853 |
case 'p7c' : $ctype = "application/pkcs7-mime"; break; |
| 4854 |
case 'p7m' : $ctype = "application/pkcs7-mime"; break; |
| 4855 |
case 'p7s' : $ctype = "application/pkcs7-signature"; break; |
| 4856 |
case 'p8' : $ctype = "application/pkcs8"; break; |
| 4857 |
case 'p10' : $ctype = "application/pkcs10"; break; |
| 4858 |
case 'pbm' : $ctype = "image/x-portable-bitmap"; break; |
| 4859 |
case 'pct' : $ctype = "image/pict"; break; |
| 4860 |
case 'pdb' : $ctype = "chemical/x-pdb"; break; |
| 4861 |
case 'pdf' : $ctype = "application/pdf"; break; |
| 4862 |
case 'pki' : $ctype = "application/pkixcmp"; break; |
| 4863 |
case 'pkipath' : $ctype = "application/pkix-pkipath"; break; |
| 4864 |
case 'pfr' : $ctype = "application/font-tdpfr"; break; |
| 4865 |
case 'pgm' : $ctype = "image/x-portable-graymap"; break; |
| 4866 |
case 'pgn' : $ctype = "application/x-chess-pgn"; break; |
| 4867 |
case 'pgp' : $ctype = "application/pgp-encrypted"; break; |
| 4868 |
case 'pic' : $ctype = "image/pict"; break; |
| 4869 |
case 'pict' : $ctype = "image/pict"; break; |
| 4870 |
case 'pkg' : $ctype = "application/octet-stream"; break; |
| 4871 |
case 'png' : $ctype = "image/png"; break; |
| 4872 |
case 'pnm' : $ctype = "image/x-portable-anymap"; break; |
| 4873 |
case 'pnt' : $ctype = "image/x-macpaint"; break; |
| 4874 |
case 'pntg' : $ctype = "image/x-macpaint"; break; |
| 4875 |
case 'pot' : $ctype = "application/vnd.ms-powerpoint"; break; |
| 4876 |
case 'potx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.template"; break; |
| 4877 |
case 'ppm' : $ctype = "image/x-portable-pixmap"; break; |
| 4878 |
case 'pps' : $ctype = "application/vnd.ms-powerpoint"; break; |
| 4879 |
case 'ppsx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.slideshow"; break; |
| 4880 |
case 'ppt' : $ctype = "application/vnd.ms-powerpoint"; break; |
| 4881 |
case 'pptx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; break; |
| 4882 |
case 'prf' : $ctype = "application/pics-rules"; break; |
| 4883 |
case 'ps' : $ctype = "application/postscript"; break; |
| 4884 |
case 'psd' : $ctype = "image/photoshop"; break; |
| 4885 |
case 'qt' : $ctype = "video/quicktime"; break; |
| 4886 |
case 'qti' : $ctype = "image/x-quicktime"; break; |
| 4887 |
case 'qtif' : $ctype = "image/x-quicktime"; break; |
| 4888 |
case 'ra' : $ctype = "audio/x-pn-realaudio"; break; |
| 4889 |
case 'ram' : $ctype = "audio/x-pn-realaudio"; break; |
| 4890 |
case 'ras' : $ctype = "image/x-cmu-raster"; break; |
| 4891 |
case 'rdf' : $ctype = "application/rdf+xml"; break; |
| 4892 |
case 'rgb' : $ctype = "image/x-rgb"; break; |
| 4893 |
case 'rm' : $ctype = "application/vnd.rn-realmedia"; break; |
| 4894 |
case 'rmi' : $ctype = "audio/midi"; break; |
| 4895 |
case 'roff' : $ctype = "application/x-troff"; break; |
| 4896 |
case 'rss' : $ctype = "application/rss+xml"; break; |
| 4897 |
case 'rtf' : $ctype = "text/rtf"; break; |
| 4898 |
case 'rtx' : $ctype = "text/richtext"; break; |
| 4899 |
case 'sgm' : $ctype = "text/sgml"; break; |
| 4900 |
case 'sgml' : $ctype = "text/sgml"; break; |
| 4901 |
case 'sh' : $ctype = "application/x-sh"; break; |
| 4902 |
case 'shar' : $ctype = "application/x-shar"; break; |
| 4903 |
case 'sig' : $ctype = "application/pgp-signature"; break; |
| 4904 |
case 'silo' : $ctype = "model/mesh"; break; |
| 4905 |
case 'sit' : $ctype = "application/x-stuffit"; break; |
| 4906 |
case 'skd' : $ctype = "application/x-koan"; break; |
| 4907 |
case 'skm' : $ctype = "application/x-koan"; break; |
| 4908 |
case 'skp' : $ctype = "application/x-koan"; break; |
| 4909 |
case 'skt' : $ctype = "application/x-koan"; break; |
| 4910 |
case 'sldx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.slide"; break; |
| 4911 |
case 'smi' : $ctype = "application/smil"; break; |
| 4912 |
case 'smil' : $ctype = "application/smil"; break; |
| 4913 |
case 'snd' : $ctype = "audio/basic"; break; |
| 4914 |
case 'so' : $ctype = "application/octet-stream"; break; |
| 4915 |
case 'spl' : $ctype = "application/x-futuresplash"; break; |
| 4916 |
case 'spx' : $ctype = "audio/ogg"; break; |
| 4917 |
case 'src' : $ctype = "application/x-wais-source"; break; |
| 4918 |
case 'stk' : $ctype = "application/hyperstudio"; break; |
| 4919 |
case 'sv4cpio' : $ctype = "application/x-sv4cpio"; break; |
| 4920 |
case 'sv4crc' : $ctype = "application/x-sv4crc"; break; |
| 4921 |
case 'svg' : $ctype = "image/svg+xml"; break; |
| 4922 |
case 'swf' : $ctype = "application/x-shockwave-flash"; break; |
| 4923 |
case 't' : $ctype = "application/x-troff"; break; |
| 4924 |
case 'tar' : $ctype = "application/x-tar"; break; |
| 4925 |
case 'tcl' : $ctype = "application/x-tcl"; break; |
| 4926 |
case 'tex' : $ctype = "application/x-tex"; break; |
| 4927 |
case 'texi' : $ctype = "application/x-texinfo"; break; |
| 4928 |
case 'texinfo' : $ctype = "application/x-texinfo"; break; |
| 4929 |
case 'tif' : $ctype = "image/tiff"; break; |
| 4930 |
case 'tiff' : $ctype = "image/tiff"; break; |
| 4931 |
case 'torrent' : $ctype = "application/x-bittorrent"; break; |
| 4932 |
case 'tr' : $ctype = "application/x-troff"; break; |
| 4933 |
case 'tsv' : $ctype = "text/tab-separated-values"; break; |
| 4934 |
case 'txt' : $ctype = "text/plain"; break; |
| 4935 |
case 'ustar' : $ctype = "application/x-ustar"; break; |
| 4936 |
case 'vcd' : $ctype = "application/x-cdlink"; break; |
| 4937 |
case 'vrml' : $ctype = "model/vrml"; break; |
| 4938 |
case 'vsd' : $ctype = "application/vnd.visio"; break; |
| 4939 |
case 'vss' : $ctype = "application/vnd.visio"; break; |
| 4940 |
case 'vst' : $ctype = "application/vnd.visio"; break; |
| 4941 |
case 'vsw' : $ctype = "application/vnd.visio"; break; |
| 4942 |
case 'vxml' : $ctype = "application/voicexml+xml"; break; |
| 4943 |
case 'wav' : $ctype = "audio/x-wav"; break; |
| 4944 |
case 'wbmp' : $ctype = "image/vnd.wap.wbmp"; break; |
| 4945 |
case 'wbmxl' : $ctype = "application/vnd.wap.wbxml"; break; |
| 4946 |
case 'wm' : $ctype = "video/x-ms-wm"; break; |
| 4947 |
case 'wml' : $ctype = "text/vnd.wap.wml"; break; |
| 4948 |
case 'wmlc' : $ctype = "application/vnd.wap.wmlc"; break; |
| 4949 |
case 'wmls' : $ctype = "text/vnd.wap.wmlscript"; break; |
| 4950 |
case 'wmlsc' : $ctype = "application/vnd.wap.wmlscriptc"; break; |
| 4951 |
case 'wmv' : $ctype = "video/x-ms-wmv"; break; |
| 4952 |
case 'wmx' : $ctype = "video/x-ms-wmx"; break; |
| 4953 |
case 'wrl' : $ctype = "model/vrml"; break; |
| 4954 |
case 'xbm' : $ctype = "image/x-xbitmap"; break; |
| 4955 |
case 'xdssc' : $ctype = "application/dssc+xml"; break; |
| 4956 |
case 'xer' : $ctype = "application/patch-ops-error+xml"; break; |
| 4957 |
case 'xht' : $ctype = "application/xhtml+xml"; break; |
| 4958 |
case 'xhtml' : $ctype = "application/xhtml+xml"; break; |
| 4959 |
case 'xla' : $ctype = "application/vnd.ms-excel"; break; |
| 4960 |
case 'xlam' : $ctype = "application/vnd.ms-excel.addin.macroEnabled.12"; break; |
| 4961 |
case 'xlc' : $ctype = "application/vnd.ms-excel"; break; |
| 4962 |
case 'xlm' : $ctype = "application/vnd.ms-excel"; break; |
| 4963 |
case 'xls' : $ctype = "application/vnd.ms-excel"; break; |
| 4964 |
case 'xlsx' : $ctype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; break; |
| 4965 |
case 'xlsb' : $ctype = "application/vnd.ms-excel.sheet.binary.macroEnabled.12"; break; |
| 4966 |
case 'xlt' : $ctype = "application/vnd.ms-excel"; break; |
| 4967 |
case 'xltx' : $ctype = "application/vnd.openxmlformats-officedocument.spreadsheetml.template"; break; |
| 4968 |
case 'xlw' : $ctype = "application/vnd.ms-excel"; break; |
| 4969 |
case 'xml' : $ctype = "application/xml"; break; |
| 4970 |
case 'xpm' : $ctype = "image/x-xpixmap"; break; |
| 4971 |
case 'xsl' : $ctype = "application/xml"; break; |
| 4972 |
case 'xslt' : $ctype = "application/xslt+xml"; break; |
| 4973 |
case 'xul' : $ctype = "application/vnd.mozilla.xul+xml"; break; |
| 4974 |
case 'xwd' : $ctype = "image/x-xwindowdump"; break; |
| 4975 |
case 'xyz' : $ctype = "chemical/x-xyz"; break; |
| 4976 |
case 'zip' : $ctype = "application/zip"; break; |
| 4977 |
default : $ctype = "application/force-download"; |
| 4978 |
endswitch; |
| 4979 |
|
| 4980 |
return apply_filters( 'ebook_file_ctype', $ctype ); |
| 4981 |
} |
| 4982 |
function ebook_process_download($data = false) { |
| 4983 |
$QSWPOptions = new QSWPOptions(); |
| 4984 |
if ($data) { |
| 4985 |
extract($data); |
| 4986 |
} |
| 4987 |
// Errors must not be printed into a binary file download, but the site's |
| 4988 |
// error *reporting* level is the site owner's business, not this plugin's. |
| 4989 |
@ini_set( 'display_errors', '0' ); |
| 4990 |
|
| 4991 |
$formats = array('mobi','txt','epub','zip', 'mp3','mp4'); |
| 4992 |
if (@$_GET['wc_order'] != '') { |
| 4993 |
$formats[] = 'pdf'; |
| 4994 |
} |
| 4995 |
if ((@$_REQUEST['ebook_key'] != false && $_REQUEST['action'] == 'download') || (@$_REQUEST['action'] == 'wc_order_download')) { |
| 4996 |
//error_reporting(E_ALL); |
| 4997 |
if( function_exists( 'apache_setenv' ) ) @apache_setenv('no-gzip', 1); |
| 4998 |
@ini_set( 'zlib.output_compression', 'Off' ); |
| 4999 |
nocache_headers(); |
| 5000 |
$order_id = 0; |
| 5001 |
$ebook = 0; |
| 5002 |
$request_ebook_key = isset( $_REQUEST['ebook_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['ebook_key'] ) ) : ''; |
| 5003 |
$request_order_id = isset( $_REQUEST['order_id'] ) ? absint( $_REQUEST['order_id'] ) : 0; |
| 5004 |
|
| 5005 |
if ( $request_order_id > 0 && $request_ebook_key !== '' ) { |
| 5006 |
$stored_ebook_key = (string) get_post_meta( $request_order_id, 'ebook_key', true ); |
| 5007 |
if ( $stored_ebook_key !== '' && hash_equals( $stored_ebook_key, $request_ebook_key ) ) { |
| 5008 |
$order_id = $request_order_id; |
| 5009 |
$ebook = (int) get_post_meta( $order_id, 'ebook', true ); |
| 5010 |
} |
| 5011 |
} |
| 5012 |
|
| 5013 |
if ( ! $order_id && $request_ebook_key !== '' ) { |
| 5014 |
$loop = new WP_Query( |
| 5015 |
array( |
| 5016 |
'post_type' => 'ebook_order', |
| 5017 |
'meta_key' => 'ebook_key', |
| 5018 |
'meta_value' => $request_ebook_key, |
| 5019 |
'posts_per_page' => 1, |
| 5020 |
'orderby' => 'ID', |
| 5021 |
'order' => 'DESC', |
| 5022 |
) |
| 5023 |
); |
| 5024 |
while ( $loop->have_posts() ) : $loop->the_post(); |
| 5025 |
$order_id = get_the_ID(); |
| 5026 |
$ebook = get_post_meta(get_the_ID(),'ebook',true); |
| 5027 |
endwhile; |
| 5028 |
wp_reset_postdata(); |
| 5029 |
} |
| 5030 |
|
| 5031 |
if (@$_REQUEST['action'] == 'wc_order_download') { |
| 5032 |
// A WooCommerce download is only authorised by the order key. Verify it |
| 5033 |
// here, before any file is resolved: this branch used to trust order_id |
| 5034 |
// and ebook_id straight from the query string, which handed every paid |
| 5035 |
// ebook to anonymous visitors. |
| 5036 |
$order_id = isset( $_GET['order_id'] ) ? absint( $_GET['order_id'] ) : 0; |
| 5037 |
$ebook_id = isset( $_GET['ebook_id'] ) ? absint( $_GET['ebook_id'] ) : 0; |
| 5038 |
|
| 5039 |
if ( ! ebook_store_wc_order_key_matches( $order_id, isset( $_GET['wc_order'] ) ? wp_unslash( $_GET['wc_order'] ) : '' ) ) { |
| 5040 |
wp_die( esc_html__( 'This download link is not valid for that order.', 'ebook-store' ), '', array( 'response' => 403 ) ); |
| 5041 |
} |
| 5042 |
|
| 5043 |
if ( ! ebook_store_wc_order_contains_ebook( $order_id, $ebook_id ) ) { |
| 5044 |
wp_die( esc_html__( 'That ebook is not part of this order.', 'ebook-store' ), '', array( 'response' => 403 ) ); |
| 5045 |
} |
| 5046 |
|
| 5047 |
$ebook = $ebook_id; |
| 5048 |
} |
| 5049 |
if (@$_REQUEST['action'] == 'download' && @$_REQUEST['type'] == 'bonus') { |
| 5050 |
$ebook_id = (int)$_GET['ebook_id']; |
| 5051 |
$ebook = $ebook_id; |
| 5052 |
$ebookObj = new EbookStoreEbook($ebook); |
| 5053 |
$requested_file = $ebookObj->encrypted((int)$_GET['order_id'], 'pdf'); |
| 5054 |
|
| 5055 |
} |
| 5056 |
$attachment = get_post_meta($ebook,'ebook_wp_custom_attachment'); |
| 5057 |
//wp_die(print_r($attachment,true)); |
| 5058 |
if (@!$requested_file) { |
| 5059 |
$requested_file = $attachment[0]['file']; |
| 5060 |
} |
| 5061 |
|
| 5062 |
if (get_post_meta($order_id,'encrypted_pdf',true) == 'in_progress') { |
| 5063 |
if (!ebook_store_encryptable($requested_file)) { |
| 5064 |
wp_die('The uploaded file is not in the proper format and can not be encrypted, please re-upload it in Acrobat 5.0 PDF 1.5 Compatibility mode or disable encryption. See video tutorial here: <a href="https://youtu.be/-yDr-76W4gk" target="_blank">https://youtu.be/-yDr-76W4gk</a>'); |
| 5065 |
} |
| 5066 |
if (get_option('ebook_store_no_autorefresh', false) == false) { |
| 5067 |
wp_die('<h3>Your copy is being generated right now, the page will refresh automatically and the download will start shortly...</h3><script type="text/javascript"> |
| 5068 |
setInterval(\'window.location.reload()\', 15000); |
| 5069 |
</script>'); |
| 5070 |
} else { |
| 5071 |
wp_die("Your copy is being generated right now, please try again by refreshing the page in a few minutes or by clicking <a href='#' onClick='window.location.reload();' id='retryLink'>retry download</a> |
| 5072 |
"); |
| 5073 |
} |
| 5074 |
|
| 5075 |
} |
| 5076 |
if (get_post_meta($order_id,'encrypted_pdf',true) != '' && $_REQUEST['type'] != 'bonus') { |
| 5077 |
$requested_file = get_post_meta($order_id,'encrypted_pdf',true); |
| 5078 |
} |
| 5079 |
if (@in_array($_GET['format'], $formats)) { |
| 5080 |
$format_meta_tag = 'ebook_wp_custom_attachment' . (@$_GET['format'] != 'pdf' ? '_' . $_GET['format'] : ''); |
| 5081 |
$attachment = get_post_meta($ebook,$format_meta_tag); |
| 5082 |
//wp_die(print_r($attachment,true)); |
| 5083 |
$requested_file = $attachment[0]['file']; |
| 5084 |
if ( @$_GET['format'] === 'epub' && get_option( 'ebook_store_epub_watermark' ) ) { |
| 5085 |
// Standalone (non-WooCommerce) epub downloads were served raw; run |
| 5086 |
// them through the watermarker too, using the resolved order id. |
| 5087 |
$ebookObj = new EbookStoreEbook( $ebook ); |
| 5088 |
$ebookObj->order_id = $order_id; |
| 5089 |
$requested_file = $ebookObj->encrypted( $order_id, 'epub' ); |
| 5090 |
} |
| 5091 |
if (@$_GET['wc_order'] != '') { |
| 5092 |
$wc_order = sanitize_text_field($_GET['wc_order']); |
| 5093 |
$ebookObj = new EbookStoreEbook($ebook); |
| 5094 |
$requested_file = $ebookObj->encrypted((int)$_GET['order_id'], $_GET['format']); |
| 5095 |
} |
| 5096 |
} |
| 5097 |
$ctype = ebook_get_file_ctype(pathinfo($requested_file,PATHINFO_EXTENSION)); |
| 5098 |
$payment_status = strtolower( trim( (string) get_post_meta( $order_id, 'payment_status', true ) ) ); |
| 5099 |
if ( $payment_status !== '' && ebook_store_is_negative_order_status( $payment_status ) ) { |
| 5100 |
wp_die( esc_html__( 'Oops, this order is no longer eligible for downloads.', 'ebook-store' ) ); |
| 5101 |
} |
| 5102 |
$downloads = (int) get_post_meta( $order_id, 'downloads', true ); |
| 5103 |
$downloads_limit = ebook_store_get_download_limit(); |
| 5104 |
//wp_die('Downloads ' . $downloads); |
| 5105 |
if ( $downloads >= $downloads_limit ) { |
| 5106 |
wp_die('Oops, you have reached the maximum amount of downloads for this order, you need to order again.'); |
| 5107 |
} |
| 5108 |
// $gmt_timestamp = get_post_time(); |
| 5109 |
// if (!$gmt_timestamp) { |
| 5110 |
// $gmt_timestamp = get_post_time('U', false, (int)$_GET['order_id']); |
| 5111 |
// } |
| 5112 |
//wp_die("GMT timestamp $gmt_timestamp, time " . time() . ' post id ' . get_the_ID()); |
| 5113 |
|
| 5114 |
|
| 5115 |
$gmt_timestamp = get_post_time('U', false, ($order_id > 0 ? $order_id : get_the_ID())); |
| 5116 |
if (!$gmt_timestamp) { |
| 5117 |
$gmt_timestamp = get_post_time('U', false, (int)$_GET['order_id']); |
| 5118 |
} |
| 5119 |
|
| 5120 |
|
| 5121 |
$link_expiration = trim( (string) get_option( 'link_expiration', $QSWPOptions->link_expiration ) ); |
| 5122 |
if ( $link_expiration === '' ) { |
| 5123 |
$link_expiration = '1 year'; |
| 5124 |
} |
| 5125 |
|
| 5126 |
$strtotime = strtotime("+" . $link_expiration,$gmt_timestamp); |
| 5127 |
|
| 5128 |
if (WP_DEBUG == true) { |
| 5129 |
//error_log("strtotime $strtotime vs time " . time() . ' vs GMT TIMESTAMP ' . $gmt_timestamp); |
| 5130 |
} |
| 5131 |
if ($strtotime < time()) { |
| 5132 |
wp_die('Oops, the link you are using has expired, you need to order again.'); |
| 5133 |
} |
| 5134 |
|
| 5135 |
if ( ! $requested_file || ! file_exists( $requested_file ) ) { |
| 5136 |
wp_die( esc_html__( 'Sorry, the ebook file was not found for that order. It may have expired.', 'ebook-store' ) ); |
| 5137 |
} |
| 5138 |
|
| 5139 |
header("Robots: none"); |
| 5140 |
header("Content-Type: " . $ctype . ""); |
| 5141 |
header("Content-Description: File Transfer"); |
| 5142 |
header("Content-Disposition: attachment; filename=\"" . apply_filters( 'ebook_requested_file_name', basename( $requested_file ) ) . "\";"); |
| 5143 |
header("Content-Transfer-Encoding: binary"); |
| 5144 |
ebook_readfile_chunked($requested_file); |
| 5145 |
ebook_count_download($order_id); |
| 5146 |
die(); |
| 5147 |
//endwhile; |
| 5148 |
} else if (@$_REQUEST['action'] == 'download_free') { |
| 5149 |
|
| 5150 |
@$formData = ebook_store_get_form($_REQUEST['md5_nonce']); |
| 5151 |
if (is_object($formData)) { |
| 5152 |
$marketing_emails = array_unique( |
| 5153 |
array_filter( |
| 5154 |
array( |
| 5155 |
isset( $formData->email ) ? $formData->email : '', |
| 5156 |
isset( $formData->payer_email ) ? $formData->payer_email : '', |
| 5157 |
) |
| 5158 |
) |
| 5159 |
); |
| 5160 |
foreach ( $marketing_emails as $marketing_email ) { |
| 5161 |
ebook_store_subscribe_buyer_to_marketing_lists( $marketing_email ); |
| 5162 |
} |
| 5163 |
} |
| 5164 |
// Only suppress *display* of errors — this runs while streaming a file. |
| 5165 |
@ini_set( 'display_errors', '0' ); |
| 5166 |
|
| 5167 |
$id = $_REQUEST['p']; |
| 5168 |
$loop = new WP_Query( array ( 'post_type' => 'ebook', 'p' => $id ) ); |
| 5169 |
|
| 5170 |
|
| 5171 |
|
| 5172 |
|
| 5173 |
while ( $loop->have_posts() ) : $loop->the_post(); |
| 5174 |
$attachment = get_post_meta($id,'ebook_wp_custom_attachment'); |
| 5175 |
$ebook = get_post_meta(get_the_ID(), 'ebook', true); |
| 5176 |
|
| 5177 |
if (get_option('encrypt_pdf') == 1) { |
| 5178 |
if (!ebook_store_encryptable($attachment[0]['file'])) { |
| 5179 |
echo('The uploaded file is not in the proper format, please re-upload it in Acrobat 5.0 PDF 1.5 Compatibility mode. See video tutorial here: <a href="https://youtu.be/-yDr-76W4gk" target="_blank">https://youtu.be/-yDr-76W4gk</a>'); |
| 5180 |
} |
| 5181 |
} |
| 5182 |
//die(); |
| 5183 |
if ($ebook['ebook_price'] == 0) { |
| 5184 |
$ebook['donate_or_download'] = 'free'; |
| 5185 |
} |
| 5186 |
if ($ebook['ebook_price'] > 0 && $ebook['donate_or_download'] != 'free') { |
| 5187 |
wp_die('You are trying to download a file that is not free - ' . get_the_title()); |
| 5188 |
} |
| 5189 |
$status = get_post_status(); |
| 5190 |
erl($status); |
| 5191 |
|
| 5192 |
if ($status != 'publish') { |
| 5193 |
wp_die('You are trying to download a file that is not available.'); |
| 5194 |
} |
| 5195 |
|
| 5196 |
$requested_file = $attachment[0]['file']; |
| 5197 |
|
| 5198 |
$ctype = ebook_get_file_ctype(pathinfo($requested_file,PATHINFO_EXTENSION)); |
| 5199 |
$gmt_timestamp = get_post_time('U'); |
| 5200 |
//insert order |
| 5201 |
$my_post = array( |
| 5202 |
'post_title' => 'Free Download', |
| 5203 |
'post_type' => 'ebook_order', |
| 5204 |
'post_status' => 'publish', |
| 5205 |
'post_author' => 1, |
| 5206 |
'post_category' => array(8,39)); |
| 5207 |
$post_id = wp_insert_post( $my_post, @$wp_error ); |
| 5208 |
//txn_id payment_date payer_email |
| 5209 |
//encrypt here. |
| 5210 |
global $current_user; |
| 5211 |
$current_user = wp_get_current_user(); |
| 5212 |
$data = array(); |
| 5213 |
|
| 5214 |
$data['txn_id'] = 'free'; |
| 5215 |
//$data['payment_date'] = date("F j, Y, g:i a"); |
| 5216 |
$data['payment_date'] = current_time('mysql'); |
| 5217 |
|
| 5218 |
|
| 5219 |
|
| 5220 |
if (get_option('encrypt_files_for_logged_in_users') == 1) { |
| 5221 |
$data['payer_email'] = $current_user->user_email; |
| 5222 |
} else { |
| 5223 |
$data['payer_email'] = ''; |
| 5224 |
if (@$formData->email != '') { |
| 5225 |
$data['payer_email'] = $formData->email; |
| 5226 |
} |
| 5227 |
} |
| 5228 |
|
| 5229 |
if ($formData->payer_email != '') { |
| 5230 |
$data['payer_email'] = $formData->payer_email; |
| 5231 |
} |
| 5232 |
|
| 5233 |
//error_log(print_r($formData,true)); |
| 5234 |
|
| 5235 |
|
| 5236 |
$data['first_name'] = $current_user->user_firstname; |
| 5237 |
$data['last_name'] = $current_user->user_lastname; |
| 5238 |
$data['mc_currency'] = ebook_store_get_store_currency(); |
| 5239 |
$data['tax'] = 0; |
| 5240 |
$data['ip'] = $_SERVER['REMOTE_ADDR']; |
| 5241 |
$data['residence_country'] = 'n/a'; |
| 5242 |
//error_log('data ' . print_r($data,true)); |
| 5243 |
if ($data['first_name'] == '') { |
| 5244 |
if ($formData->name != '') { |
| 5245 |
$data['first_name'] = $formData->name; |
| 5246 |
$data['last_name'] = ''; |
| 5247 |
} else if ($formData->first_name != '') { |
| 5248 |
$data['first_name'] = $formData->first_name; |
| 5249 |
$data['last_name'] = $formData->last_name; |
| 5250 |
} else { |
| 5251 |
$data['first_name'] = 'Anonymous'; |
| 5252 |
$data['last_name'] = 'User'; |
| 5253 |
|
| 5254 |
} |
| 5255 |
} |
| 5256 |
|
| 5257 |
|
| 5258 |
//wp_die($data['payer_email']); |
| 5259 |
|
| 5260 |
$md5_nonce = strip_tags($_REQUEST['md5_nonce']); |
| 5261 |
$ebook_key = $md5_nonce; |
| 5262 |
|
| 5263 |
$data['password'] = ebook_store_get_password_for_order($post_id, array( |
| 5264 |
'password' => isset($data['password']) ? $data['password'] : '', |
| 5265 |
'payer_email' => $data['payer_email'], |
| 5266 |
'md5_nonce' => $md5_nonce, |
| 5267 |
'ebook_key' => $ebook_key, |
| 5268 |
)); |
| 5269 |
|
| 5270 |
global $attachment; |
| 5271 |
@$attachment[0]['file'] = $requested_file; |
| 5272 |
|
| 5273 |
$fileExt = pathinfo($attachment[0]['file'],PATHINFO_EXTENSION); |
| 5274 |
if ($fileExt == 'pdf' && get_option('encrypt_pdf')) { |
| 5275 |
//ebook_encrypt_pdf($data, $post_id); |
| 5276 |
$requested_file = ebook_encrypt_pdf($data, $post_id); |
| 5277 |
} |
| 5278 |
|
| 5279 |
$formData = ebook_store_get_form($md5_nonce); |
| 5280 |
$formData = json_encode($formData); |
| 5281 |
$item_name = get_the_title((int)$_REQUEST['p']); |
| 5282 |
//die($item_name); |
| 5283 |
update_post_meta($post_id,'ebook_key',$md5_nonce); |
| 5284 |
update_post_meta($post_id,'downloads',1); |
| 5285 |
update_post_meta($post_id,'formData',wp_slash($formData)); |
| 5286 |
update_post_meta($post_id, 'downloadlink', 'http://' . sanitize_text_field($_SERVER['HTTP_HOST']) . esc_url_raw($_SERVER['REQUEST_URI'])); |
| 5287 |
update_post_meta($post_id,'ebook',(int)$_REQUEST['p']); |
| 5288 |
update_post_meta($post_id,'item_name',$item_name); |
| 5289 |
update_post_meta($post_id,'payer_email',(@$data['payer_email'] != '' ? $data['payer_email'] : 'N/A')); |
| 5290 |
update_post_meta($post_id,'mc_gross',0); |
| 5291 |
update_post_meta($post_id,'mc_fee',0); |
| 5292 |
update_post_meta($post_id,'payment_date',date("F j, Y, g:i a")); |
| 5293 |
foreach ($data as $dk => $dv) { |
| 5294 |
update_post_meta($post_id,$dk,$dv); |
| 5295 |
} |
| 5296 |
//get_post_meta($post->ID,'item_name',true) |
| 5297 |
//insert order end |
| 5298 |
if (headers_sent()) { |
| 5299 |
die('Sorry, headers sent.'); |
| 5300 |
} |
| 5301 |
$checkoutPage = esc_attr(get_option('ebook_store_checkout_page')); |
| 5302 |
if (!$checkoutPage) { |
| 5303 |
$checkoutPage = $post_id; |
| 5304 |
} |
| 5305 |
update_post_meta($post_id,'encrypted_pdf',wp_slash($requested_file)); |
| 5306 |
header("Location: ". add_query_arg(array('ebook_key' => $md5_nonce, 'action' => 'thank_you'),get_permalink($checkoutPage))); |
| 5307 |
|
| 5308 |
// header("Robots: none"); |
| 5309 |
// header("Content-Type: " . $ctype . ""); |
| 5310 |
// header("Content-Description: File Transfer"); |
| 5311 |
// header("Content-Disposition: attachment; filename=\"" . apply_filters( 'ebook_requested_file_name', basename( $requested_file ) ) . "\";"); |
| 5312 |
// header("Content-Transfer-Encoding: binary"); |
| 5313 |
// ebook_readfile_chunked($requested_file); |
| 5314 |
endwhile; |
| 5315 |
die(); |
| 5316 |
} else if ($_REQUEST['action'] == 'ebook_bonus_download') { |
| 5317 |
$ebook_id = isset( $_GET['ebook_id'] ) ? absint( $_GET['ebook_id'] ) : 0; |
| 5318 |
$ebook_key = isset( $_GET['ebook_key'] ) ? preg_replace( '/[^a-zA-Z0-9]/', '', (string) wp_unslash( $_GET['ebook_key'] ) ) : ''; |
| 5319 |
|
| 5320 |
// The key must resolve to a real order before we do anything. Without this |
| 5321 |
// an anonymous GET with a bogus key created a junk ebook_order (null email, |
| 5322 |
// garbage details) and could trigger a marketing subscribe + PDF encrypt. |
| 5323 |
$ebook_order_meta = $ebook_key !== '' ? ebook_get_order( 'ebook_key', $ebook_key ) : null; |
| 5324 |
if ( empty( $ebook_order_meta['order_id'][0] ) ) { |
| 5325 |
wp_die( esc_html__( 'This download link is not valid.', 'ebook-store' ), '', array( 'response' => 403 ) ); |
| 5326 |
} |
| 5327 |
|
| 5328 |
// The requested bonus book must actually be a bonus of that order's ebook. |
| 5329 |
$parent_ebook = isset( $ebook_order_meta['ebook'][0] ) ? absint( $ebook_order_meta['ebook'][0] ) : 0; |
| 5330 |
$allowed_bonus = $parent_ebook ? (array) get_post_meta( $parent_ebook, 'ebook_bonus', true ) : array(); |
| 5331 |
$allowed_bonus = array_map( 'absint', $allowed_bonus ); |
| 5332 |
if ( ! in_array( $ebook_id, $allowed_bonus, true ) ) { |
| 5333 |
wp_die( esc_html__( 'That bonus book is not part of this order.', 'ebook-store' ), '', array( 'response' => 403 ) ); |
| 5334 |
} |
| 5335 |
|
| 5336 |
$data['ebook'] = get_post_meta( $ebook_id, 'ebook_bonus', true ); |
| 5337 |
$data['txn_id'] = 'Bonus Download (' . ( isset( $ebook_order_meta['item_name'][0] ) ? $ebook_order_meta['item_name'][0] : '' ) . ')'; |
| 5338 |
$data['payer_email'] = isset( $ebook_order_meta['payer_email'][0] ) ? $ebook_order_meta['payer_email'][0] : ''; |
| 5339 |
$data['first_name'] = 'Bonus'; |
| 5340 |
$data['last_name'] = 'Download (Order #' . $ebook_order_meta['order_id'][0] . ')'; |
| 5341 |
ebook_store_add_order($data, true); |
| 5342 |
@$ebook_order['ebook_key'][0] = $ebook_key; |
| 5343 |
@$ebook_order['ebook'][0] = $data['ebook']; |
| 5344 |
$downloadlink = ebook_download_link($ebook_order, 0, 1); |
| 5345 |
header("Location: " . $downloadlink); //add proper ebook id here and allow it. |
| 5346 |
//wp_die($downloadlink); |
| 5347 |
} |
| 5348 |
} |
| 5349 |
function ebook_count_download($order_id) { |
| 5350 |
$downloads = get_post_meta($order_id,'downloads',true); |
| 5351 |
return update_post_meta($order_id,'downloads',floatval($downloads) + 1); |
| 5352 |
} |
| 5353 |
function ebook_get_order($key = 'ebook_key',$val = '') { |
| 5354 |
$loop = new WP_Query( array ( 'post_type' => 'ebook_order', 'meta_key' => $key, 'meta_value' => $val ) ); |
| 5355 |
while ( $loop->have_posts() ) : $loop->the_post(); |
| 5356 |
$meta = get_post_meta(get_the_ID(),null,true); |
| 5357 |
$meta['order_id'][0] = get_the_ID(); |
| 5358 |
$gross = isset( $meta['mc_gross'][0] ) ? (float) $meta['mc_gross'][0] : 0; |
| 5359 |
$tax = isset( $meta['tax'][0] ) ? (float) $meta['tax'][0] : 0; |
| 5360 |
$meta['total'][0] = number_format( round( $gross + $tax, 2 ), 2 ); |
| 5361 |
return $meta; |
| 5362 |
endwhile; |
| 5363 |
} |
| 5364 |
|
| 5365 |
function toMoney($val,$symbol='',$r=2) |
| 5366 |
{ |
| 5367 |
if ( is_array( $val ) ) { |
| 5368 |
$val = reset( $val ); |
| 5369 |
} |
| 5370 |
|
| 5371 |
return number_format( (float) $val, 2 ); |
| 5372 |
|
| 5373 |
} |
| 5374 |
|
| 5375 |
function ebook_store_normalize_email_log_headers( $headers ) { |
| 5376 |
if ( empty( $headers ) ) { |
| 5377 |
return array(); |
| 5378 |
} |
| 5379 |
|
| 5380 |
if ( is_string( $headers ) ) { |
| 5381 |
$headers = preg_split( '/\r\n|\r|\n/', $headers ); |
| 5382 |
} |
| 5383 |
|
| 5384 |
if ( ! is_array( $headers ) ) { |
| 5385 |
$headers = array( (string) $headers ); |
| 5386 |
} |
| 5387 |
|
| 5388 |
$normalized = array(); |
| 5389 |
|
| 5390 |
foreach ( $headers as $header ) { |
| 5391 |
$header = trim( wp_strip_all_tags( (string) $header ) ); |
| 5392 |
if ( $header === '' ) { |
| 5393 |
continue; |
| 5394 |
} |
| 5395 |
$normalized[] = $header; |
| 5396 |
} |
| 5397 |
|
| 5398 |
return array_values( array_unique( $normalized ) ); |
| 5399 |
} |
| 5400 |
|
| 5401 |
function ebook_store_normalize_email_log_attachments( $attachments ) { |
| 5402 |
if ( empty( $attachments ) ) { |
| 5403 |
return array(); |
| 5404 |
} |
| 5405 |
|
| 5406 |
if ( is_string( $attachments ) ) { |
| 5407 |
$attachments = array( $attachments ); |
| 5408 |
} |
| 5409 |
|
| 5410 |
if ( ! is_array( $attachments ) ) { |
| 5411 |
return array(); |
| 5412 |
} |
| 5413 |
|
| 5414 |
$normalized = array(); |
| 5415 |
|
| 5416 |
foreach ( $attachments as $attachment ) { |
| 5417 |
if ( is_array( $attachment ) && isset( $attachment['file'] ) ) { |
| 5418 |
$attachment = $attachment['file']; |
| 5419 |
} |
| 5420 |
|
| 5421 |
$attachment = trim( (string) $attachment ); |
| 5422 |
|
| 5423 |
if ( $attachment === '' ) { |
| 5424 |
continue; |
| 5425 |
} |
| 5426 |
|
| 5427 |
$normalized[] = $attachment; |
| 5428 |
} |
| 5429 |
|
| 5430 |
return array_values( array_unique( $normalized ) ); |
| 5431 |
} |
| 5432 |
|
| 5433 |
function ebook_store_json_encode_for_log( $value ) { |
| 5434 |
$encoded = wp_json_encode( $value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); |
| 5435 |
|
| 5436 |
return $encoded ? $encoded : ''; |
| 5437 |
} |
| 5438 |
|
| 5439 |
function ebook_store_get_first_meta_value( $value ) { |
| 5440 |
if ( is_array( $value ) ) { |
| 5441 |
$value = reset( $value ); |
| 5442 |
} |
| 5443 |
|
| 5444 |
return $value; |
| 5445 |
} |
| 5446 |
|
| 5447 |
function ebook_store_create_email_log_entry( $args = array() ) { |
| 5448 |
$defaults = array( |
| 5449 |
'to' => '', |
| 5450 |
'subject' => '', |
| 5451 |
'body' => '', |
| 5452 |
'headers' => array(), |
| 5453 |
'attachments' => array(), |
| 5454 |
'result' => false, |
| 5455 |
'source' => 'standalone', |
| 5456 |
'related_order_type' => '', |
| 5457 |
'related_order_id' => 0, |
| 5458 |
'ebook_id' => 0, |
| 5459 |
'ebook_key' => '', |
| 5460 |
'gateway' => '', |
| 5461 |
'order' => array(), |
| 5462 |
'context' => array(), |
| 5463 |
'parent_log_id' => 0, |
| 5464 |
); |
| 5465 |
|
| 5466 |
$args = wp_parse_args( $args, $defaults ); |
| 5467 |
|
| 5468 |
$recipient = trim( (string) $args['to'] ); |
| 5469 |
$subject = wp_strip_all_tags( (string) $args['subject'] ); |
| 5470 |
$sent_at = current_time( 'mysql' ); |
| 5471 |
$sent_at_gmt = current_time( 'mysql', true ); |
| 5472 |
$status = $args['result'] ? 'sent' : 'failed'; |
| 5473 |
$order = is_array( $args['order'] ) ? $args['order'] : array(); |
| 5474 |
$context = is_array( $args['context'] ) ? $args['context'] : array(); |
| 5475 |
$headers = ebook_store_normalize_email_log_headers( $args['headers'] ); |
| 5476 |
$attachments = ebook_store_normalize_email_log_attachments( $args['attachments'] ); |
| 5477 |
$ebook_id = absint( ebook_store_get_first_meta_value( $args['ebook_id'] ) ); |
| 5478 |
$related_order_id = absint( ebook_store_get_first_meta_value( $args['related_order_id'] ) ); |
| 5479 |
$parent_log_id = absint( $args['parent_log_id'] ); |
| 5480 |
$gateway = sanitize_key( (string) $args['gateway'] ); |
| 5481 |
$source = sanitize_key( (string) $args['source'] ); |
| 5482 |
$related_order_type = sanitize_key( (string) $args['related_order_type'] ); |
| 5483 |
|
| 5484 |
if ( $ebook_id < 1 && ! empty( $order['ebook'] ) ) { |
| 5485 |
$ebook_id = absint( ebook_store_get_first_meta_value( $order['ebook'] ) ); |
| 5486 |
} |
| 5487 |
|
| 5488 |
if ( $related_order_id < 1 && ! empty( $order['order_id'] ) ) { |
| 5489 |
$related_order_id = absint( ebook_store_get_first_meta_value( $order['order_id'] ) ); |
| 5490 |
} |
| 5491 |
|
| 5492 |
if ( $related_order_type === '' ) { |
| 5493 |
$related_order_type = $source === 'woocommerce' ? 'shop_order' : 'ebook_order'; |
| 5494 |
} |
| 5495 |
|
| 5496 |
$title_bits = array(); |
| 5497 |
|
| 5498 |
if ( $recipient !== '' ) { |
| 5499 |
$title_bits[] = $recipient; |
| 5500 |
} |
| 5501 |
|
| 5502 |
if ( $subject !== '' ) { |
| 5503 |
$title_bits[] = $subject; |
| 5504 |
} |
| 5505 |
|
| 5506 |
if ( empty( $title_bits ) ) { |
| 5507 |
$title_bits[] = __( 'Order email', 'ebook-store' ); |
| 5508 |
} |
| 5509 |
|
| 5510 |
$post_id = wp_insert_post( |
| 5511 |
array( |
| 5512 |
'post_type' => 'ebook_email_log', |
| 5513 |
'post_status' => 'publish', |
| 5514 |
'post_title' => implode( ' - ', array_slice( $title_bits, 0, 2 ) ), |
| 5515 |
), |
| 5516 |
true |
| 5517 |
); |
| 5518 |
|
| 5519 |
if ( is_wp_error( $post_id ) || ! $post_id ) { |
| 5520 |
return 0; |
| 5521 |
} |
| 5522 |
|
| 5523 |
update_post_meta( $post_id, 'status', $status ); |
| 5524 |
update_post_meta( $post_id, 'sent_at', $sent_at ); |
| 5525 |
update_post_meta( $post_id, 'sent_at_gmt', $sent_at_gmt ); |
| 5526 |
update_post_meta( $post_id, 'recipient', $recipient ); |
| 5527 |
update_post_meta( $post_id, 'subject', (string) $args['subject'] ); |
| 5528 |
update_post_meta( $post_id, 'body_html', (string) $args['body'] ); |
| 5529 |
update_post_meta( $post_id, 'headers', $headers ); |
| 5530 |
update_post_meta( $post_id, 'attachments', $attachments ); |
| 5531 |
update_post_meta( $post_id, 'source', $source ); |
| 5532 |
update_post_meta( $post_id, 'related_order_type', $related_order_type ); |
| 5533 |
update_post_meta( $post_id, 'related_order_id', $related_order_id ); |
| 5534 |
update_post_meta( $post_id, 'ebook_id', $ebook_id ); |
| 5535 |
update_post_meta( $post_id, 'ebook_key', isset( $args['ebook_key'] ) ? sanitize_text_field( (string) ebook_store_get_first_meta_value( $args['ebook_key'] ) ) : '' ); |
| 5536 |
update_post_meta( $post_id, 'gateway', $gateway ); |
| 5537 |
update_post_meta( $post_id, 'order_snapshot', ebook_store_json_encode_for_log( $order ) ); |
| 5538 |
update_post_meta( $post_id, 'context_snapshot', ebook_store_json_encode_for_log( $context ) ); |
| 5539 |
|
| 5540 |
if ( $parent_log_id > 0 ) { |
| 5541 |
update_post_meta( $post_id, 'parent_log_id', $parent_log_id ); |
| 5542 |
} |
| 5543 |
|
| 5544 |
return $post_id; |
| 5545 |
} |
| 5546 |
|
| 5547 |
function ebook_store_get_email_log_order_edit_link( $log_id ) { |
| 5548 |
$order_id = (int) get_post_meta( $log_id, 'related_order_id', true ); |
| 5549 |
$order_type = (string) get_post_meta( $log_id, 'related_order_type', true ); |
| 5550 |
|
| 5551 |
if ( $order_id < 1 ) { |
| 5552 |
return ''; |
| 5553 |
} |
| 5554 |
|
| 5555 |
if ( $order_type === 'ebook_order' && get_post_type( $order_id ) === 'ebook_order' ) { |
| 5556 |
return admin_url( 'post.php?post=' . $order_id . '&action=edit' ); |
| 5557 |
} |
| 5558 |
|
| 5559 |
if ( $order_type === 'shop_order' || get_post_type( $order_id ) === 'shop_order' ) { |
| 5560 |
return admin_url( 'post.php?post=' . $order_id . '&action=edit' ); |
| 5561 |
} |
| 5562 |
|
| 5563 |
return ''; |
| 5564 |
} |
| 5565 |
|
| 5566 |
function ebook_store_get_email_log_ebook_edit_link( $log_id ) { |
| 5567 |
$ebook_id = (int) get_post_meta( $log_id, 'ebook_id', true ); |
| 5568 |
|
| 5569 |
if ( $ebook_id < 1 || get_post_type( $ebook_id ) !== 'ebook' ) { |
| 5570 |
return ''; |
| 5571 |
} |
| 5572 |
|
| 5573 |
return admin_url( 'post.php?post=' . $ebook_id . '&action=edit' ); |
| 5574 |
} |
| 5575 |
|
| 5576 |
function ebook_store_get_email_log_resend_url( $log_id ) { |
| 5577 |
return wp_nonce_url( |
| 5578 |
admin_url( 'admin-post.php?action=ebook_store_resend_email_log&log_id=' . absint( $log_id ) ), |
| 5579 |
'ebook_store_resend_email_log_' . absint( $log_id ) |
| 5580 |
); |
| 5581 |
} |
| 5582 |
|
| 5583 |
function ebook_store_get_order_reset_downloads_url( $order_id ) { |
| 5584 |
return wp_nonce_url( |
| 5585 |
admin_url( 'admin-post.php?action=ebook_store_reset_order_downloads&order_id=' . absint( $order_id ) ), |
| 5586 |
'ebook_store_reset_order_downloads_' . absint( $order_id ) |
| 5587 |
); |
| 5588 |
} |
| 5589 |
|
| 5590 |
function ebook_store_get_order_resend_delivery_url( $order_id ) { |
| 5591 |
return wp_nonce_url( |
| 5592 |
admin_url( 'admin-post.php?action=ebook_store_resend_order_delivery&order_id=' . absint( $order_id ) ), |
| 5593 |
'ebook_store_resend_order_delivery_' . absint( $order_id ) |
| 5594 |
); |
| 5595 |
} |
| 5596 |
|
| 5597 |
function ebook_store_get_order_refund_url( $order_id ) { |
| 5598 |
return wp_nonce_url( |
| 5599 |
admin_url( 'admin-post.php?action=ebook_store_refund_order&order_id=' . absint( $order_id ) ), |
| 5600 |
'ebook_store_refund_order_' . absint( $order_id ) |
| 5601 |
); |
| 5602 |
} |
| 5603 |
|
| 5604 |
function ebook_store_get_order_refund_gateway( $order_id ) { |
| 5605 |
$order_id = absint( $order_id ); |
| 5606 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 5607 |
return ''; |
| 5608 |
} |
| 5609 |
|
| 5610 |
$payment_status = strtolower( trim( (string) get_post_meta( $order_id, 'payment_status', true ) ) ); |
| 5611 |
if ( ebook_store_is_negative_order_status( $payment_status ) ) { |
| 5612 |
return ''; |
| 5613 |
} |
| 5614 |
|
| 5615 |
if ( get_post_meta( $order_id, 'refund_gateway_id', true ) !== '' ) { |
| 5616 |
return ''; |
| 5617 |
} |
| 5618 |
|
| 5619 |
$payment_type = strtolower( trim( (string) get_post_meta( $order_id, 'payment_type', true ) ) ); |
| 5620 |
|
| 5621 |
if ( |
| 5622 |
$payment_type === 'stripe' |
| 5623 |
&& trim( (string) get_post_meta( $order_id, 'stripe_payment_intent_id', true ) ) !== '' |
| 5624 |
&& trim( (string) get_option( 'stripe_secret_key', '' ) ) !== '' |
| 5625 |
) { |
| 5626 |
return 'stripe'; |
| 5627 |
} |
| 5628 |
|
| 5629 |
if ( |
| 5630 |
$payment_type === 'adyen' |
| 5631 |
&& trim( (string) get_post_meta( $order_id, 'adyen_psp_reference', true ) ) !== '' |
| 5632 |
&& trim( (string) get_option( 'adyen_api_key', '' ) ) !== '' |
| 5633 |
&& trim( (string) get_option( 'adyen_merchant_account', '' ) ) !== '' |
| 5634 |
) { |
| 5635 |
return 'adyen'; |
| 5636 |
} |
| 5637 |
|
| 5638 |
return ''; |
| 5639 |
} |
| 5640 |
|
| 5641 |
function ebook_store_get_email_log_status_badge( $status ) { |
| 5642 |
$status = sanitize_key( (string) $status ); |
| 5643 |
$label = $status === 'sent' ? __( 'Sent', 'ebook-store' ) : __( 'Failed', 'ebook-store' ); |
| 5644 |
|
| 5645 |
return '<span class="ebook-email-log-badge ebook-email-log-badge--' . esc_attr( $status === 'sent' ? 'sent' : 'failed' ) . '">' . esc_html( $label ) . '</span>'; |
| 5646 |
} |
| 5647 |
|
| 5648 |
function ebook_store_render_email_log_box( $post ) { |
| 5649 |
$recipient = (string) get_post_meta( $post->ID, 'recipient', true ); |
| 5650 |
$subject = (string) get_post_meta( $post->ID, 'subject', true ); |
| 5651 |
$body_html = (string) get_post_meta( $post->ID, 'body_html', true ); |
| 5652 |
$headers = get_post_meta( $post->ID, 'headers', true ); |
| 5653 |
$attachments = get_post_meta( $post->ID, 'attachments', true ); |
| 5654 |
$status = (string) get_post_meta( $post->ID, 'status', true ); |
| 5655 |
$source = (string) get_post_meta( $post->ID, 'source', true ); |
| 5656 |
$sent_at = (string) get_post_meta( $post->ID, 'sent_at', true ); |
| 5657 |
$order_snapshot = (string) get_post_meta( $post->ID, 'order_snapshot', true ); |
| 5658 |
$context_snapshot = (string) get_post_meta( $post->ID, 'context_snapshot', true ); |
| 5659 |
$ebook_key = (string) get_post_meta( $post->ID, 'ebook_key', true ); |
| 5660 |
$resend_count = (int) get_post_meta( $post->ID, 'resend_count', true ); |
| 5661 |
$last_resend_at = (string) get_post_meta( $post->ID, 'last_resend_at', true ); |
| 5662 |
$last_resend_status = (string) get_post_meta( $post->ID, 'last_resend_status', true ); |
| 5663 |
$order_link = ebook_store_get_email_log_order_edit_link( $post->ID ); |
| 5664 |
$ebook_link = ebook_store_get_email_log_ebook_edit_link( $post->ID ); |
| 5665 |
|
| 5666 |
if ( ! is_array( $headers ) ) { |
| 5667 |
$headers = array(); |
| 5668 |
} |
| 5669 |
|
| 5670 |
if ( ! is_array( $attachments ) ) { |
| 5671 |
$attachments = array(); |
| 5672 |
} |
| 5673 |
|
| 5674 |
echo '<div class="ebook-email-log">'; |
| 5675 |
echo '<div class="ebook-email-log__actions">'; |
| 5676 |
echo '<a class="button button-primary" href="' . esc_url( ebook_store_get_email_log_resend_url( $post->ID ) ) . '">' . esc_html__( 'Resend email', 'ebook-store' ) . '</a>'; |
| 5677 |
if ( $order_link !== '' ) { |
| 5678 |
echo '<a class="button button-secondary" href="' . esc_url( $order_link ) . '">' . esc_html__( 'View related order', 'ebook-store' ) . '</a>'; |
| 5679 |
} |
| 5680 |
if ( $ebook_link !== '' ) { |
| 5681 |
echo '<a class="button button-secondary" href="' . esc_url( $ebook_link ) . '">' . esc_html__( 'Edit ebook', 'ebook-store' ) . '</a>'; |
| 5682 |
} |
| 5683 |
echo '</div>'; |
| 5684 |
|
| 5685 |
echo '<div class="ebook-email-log__summary">'; |
| 5686 |
echo '<div><span>' . esc_html__( 'Status', 'ebook-store' ) . '</span><strong>' . wp_kses_post( ebook_store_get_email_log_status_badge( $status ) ) . '</strong></div>'; |
| 5687 |
echo '<div><span>' . esc_html__( 'Recipient', 'ebook-store' ) . '</span><strong>' . ( $recipient !== '' ? '<a href="mailto:' . esc_attr( $recipient ) . '">' . esc_html( $recipient ) . '</a>' : esc_html__( 'Not set', 'ebook-store' ) ) . '</strong></div>'; |
| 5688 |
echo '<div><span>' . esc_html__( 'Source', 'ebook-store' ) . '</span><strong>' . esc_html( $source !== '' ? ucfirst( str_replace( '_', ' ', $source ) ) : __( 'Unknown', 'ebook-store' ) ) . '</strong></div>'; |
| 5689 |
echo '<div><span>' . esc_html__( 'Sent at', 'ebook-store' ) . '</span><strong>' . esc_html( $sent_at !== '' ? $sent_at : __( 'Unknown', 'ebook-store' ) ) . '</strong></div>'; |
| 5690 |
echo '<div><span>' . esc_html__( 'Ebook key', 'ebook-store' ) . '</span><strong>' . esc_html( $ebook_key !== '' ? $ebook_key : __( 'Not set', 'ebook-store' ) ) . '</strong></div>'; |
| 5691 |
echo '<div><span>' . esc_html__( 'Resends', 'ebook-store' ) . '</span><strong>' . esc_html( number_format_i18n( $resend_count ) ) . ( $last_resend_at !== '' ? '<small>' . esc_html( sprintf( __( 'Last attempt: %1$s (%2$s)', 'ebook-store' ), $last_resend_at, $last_resend_status !== '' ? $last_resend_status : __( 'unknown', 'ebook-store' ) ) ) . '</small>' : '' ) . '</strong></div>'; |
| 5692 |
echo '</div>'; |
| 5693 |
|
| 5694 |
echo '<div class="ebook-email-log__section">'; |
| 5695 |
echo '<h2>' . esc_html__( 'Message', 'ebook-store' ) . '</h2>'; |
| 5696 |
echo '<p><strong>' . esc_html__( 'Subject', 'ebook-store' ) . ':</strong> ' . esc_html( $subject ) . '</p>'; |
| 5697 |
echo '<div class="ebook-email-log__preview">' . wp_kses_post( $body_html ) . '</div>'; |
| 5698 |
echo '<textarea class="widefat ebook-email-log__code" rows="10" readonly>' . esc_textarea( $body_html ) . '</textarea>'; |
| 5699 |
echo '</div>'; |
| 5700 |
|
| 5701 |
echo '<div class="ebook-email-log__section">'; |
| 5702 |
echo '<h2>' . esc_html__( 'Headers and attachments', 'ebook-store' ) . '</h2>'; |
| 5703 |
echo '<p><strong>' . esc_html__( 'Headers', 'ebook-store' ) . ':</strong></p>'; |
| 5704 |
echo '<textarea class="widefat ebook-email-log__code" rows="4" readonly>' . esc_textarea( implode( "\n", $headers ) ) . '</textarea>'; |
| 5705 |
echo '<p><strong>' . esc_html__( 'Attachments', 'ebook-store' ) . ':</strong></p>'; |
| 5706 |
if ( empty( $attachments ) ) { |
| 5707 |
echo '<p>' . esc_html__( 'No attachments were sent with this email.', 'ebook-store' ) . '</p>'; |
| 5708 |
} else { |
| 5709 |
echo '<ul class="ebook-email-log__attachments">'; |
| 5710 |
foreach ( $attachments as $attachment ) { |
| 5711 |
$label = basename( (string) $attachment ); |
| 5712 |
echo '<li><code>' . esc_html( $label ) . '</code><span>' . esc_html( (string) $attachment ) . '</span></li>'; |
| 5713 |
} |
| 5714 |
echo '</ul>'; |
| 5715 |
} |
| 5716 |
echo '</div>'; |
| 5717 |
|
| 5718 |
echo '<div class="ebook-email-log__section">'; |
| 5719 |
echo '<h2>' . esc_html__( 'Raw order snapshot', 'ebook-store' ) . '</h2>'; |
| 5720 |
echo '<textarea class="widefat ebook-email-log__code" rows="12" readonly>' . esc_textarea( $order_snapshot ) . '</textarea>'; |
| 5721 |
echo '</div>'; |
| 5722 |
|
| 5723 |
echo '<div class="ebook-email-log__section">'; |
| 5724 |
echo '<h2>' . esc_html__( 'Raw email context', 'ebook-store' ) . '</h2>'; |
| 5725 |
echo '<textarea class="widefat ebook-email-log__code" rows="12" readonly>' . esc_textarea( $context_snapshot ) . '</textarea>'; |
| 5726 |
echo '</div>'; |
| 5727 |
echo '</div>'; |
| 5728 |
} |
| 5729 |
|
| 5730 |
/** |
| 5731 |
* Build the list of files to attach to a delivery email. |
| 5732 |
* |
| 5733 |
* Always keeps the core file the caller already resolved (e.g. the encrypted |
| 5734 |
* PDF), and additionally attaches a per-buyer watermarked ePub when the ebook |
| 5735 |
* offers one and ePub watermarking is enabled. So a buyer who opts into email |
| 5736 |
* attachments gets the ePub attached AND watermarked, for every gateway — not |
| 5737 |
* only via the download link. encrypted() caches per order, so this is idempotent. |
| 5738 |
* |
| 5739 |
* @param string|null $core_file Path already chosen for attachment (may be null). |
| 5740 |
* @param int $order_id Order post ID. |
| 5741 |
* @param int $ebook_id Ebook post ID. |
| 5742 |
* @return string|array|null A single path, an array of paths, or null. |
| 5743 |
*/ |
| 5744 |
function ebook_store_collect_delivery_attachments( $core_file, $order_id, $ebook_id ) { |
| 5745 |
$order_id = (int) $order_id; |
| 5746 |
$ebook_id = (int) $ebook_id; |
| 5747 |
$files = array(); |
| 5748 |
|
| 5749 |
if ( is_string( $core_file ) && $core_file !== '' && file_exists( $core_file ) ) { |
| 5750 |
$files[] = $core_file; |
| 5751 |
} |
| 5752 |
|
| 5753 |
if ( $ebook_id > 0 && get_option( 'ebook_store_epub_watermark' ) && function_exists( 'ebook_store_watermark_epub' ) ) { |
| 5754 |
$obj = new EbookStoreEbook( $ebook_id ); |
| 5755 |
if ( ! empty( $obj->files['epub'] ) && file_exists( $obj->files['epub'] ) ) { |
| 5756 |
$obj->order_id = $order_id; |
| 5757 |
$epub = $obj->encrypted( $order_id, 'epub' ); |
| 5758 |
if ( is_string( $epub ) && $epub !== '' && file_exists( $epub ) && ! in_array( $epub, $files, true ) ) { |
| 5759 |
$files[] = $epub; |
| 5760 |
} |
| 5761 |
} |
| 5762 |
} |
| 5763 |
|
| 5764 |
if ( empty( $files ) ) { |
| 5765 |
return null; |
| 5766 |
} |
| 5767 |
|
| 5768 |
return count( $files ) === 1 ? $files[0] : $files; |
| 5769 |
} |
| 5770 |
|
| 5771 |
function ebook_email_delivery($post_id = null) { |
| 5772 |
$QSWPOptions = new QSWPOptions(); |
| 5773 |
global $ebook_email_delivery, $formData; |
| 5774 |
if (@$ebook_email_delivery['done'] == 1) { |
| 5775 |
return; |
| 5776 |
} |
| 5777 |
$ebook_email_delivery['done'] = 1; |
| 5778 |
|
| 5779 |
if ( isset( $formData ) ) { |
| 5780 |
if ( is_string( $formData ) && $formData !== '' ) { |
| 5781 |
$decoded_form_data = json_decode( $formData, true ); |
| 5782 |
$formData = is_array( $decoded_form_data ) ? $decoded_form_data : array(); |
| 5783 |
} elseif ( is_array( $formData ) ) { |
| 5784 |
$formData = $formData; |
| 5785 |
} else { |
| 5786 |
$formData = array(); |
| 5787 |
} |
| 5788 |
} else { |
| 5789 |
$formData = array(); |
| 5790 |
} |
| 5791 |
|
| 5792 |
// error_log(var_export(debug_backtrace(),true)); |
| 5793 |
$ebook_email_delivery['order']['download_links'] = implode("<br />",ebook_download_links($ebook_email_delivery['order'])); |
| 5794 |
$ebook_email_delivery['order']['downloadlinks'] = implode("<br />",ebook_download_links($ebook_email_delivery['order'])); |
| 5795 |
$ebook_email_delivery['order']['ebook_bonus'] = (implode("<br />",ebook_download_links_bonus($ebook_email_delivery['order'])) != '' ? implode("<br />",ebook_download_links_bonus($ebook_email_delivery['order'])) : __('None','ebook-store')); |
| 5796 |
|
| 5797 |
$order_total = isset( $ebook_email_delivery['order']['total'] ) ? $ebook_email_delivery['order']['total'] : false; |
| 5798 |
$order_tax = isset( $ebook_email_delivery['order']['tax'] ) ? $ebook_email_delivery['order']['tax'] : 0; |
| 5799 |
$order_gross = isset( $ebook_email_delivery['order']['mc_gross'] ) ? $ebook_email_delivery['order']['mc_gross'] : 0; |
| 5800 |
|
| 5801 |
if ( is_array( $order_total ) ) { |
| 5802 |
$order_total = reset( $order_total ); |
| 5803 |
} |
| 5804 |
if ( is_array( $order_tax ) ) { |
| 5805 |
$order_tax = reset( $order_tax ); |
| 5806 |
} |
| 5807 |
if ( is_array( $order_gross ) ) { |
| 5808 |
$order_gross = reset( $order_gross ); |
| 5809 |
} |
| 5810 |
|
| 5811 |
if ( $order_total === false || $order_total === '' ) { |
| 5812 |
$order_total = (float) $order_gross + (float) $order_tax; |
| 5813 |
} |
| 5814 |
|
| 5815 |
$ebook_email_delivery['order']['total'] = toMoney( $order_total ); |
| 5816 |
$ebook_email_delivery['order']['tax'] = toMoney( $order_tax ); |
| 5817 |
|
| 5818 |
if (!isset($ebook_email_delivery['order']['password'])) { |
| 5819 |
$ebook_email_delivery['order']['password'] = ebook_store_get_password_for_order( |
| 5820 |
isset($ebook_email_delivery['order']['order_id']) ? (int)$ebook_email_delivery['order']['order_id'] : 0, |
| 5821 |
array( |
| 5822 |
'payer_email' => isset($ebook_email_delivery['order']['payer_email']) ? $ebook_email_delivery['order']['payer_email'] : '', |
| 5823 |
'password' => '', |
| 5824 |
'md5_nonce' => isset($ebook_email_delivery['order']['md5_nonce']) ? $ebook_email_delivery['order']['md5_nonce'] : '', |
| 5825 |
'ebook_key' => isset($ebook_email_delivery['order']['ebook_key']) ? $ebook_email_delivery['order']['ebook_key'] : '', |
| 5826 |
) |
| 5827 |
); |
| 5828 |
} |
| 5829 |
|
| 5830 |
@$attachmentFile = $ebook_email_delivery['attachment'][0]['file']; |
| 5831 |
$ebook_email_delivery['order']['filesize'] = ( $attachmentFile && file_exists( $attachmentFile ) ) ? ebook_store_human_filesize(filesize($attachmentFile)) : ''; |
| 5832 |
if (get_option('attach_files') != 1) { |
| 5833 |
$attachmentFile = null; |
| 5834 |
} else { |
| 5835 |
// Attachments are on: attach the core file AND a watermarked ePub if the |
| 5836 |
// ebook offers one, for every gateway (not only the download link). |
| 5837 |
$ebook_attach_order = isset( $ebook_email_delivery['order']['order_id'] ) ? $ebook_email_delivery['order']['order_id'] : $post_id; |
| 5838 |
if ( is_array( $ebook_attach_order ) ) { $ebook_attach_order = reset( $ebook_attach_order ); } |
| 5839 |
if ( ! $ebook_attach_order ) { $ebook_attach_order = $post_id; } |
| 5840 |
$ebook_attach_id = isset( $ebook_email_delivery['order']['ebook'] ) ? $ebook_email_delivery['order']['ebook'] : 0; |
| 5841 |
if ( is_array( $ebook_attach_id ) ) { $ebook_attach_id = reset( $ebook_attach_id ); } |
| 5842 |
$attachmentFile = ebook_store_collect_delivery_attachments( $attachmentFile, (int) $ebook_attach_order, (int) $ebook_attach_id ); |
| 5843 |
} |
| 5844 |
|
| 5845 |
|
| 5846 |
//error_reporting(E_ALL); |
| 5847 |
foreach ($ebook_email_delivery['order'] as $k => $v) { |
| 5848 |
if (is_array($v)) { |
| 5849 |
$v = $v[0]; |
| 5850 |
} |
| 5851 |
$ebook_email_delivery['text'] = str_replace('%%' . $k . '%%', $v, $ebook_email_delivery['text']); |
| 5852 |
} |
| 5853 |
|
| 5854 |
|
| 5855 |
$ebook_email_delivery['text'] = nl2br($ebook_email_delivery['text']); |
| 5856 |
//$ebook_email_delivery['text'] = apply_filters('the_content',$ebook_email_delivery['text']); |
| 5857 |
//error_log('ebook_email_delivery ' . print_r($ebook_email_delivery,true)); |
| 5858 |
add_filter( 'wp_mail_content_type', 'ebook_set_content_type' ); |
| 5859 |
$ebook_email_delivery['mail_result'] = false; |
| 5860 |
if (floatval(get_option('email_delivery',$QSWPOptions->email_delivery)) == 1) { |
| 5861 |
//echo "Sending mail, data: " . implode("; ", $ebook_email_delivery); |
| 5862 |
if (floatval(get_option('kindleDelivery',$QSWPOptions->kindleDelivery)) == 1) { |
| 5863 |
if ( ! empty( $formData['kindle_email'] ) && strpos($formData['kindle_email'], '@kindle.com')) { |
| 5864 |
// if (1) { |
| 5865 |
$formData['kindle_email'] = filter_var($formData['kindle_email'], FILTER_VALIDATE_EMAIL); |
| 5866 |
$ebook_email_delivery['to'] = $formData['kindle_email']; |
| 5867 |
} else { |
| 5868 |
return true; //not a kind |
| 5869 |
} |
| 5870 |
} |
| 5871 |
if ( ! empty( $formData['gift_email'] ) ) { |
| 5872 |
$formData['gift_email'] = filter_var($formData['gift_email'], FILTER_VALIDATE_EMAIL); |
| 5873 |
$ebook_email_delivery['to'] = $formData['gift_email']; |
| 5874 |
} |
| 5875 |
$headers = array('Content-Type: text/html; charset=UTF-8'); |
| 5876 |
|
| 5877 |
$mail_result = wp_mail($ebook_email_delivery['to'],$ebook_email_delivery['subject'], $ebook_email_delivery['text'],$headers,$attachmentFile); |
| 5878 |
$ebook_email_delivery['mail_result'] = (bool) $mail_result; |
| 5879 |
|
| 5880 |
ebook_store_create_email_log_entry( |
| 5881 |
array( |
| 5882 |
'to' => isset( $ebook_email_delivery['to'] ) ? $ebook_email_delivery['to'] : '', |
| 5883 |
'subject' => isset( $ebook_email_delivery['subject'] ) ? $ebook_email_delivery['subject'] : '', |
| 5884 |
'body' => isset( $ebook_email_delivery['text'] ) ? $ebook_email_delivery['text'] : '', |
| 5885 |
'headers' => $headers, |
| 5886 |
'attachments' => $attachmentFile, |
| 5887 |
'result' => $mail_result, |
| 5888 |
'source' => isset( $ebook_email_delivery['source'] ) ? $ebook_email_delivery['source'] : 'standalone', |
| 5889 |
'related_order_type' => isset( $ebook_email_delivery['related_order_type'] ) ? $ebook_email_delivery['related_order_type'] : '', |
| 5890 |
'related_order_id' => isset( $ebook_email_delivery['order']['order_id'] ) ? $ebook_email_delivery['order']['order_id'] : 0, |
| 5891 |
'ebook_id' => isset( $ebook_email_delivery['order']['ebook'] ) ? $ebook_email_delivery['order']['ebook'] : 0, |
| 5892 |
'ebook_key' => isset( $ebook_email_delivery['order']['ebook_key'] ) ? $ebook_email_delivery['order']['ebook_key'] : '', |
| 5893 |
'gateway' => isset( $ebook_email_delivery['order']['gateway'] ) ? $ebook_email_delivery['order']['gateway'] : '', |
| 5894 |
'order' => isset( $ebook_email_delivery['order'] ) ? $ebook_email_delivery['order'] : array(), |
| 5895 |
'context' => $ebook_email_delivery, |
| 5896 |
) |
| 5897 |
); |
| 5898 |
} |
| 5899 |
} |
| 5900 |
|
| 5901 |
function ebook_set_content_type( $content_type ){ |
| 5902 |
return 'text/html'; |
| 5903 |
} |
| 5904 |
function ebook_attachment($post_id, $ignoreSetting = false) { |
| 5905 |
if (get_option('attach_files') == 1 || $ignoreSetting == true) { |
| 5906 |
$attachment = get_post_meta($post_id,'ebook_wp_custom_attachment'); |
| 5907 |
if (WP_DEBUG == true) error_log('attachment - ' . print_r($attachment,true)); |
| 5908 |
return $attachment; |
| 5909 |
} |
| 5910 |
} |
| 5911 |
|
| 5912 |
function ebook_store_set_email_log_columns( $columns ) { |
| 5913 |
return array( |
| 5914 |
'cb' => '<input type="checkbox" />', |
| 5915 |
'title' => __( 'Email', 'ebook-store' ), |
| 5916 |
'recipient' => __( 'Recipient', 'ebook-store' ), |
| 5917 |
'source' => __( 'Source', 'ebook-store' ), |
| 5918 |
'related_order' => __( 'Order', 'ebook-store' ), |
| 5919 |
'ebook_reference' => __( 'Ebook', 'ebook-store' ), |
| 5920 |
'status' => __( 'Status', 'ebook-store' ), |
| 5921 |
'resends' => __( 'Resends', 'ebook-store' ), |
| 5922 |
'date' => __( 'Logged', 'ebook-store' ), |
| 5923 |
); |
| 5924 |
} |
| 5925 |
|
| 5926 |
function ebook_store_email_log_columns_output( $column, $post_id ) { |
| 5927 |
switch ( $column ) { |
| 5928 |
case 'recipient': |
| 5929 |
$recipient = (string) get_post_meta( $post_id, 'recipient', true ); |
| 5930 |
echo $recipient !== '' ? '<a href="mailto:' . esc_attr( $recipient ) . '">' . esc_html( $recipient ) . '</a>' : '—'; |
| 5931 |
break; |
| 5932 |
case 'source': |
| 5933 |
$source = (string) get_post_meta( $post_id, 'source', true ); |
| 5934 |
echo esc_html( $source !== '' ? ucfirst( str_replace( '_', ' ', $source ) ) : __( 'Unknown', 'ebook-store' ) ); |
| 5935 |
break; |
| 5936 |
case 'related_order': |
| 5937 |
$order_id = (int) get_post_meta( $post_id, 'related_order_id', true ); |
| 5938 |
$link = ebook_store_get_email_log_order_edit_link( $post_id ); |
| 5939 |
if ( $order_id < 1 ) { |
| 5940 |
echo '—'; |
| 5941 |
break; |
| 5942 |
} |
| 5943 |
echo $link !== '' ? '<a href="' . esc_url( $link ) . '">#' . esc_html( $order_id ) . '</a>' : '#' . esc_html( $order_id ); |
| 5944 |
break; |
| 5945 |
case 'ebook_reference': |
| 5946 |
$ebook_id = (int) get_post_meta( $post_id, 'ebook_id', true ); |
| 5947 |
$link = ebook_store_get_email_log_ebook_edit_link( $post_id ); |
| 5948 |
if ( $ebook_id < 1 ) { |
| 5949 |
echo '—'; |
| 5950 |
break; |
| 5951 |
} |
| 5952 |
$title = get_the_title( $ebook_id ); |
| 5953 |
echo $link !== '' ? '<a href="' . esc_url( $link ) . '">' . esc_html( $title !== '' ? $title : '#' . $ebook_id ) . '</a>' : esc_html( $title !== '' ? $title : '#' . $ebook_id ); |
| 5954 |
break; |
| 5955 |
case 'status': |
| 5956 |
echo wp_kses_post( ebook_store_get_email_log_status_badge( get_post_meta( $post_id, 'status', true ) ) ); |
| 5957 |
break; |
| 5958 |
case 'resends': |
| 5959 |
$resend_count = (int) get_post_meta( $post_id, 'resend_count', true ); |
| 5960 |
$last_resend_at = (string) get_post_meta( $post_id, 'last_resend_at', true ); |
| 5961 |
echo '<strong>' . esc_html( number_format_i18n( $resend_count ) ) . '</strong>'; |
| 5962 |
if ( $last_resend_at !== '' ) { |
| 5963 |
echo '<div class="ebook-email-log-list__meta">' . esc_html( $last_resend_at ) . '</div>'; |
| 5964 |
} |
| 5965 |
break; |
| 5966 |
} |
| 5967 |
} |
| 5968 |
|
| 5969 |
function ebook_store_email_log_row_actions( $actions, $post ) { |
| 5970 |
if ( ! $post || $post->post_type !== 'ebook_email_log' ) { |
| 5971 |
return $actions; |
| 5972 |
} |
| 5973 |
|
| 5974 |
$actions['resend_email'] = '<a href="' . esc_url( ebook_store_get_email_log_resend_url( $post->ID ) ) . '">' . esc_html__( 'Resend', 'ebook-store' ) . '</a>'; |
| 5975 |
|
| 5976 |
$order_link = ebook_store_get_email_log_order_edit_link( $post->ID ); |
| 5977 |
if ( $order_link !== '' ) { |
| 5978 |
$actions['view_related_order'] = '<a href="' . esc_url( $order_link ) . '">' . esc_html__( 'View order', 'ebook-store' ) . '</a>'; |
| 5979 |
} |
| 5980 |
|
| 5981 |
return $actions; |
| 5982 |
} |
| 5983 |
|
| 5984 |
function ebook_store_remove_email_log_submit_box() { |
| 5985 |
remove_meta_box( 'submitdiv', 'ebook_email_log', 'side' ); |
| 5986 |
} |
| 5987 |
|
| 5988 |
function ebook_store_cleanup_email_log_submenu() { |
| 5989 |
remove_submenu_page( 'edit.php?post_type=ebook', 'post-new.php?post_type=ebook_email_log' ); |
| 5990 |
} |
| 5991 |
|
| 5992 |
function ebook_store_redirect_new_email_log_entry() { |
| 5993 |
global $pagenow; |
| 5994 |
|
| 5995 |
if ( ! is_admin() || $pagenow !== 'post-new.php' ) { |
| 5996 |
return; |
| 5997 |
} |
| 5998 |
|
| 5999 |
if ( empty( $_GET['post_type'] ) || wp_unslash( $_GET['post_type'] ) !== 'ebook_email_log' ) { |
| 6000 |
return; |
| 6001 |
} |
| 6002 |
|
| 6003 |
wp_safe_redirect( admin_url( 'edit.php?post_type=ebook_email_log' ) ); |
| 6004 |
exit; |
| 6005 |
} |
| 6006 |
|
| 6007 |
function ebook_store_render_email_log_admin_styles() { |
| 6008 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 6009 |
if ( ! $screen || $screen->post_type !== 'ebook_email_log' ) { |
| 6010 |
return; |
| 6011 |
} |
| 6012 |
|
| 6013 |
echo '<style> |
| 6014 |
.post-type-ebook_email_log #titlediv, |
| 6015 |
.post-type-ebook_email_log #minor-publishing, |
| 6016 |
.post-type-ebook_email_log #misc-publishing-actions, |
| 6017 |
.post-type-ebook_email_log #major-publishing-actions .open-if-no-js { |
| 6018 |
display: none; |
| 6019 |
} |
| 6020 |
.post-type-ebook_email_log .page-title-action { |
| 6021 |
display: none; |
| 6022 |
} |
| 6023 |
.post-type-ebook_email_log #major-publishing-actions { |
| 6024 |
padding: 0; |
| 6025 |
border-top: 0; |
| 6026 |
} |
| 6027 |
.ebook-email-log { |
| 6028 |
display: grid; |
| 6029 |
gap: 16px; |
| 6030 |
} |
| 6031 |
.ebook-email-log__actions { |
| 6032 |
display: flex; |
| 6033 |
flex-wrap: wrap; |
| 6034 |
gap: 10px; |
| 6035 |
} |
| 6036 |
.ebook-email-log__summary { |
| 6037 |
display: grid; |
| 6038 |
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); |
| 6039 |
gap: 12px; |
| 6040 |
} |
| 6041 |
.ebook-email-log__summary > div, |
| 6042 |
.ebook-email-log__section { |
| 6043 |
background: #fff; |
| 6044 |
border: 1px solid #dcdcde; |
| 6045 |
border-radius: 14px; |
| 6046 |
padding: 16px; |
| 6047 |
} |
| 6048 |
.ebook-email-log__summary span { |
| 6049 |
display: block; |
| 6050 |
margin-bottom: 6px; |
| 6051 |
color: #646970; |
| 6052 |
font-size: 12px; |
| 6053 |
text-transform: uppercase; |
| 6054 |
letter-spacing: .04em; |
| 6055 |
} |
| 6056 |
.ebook-email-log__summary strong { |
| 6057 |
display: block; |
| 6058 |
font-size: 14px; |
| 6059 |
line-height: 1.5; |
| 6060 |
} |
| 6061 |
.ebook-email-log__summary small { |
| 6062 |
display: block; |
| 6063 |
margin-top: 6px; |
| 6064 |
color: #646970; |
| 6065 |
font-weight: 400; |
| 6066 |
} |
| 6067 |
.ebook-email-log__section h2 { |
| 6068 |
margin: 0 0 12px; |
| 6069 |
font-size: 15px; |
| 6070 |
} |
| 6071 |
.ebook-email-log__preview { |
| 6072 |
max-height: 360px; |
| 6073 |
overflow: auto; |
| 6074 |
padding: 16px; |
| 6075 |
background: #f6f7f7; |
| 6076 |
border: 1px solid #dcdcde; |
| 6077 |
border-radius: 12px; |
| 6078 |
margin-bottom: 12px; |
| 6079 |
} |
| 6080 |
.ebook-email-log__code { |
| 6081 |
font-family: Consolas, Monaco, monospace; |
| 6082 |
font-size: 12px; |
| 6083 |
} |
| 6084 |
.ebook-email-log__attachments { |
| 6085 |
margin: 0; |
| 6086 |
padding: 0; |
| 6087 |
list-style: none; |
| 6088 |
display: grid; |
| 6089 |
gap: 10px; |
| 6090 |
} |
| 6091 |
.ebook-email-log__attachments li { |
| 6092 |
display: grid; |
| 6093 |
gap: 4px; |
| 6094 |
padding: 10px 12px; |
| 6095 |
background: #f6f7f7; |
| 6096 |
border-radius: 10px; |
| 6097 |
} |
| 6098 |
.ebook-email-log-badge { |
| 6099 |
display: inline-flex; |
| 6100 |
align-items: center; |
| 6101 |
padding: 4px 10px; |
| 6102 |
border-radius: 999px; |
| 6103 |
font-size: 12px; |
| 6104 |
font-weight: 600; |
| 6105 |
} |
| 6106 |
.ebook-email-log-badge--sent { |
| 6107 |
background: #e7f7ed; |
| 6108 |
color: #116b38; |
| 6109 |
} |
| 6110 |
.ebook-email-log-badge--failed { |
| 6111 |
background: #fdeaea; |
| 6112 |
color: #b42318; |
| 6113 |
} |
| 6114 |
.ebook-email-log-list__meta { |
| 6115 |
color: #646970; |
| 6116 |
font-size: 12px; |
| 6117 |
margin-top: 4px; |
| 6118 |
} |
| 6119 |
</style>'; |
| 6120 |
} |
| 6121 |
|
| 6122 |
function ebook_store_email_log_admin_notice() { |
| 6123 |
if ( ! is_admin() || empty( $_GET['ebook_store_email_log_notice'] ) ) { |
| 6124 |
return; |
| 6125 |
} |
| 6126 |
|
| 6127 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 6128 |
if ( ! $screen || $screen->post_type !== 'ebook_email_log' ) { |
| 6129 |
return; |
| 6130 |
} |
| 6131 |
|
| 6132 |
$notice = sanitize_key( wp_unslash( $_GET['ebook_store_email_log_notice'] ) ); |
| 6133 |
if ( ! in_array( $notice, array( 'resent', 'resend_failed' ), true ) ) { |
| 6134 |
return; |
| 6135 |
} |
| 6136 |
|
| 6137 |
$class = $notice === 'resent' ? 'notice-success' : 'notice-error'; |
| 6138 |
$message = $notice === 'resent' |
| 6139 |
? __( 'The email was resent and logged as a new entry.', 'ebook-store' ) |
| 6140 |
: __( 'The resend attempt failed. Check your mail configuration and the attachment paths.', 'ebook-store' ); |
| 6141 |
|
| 6142 |
echo '<div class="notice ' . esc_attr( $class ) . ' is-dismissible"><p>' . esc_html( $message ) . '</p></div>'; |
| 6143 |
} |
| 6144 |
|
| 6145 |
function ebook_store_order_admin_notice() { |
| 6146 |
if ( ! is_admin() || empty( $_GET['ebook_store_order_notice'] ) ) { |
| 6147 |
return; |
| 6148 |
} |
| 6149 |
|
| 6150 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 6151 |
if ( ! $screen || $screen->post_type !== 'ebook_order' ) { |
| 6152 |
return; |
| 6153 |
} |
| 6154 |
|
| 6155 |
$notice = sanitize_key( wp_unslash( $_GET['ebook_store_order_notice'] ) ); |
| 6156 |
$messages = array( |
| 6157 |
'downloads_reset' => array( |
| 6158 |
'class' => 'notice-success', |
| 6159 |
'text' => __( 'The order download counter was reset to 0.', 'ebook-store' ), |
| 6160 |
), |
| 6161 |
'delivery_resent' => array( |
| 6162 |
'class' => 'notice-success', |
| 6163 |
'text' => __( 'The delivery email was sent again and logged as a new email entry.', 'ebook-store' ), |
| 6164 |
), |
| 6165 |
'delivery_failed' => array( |
| 6166 |
'class' => 'notice-error', |
| 6167 |
'text' => __( 'The delivery email resend failed. Check the order email address, attachment file, and mail configuration.', 'ebook-store' ), |
| 6168 |
), |
| 6169 |
'refund_succeeded' => array( |
| 6170 |
'class' => 'notice-success', |
| 6171 |
'text' => __( 'The payment was refunded successfully.', 'ebook-store' ), |
| 6172 |
), |
| 6173 |
'refund_failed' => array( |
| 6174 |
'class' => 'notice-error', |
| 6175 |
'text' => __( 'The payment refund failed.', 'ebook-store' ), |
| 6176 |
), |
| 6177 |
); |
| 6178 |
|
| 6179 |
if ( ! isset( $messages[ $notice ] ) ) { |
| 6180 |
return; |
| 6181 |
} |
| 6182 |
|
| 6183 |
$message = $messages[ $notice ]['text']; |
| 6184 |
if ( ! empty( $_GET['ebook_store_order_message'] ) ) { |
| 6185 |
$message = sanitize_text_field( wp_unslash( $_GET['ebook_store_order_message'] ) ); |
| 6186 |
} |
| 6187 |
|
| 6188 |
echo '<div class="notice ' . esc_attr( $messages[ $notice ]['class'] ) . ' is-dismissible"><p>' . esc_html( $message ) . '</p></div>'; |
| 6189 |
} |
| 6190 |
|
| 6191 |
function ebook_store_get_order_admin_list_url( $notice = '', $args = array() ) { |
| 6192 |
$args = array( |
| 6193 |
'post_type' => 'ebook_order', |
| 6194 |
) + (array) $args; |
| 6195 |
|
| 6196 |
if ( $notice !== '' ) { |
| 6197 |
$args['ebook_store_order_notice'] = $notice; |
| 6198 |
} |
| 6199 |
|
| 6200 |
return add_query_arg( $args, admin_url( 'edit.php' ) ); |
| 6201 |
} |
| 6202 |
|
| 6203 |
function ebook_store_mark_order_refunded( $order_id, $gateway, $refund_id, $refund_status, $amount = '', $currency = '' ) { |
| 6204 |
$order_id = absint( $order_id ); |
| 6205 |
if ( $order_id < 1 ) { |
| 6206 |
return; |
| 6207 |
} |
| 6208 |
|
| 6209 |
update_post_meta( $order_id, 'payment_status', 'Refunded' ); |
| 6210 |
update_post_meta( $order_id, 'refund_gateway', sanitize_key( (string) $gateway ) ); |
| 6211 |
update_post_meta( $order_id, 'refund_gateway_id', sanitize_text_field( (string) $refund_id ) ); |
| 6212 |
update_post_meta( $order_id, 'refund_gateway_status', sanitize_text_field( (string) $refund_status ) ); |
| 6213 |
update_post_meta( $order_id, 'refund_date', current_time( 'mysql' ) ); |
| 6214 |
|
| 6215 |
if ( $amount !== '' && is_numeric( $amount ) ) { |
| 6216 |
update_post_meta( $order_id, 'refund_amount', (string) $amount ); |
| 6217 |
} |
| 6218 |
|
| 6219 |
if ( $currency !== '' ) { |
| 6220 |
update_post_meta( $order_id, 'refund_currency', strtoupper( sanitize_text_field( (string) $currency ) ) ); |
| 6221 |
} |
| 6222 |
} |
| 6223 |
|
| 6224 |
function ebook_store_refund_stripe_order( $order_id ) { |
| 6225 |
$payment_intent_id = trim( (string) get_post_meta( $order_id, 'stripe_payment_intent_id', true ) ); |
| 6226 |
if ( $payment_intent_id === '' ) { |
| 6227 |
return new WP_Error( 'ebook_store_missing_stripe_payment_intent', __( 'This Stripe order is missing its payment intent ID.', 'ebook-store' ) ); |
| 6228 |
} |
| 6229 |
|
| 6230 |
$bootstrap = ebook_store_bootstrap_stripe_sdk(); |
| 6231 |
if ( is_wp_error( $bootstrap ) ) { |
| 6232 |
return $bootstrap; |
| 6233 |
} |
| 6234 |
|
| 6235 |
try { |
| 6236 |
$refund = \Stripe\Refund::create( |
| 6237 |
array( |
| 6238 |
'payment_intent' => $payment_intent_id, |
| 6239 |
'reason' => 'requested_by_customer', |
| 6240 |
'metadata' => array( |
| 6241 |
'ebook_order_id' => (string) $order_id, |
| 6242 |
), |
| 6243 |
) |
| 6244 |
); |
| 6245 |
} catch ( Exception $e ) { |
| 6246 |
return new WP_Error( 'ebook_store_stripe_refund_failed', $e->getMessage() ); |
| 6247 |
} |
| 6248 |
|
| 6249 |
$refund_amount = isset( $refund->amount ) ? ((float) $refund->amount / 100) : (float) get_post_meta( $order_id, 'mc_gross', true ); |
| 6250 |
$refund_currency = isset( $refund->currency ) ? strtoupper( (string) $refund->currency ) : (string) get_post_meta( $order_id, 'mc_currency', true ); |
| 6251 |
$refund_status = isset( $refund->status ) ? (string) $refund->status : 'succeeded'; |
| 6252 |
$refund_id = isset( $refund->id ) ? (string) $refund->id : ''; |
| 6253 |
|
| 6254 |
ebook_store_mark_order_refunded( $order_id, 'stripe', $refund_id, $refund_status, $refund_amount, $refund_currency ); |
| 6255 |
|
| 6256 |
return array( |
| 6257 |
'gateway' => 'stripe', |
| 6258 |
'refund_id' => $refund_id, |
| 6259 |
'status' => $refund_status, |
| 6260 |
'amount' => $refund_amount, |
| 6261 |
'currency' => $refund_currency, |
| 6262 |
); |
| 6263 |
} |
| 6264 |
|
| 6265 |
function ebook_store_refund_adyen_order( $order_id ) { |
| 6266 |
$psp_reference = trim( (string) get_post_meta( $order_id, 'adyen_psp_reference', true ) ); |
| 6267 |
if ( $psp_reference === '' ) { |
| 6268 |
return new WP_Error( 'ebook_store_missing_adyen_psp_reference', __( 'This Adyen order is missing its PSP reference.', 'ebook-store' ) ); |
| 6269 |
} |
| 6270 |
|
| 6271 |
$amount = (float) get_post_meta( $order_id, 'mc_gross', true ); |
| 6272 |
$currency = strtoupper( trim( (string) get_post_meta( $order_id, 'mc_currency', true ) ) ); |
| 6273 |
if ( $amount <= 0 || $currency === '' ) { |
| 6274 |
return new WP_Error( 'ebook_store_invalid_adyen_refund_amount', __( 'This Adyen order is missing a valid refund amount or currency.', 'ebook-store' ) ); |
| 6275 |
} |
| 6276 |
|
| 6277 |
$response = ebook_store_adyen_request( |
| 6278 |
'POST', |
| 6279 |
'payments/' . rawurlencode( $psp_reference ) . '/refunds', |
| 6280 |
array( |
| 6281 |
'merchantAccount' => trim( (string) get_option( 'adyen_merchant_account', '' ) ), |
| 6282 |
'amount' => array( |
| 6283 |
'currency' => $currency, |
| 6284 |
'value' => (int) round( $amount * 100 ), |
| 6285 |
), |
| 6286 |
'reference' => 'refund-' . $order_id . '-' . wp_generate_password( 8, false, false ), |
| 6287 |
), |
| 6288 |
array( |
| 6289 |
'Idempotency-Key' => wp_generate_uuid4(), |
| 6290 |
) |
| 6291 |
); |
| 6292 |
|
| 6293 |
if ( is_wp_error( $response ) ) { |
| 6294 |
return $response; |
| 6295 |
} |
| 6296 |
|
| 6297 |
$refund_id = ! empty( $response['pspReference'] ) ? (string) $response['pspReference'] : ''; |
| 6298 |
ebook_store_mark_order_refunded( $order_id, 'adyen', $refund_id, 'received', $amount, $currency ); |
| 6299 |
|
| 6300 |
return array( |
| 6301 |
'gateway' => 'adyen', |
| 6302 |
'refund_id' => $refund_id, |
| 6303 |
'status' => 'received', |
| 6304 |
'amount' => $amount, |
| 6305 |
'currency' => $currency, |
| 6306 |
); |
| 6307 |
} |
| 6308 |
|
| 6309 |
function ebook_store_process_order_refund( $order_id ) { |
| 6310 |
$order_id = absint( $order_id ); |
| 6311 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 6312 |
return new WP_Error( 'ebook_store_invalid_order', __( 'Invalid ebook order.', 'ebook-store' ) ); |
| 6313 |
} |
| 6314 |
|
| 6315 |
$gateway = ebook_store_get_order_refund_gateway( $order_id ); |
| 6316 |
if ( $gateway === '' ) { |
| 6317 |
return new WP_Error( 'ebook_store_refund_not_supported', __( 'Refund is not available for this order or payment method.', 'ebook-store' ) ); |
| 6318 |
} |
| 6319 |
|
| 6320 |
if ( $gateway === 'stripe' ) { |
| 6321 |
return ebook_store_refund_stripe_order( $order_id ); |
| 6322 |
} |
| 6323 |
|
| 6324 |
if ( $gateway === 'adyen' ) { |
| 6325 |
return ebook_store_refund_adyen_order( $order_id ); |
| 6326 |
} |
| 6327 |
|
| 6328 |
return new WP_Error( 'ebook_store_refund_not_supported', __( 'Refund is not available for this payment method.', 'ebook-store' ) ); |
| 6329 |
} |
| 6330 |
|
| 6331 |
function ebook_store_prepare_order_delivery_email( $order_id ) { |
| 6332 |
$order_id = absint( $order_id ); |
| 6333 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 6334 |
return new WP_Error( 'ebook_store_invalid_order', __( 'Invalid ebook order.', 'ebook-store' ) ); |
| 6335 |
} |
| 6336 |
|
| 6337 |
$order_meta = get_post_meta( $order_id, null, true ); |
| 6338 |
if ( ! is_array( $order_meta ) || empty( $order_meta['ebook'][0] ) ) { |
| 6339 |
return new WP_Error( 'ebook_store_invalid_order_meta', __( 'This order is missing ebook delivery data.', 'ebook-store' ) ); |
| 6340 |
} |
| 6341 |
|
| 6342 |
$ebook_id = absint( $order_meta['ebook'][0] ); |
| 6343 |
if ( $ebook_id < 1 ) { |
| 6344 |
return new WP_Error( 'ebook_store_invalid_order_ebook', __( 'This order is not linked to a valid ebook.', 'ebook-store' ) ); |
| 6345 |
} |
| 6346 |
|
| 6347 |
$QSWPOptions = new QSWPOptions(); |
| 6348 |
$attachment_file = (string) get_post_meta( $order_id, 'encrypted_pdf', true ); |
| 6349 |
if ( $attachment_file === '' ) { |
| 6350 |
// Fall back to a watermarked EPUB copy if one was generated for this order. |
| 6351 |
$attachment_file = (string) get_post_meta( $order_id, 'encrypted_epub', true ); |
| 6352 |
} |
| 6353 |
if ( $attachment_file === '' || ! file_exists( $attachment_file ) ) { |
| 6354 |
$attachment = ebook_attachment( $ebook_id, true ); |
| 6355 |
$attachment_file = isset( $attachment[0]['file'] ) ? (string) $attachment[0]['file'] : ''; |
| 6356 |
} |
| 6357 |
|
| 6358 |
$order_meta['order_id'][0] = $order_id; |
| 6359 |
$order_meta['downloadlink'][0] = ebook_download_link( $order_meta ); |
| 6360 |
|
| 6361 |
// The delivery-email template substitutes %%downloadlink_html%%. The checkout |
| 6362 |
// path builds it, but the admin resend path (which rebuilds the order array |
| 6363 |
// from post meta) did not — so a resent email showed the raw placeholder and |
| 6364 |
// the buyer had no way to download. Build it here too. |
| 6365 |
$delivery_ebook = new EbookStoreEbook( $ebook_id ); |
| 6366 |
// Plugin order: give link() the ebook_key credentials, otherwise the non-PDF |
| 6367 |
// format links come out as wc_order_download URLs with no order key, which |
| 6368 |
// the download endpoint (rightly) refuses. |
| 6369 |
$delivery_ebook->ebook_key = isset( $order_meta['ebook_key'][0] ) ? (string) $order_meta['ebook_key'][0] : ''; |
| 6370 |
$delivery_ebook->md5_nonce = isset( $order_meta['md5_nonce'][0] ) ? (string) $order_meta['md5_nonce'][0] : ''; |
| 6371 |
$delivery_ebook->order_id = $order_id; |
| 6372 |
$delivery_ebook->setLink['pdf'] = $order_meta['downloadlink'][0]; |
| 6373 |
$order_meta['downloadlink_html'][0] = $delivery_ebook->format_links(); |
| 6374 |
|
| 6375 |
$recipient = isset( $order_meta['payer_email'][0] ) ? sanitize_email( (string) $order_meta['payer_email'][0] ) : ''; |
| 6376 |
if ( $recipient === '' ) { |
| 6377 |
return new WP_Error( 'ebook_store_missing_order_email', __( 'This order does not have a valid delivery email address.', 'ebook-store' ) ); |
| 6378 |
} |
| 6379 |
$email_template = ebook_store_get_effective_delivery_email_template( $ebook_id ); |
| 6380 |
|
| 6381 |
return array( |
| 6382 |
'to' => $recipient, |
| 6383 |
'subject' => $email_template['subject'], |
| 6384 |
'text' => $email_template['text'], |
| 6385 |
'attachment' => array( |
| 6386 |
array( |
| 6387 |
'file' => $attachment_file, |
| 6388 |
), |
| 6389 |
), |
| 6390 |
'order' => $order_meta, |
| 6391 |
'source' => 'admin_resend', |
| 6392 |
'related_order_type' => 'ebook_order', |
| 6393 |
); |
| 6394 |
} |
| 6395 |
|
| 6396 |
function ebook_store_reset_order_downloads() { |
| 6397 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 6398 |
wp_die( esc_html__( 'You are not allowed to reset order downloads.', 'ebook-store' ) ); |
| 6399 |
} |
| 6400 |
|
| 6401 |
$order_id = isset( $_GET['order_id'] ) ? absint( wp_unslash( $_GET['order_id'] ) ) : 0; |
| 6402 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 6403 |
wp_die( esc_html__( 'Invalid ebook order.', 'ebook-store' ) ); |
| 6404 |
} |
| 6405 |
|
| 6406 |
check_admin_referer( 'ebook_store_reset_order_downloads_' . $order_id ); |
| 6407 |
update_post_meta( $order_id, 'downloads', 0 ); |
| 6408 |
|
| 6409 |
wp_safe_redirect( ebook_store_get_order_admin_list_url( 'downloads_reset' ) ); |
| 6410 |
exit; |
| 6411 |
} |
| 6412 |
|
| 6413 |
function ebook_store_resend_order_delivery() { |
| 6414 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 6415 |
wp_die( esc_html__( 'You are not allowed to resend order emails.', 'ebook-store' ) ); |
| 6416 |
} |
| 6417 |
|
| 6418 |
$order_id = isset( $_GET['order_id'] ) ? absint( wp_unslash( $_GET['order_id'] ) ) : 0; |
| 6419 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 6420 |
wp_die( esc_html__( 'Invalid ebook order.', 'ebook-store' ) ); |
| 6421 |
} |
| 6422 |
|
| 6423 |
check_admin_referer( 'ebook_store_resend_order_delivery_' . $order_id ); |
| 6424 |
|
| 6425 |
$email_context = ebook_store_prepare_order_delivery_email( $order_id ); |
| 6426 |
if ( is_wp_error( $email_context ) ) { |
| 6427 |
wp_die( esc_html( $email_context->get_error_message() ) ); |
| 6428 |
} |
| 6429 |
|
| 6430 |
global $ebook_email_delivery, $formData; |
| 6431 |
$previous_email_delivery = isset( $ebook_email_delivery ) ? $ebook_email_delivery : null; |
| 6432 |
$previous_form_data = isset( $formData ) ? $formData : null; |
| 6433 |
$previous_email_delivery_setting = get_option( 'email_delivery', null ); |
| 6434 |
|
| 6435 |
$ebook_email_delivery = $email_context; |
| 6436 |
unset( $ebook_email_delivery['done'] ); |
| 6437 |
$formData = null; |
| 6438 |
update_option( 'email_delivery', 1 ); |
| 6439 |
|
| 6440 |
ob_start(); |
| 6441 |
ebook_email_delivery( $order_id ); |
| 6442 |
ob_end_clean(); |
| 6443 |
|
| 6444 |
$resend_succeeded = ! empty( $ebook_email_delivery['mail_result'] ); |
| 6445 |
|
| 6446 |
if ( null === $previous_email_delivery_setting ) { |
| 6447 |
delete_option( 'email_delivery' ); |
| 6448 |
} else { |
| 6449 |
update_option( 'email_delivery', $previous_email_delivery_setting ); |
| 6450 |
} |
| 6451 |
$ebook_email_delivery = $previous_email_delivery; |
| 6452 |
$formData = $previous_form_data; |
| 6453 |
|
| 6454 |
wp_safe_redirect( ebook_store_get_order_admin_list_url( $resend_succeeded ? 'delivery_resent' : 'delivery_failed' ) ); |
| 6455 |
exit; |
| 6456 |
} |
| 6457 |
|
| 6458 |
function ebook_store_refund_order() { |
| 6459 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 6460 |
wp_die( esc_html__( 'You are not allowed to refund ebook orders.', 'ebook-store' ) ); |
| 6461 |
} |
| 6462 |
|
| 6463 |
$order_id = isset( $_GET['order_id'] ) ? absint( wp_unslash( $_GET['order_id'] ) ) : 0; |
| 6464 |
if ( $order_id < 1 || get_post_type( $order_id ) !== 'ebook_order' ) { |
| 6465 |
wp_die( esc_html__( 'Invalid ebook order.', 'ebook-store' ) ); |
| 6466 |
} |
| 6467 |
|
| 6468 |
check_admin_referer( 'ebook_store_refund_order_' . $order_id ); |
| 6469 |
|
| 6470 |
$result = ebook_store_process_order_refund( $order_id ); |
| 6471 |
if ( is_wp_error( $result ) ) { |
| 6472 |
wp_safe_redirect( |
| 6473 |
ebook_store_get_order_admin_list_url( |
| 6474 |
'refund_failed', |
| 6475 |
array( |
| 6476 |
'ebook_store_order_message' => $result->get_error_message(), |
| 6477 |
) |
| 6478 |
) |
| 6479 |
); |
| 6480 |
exit; |
| 6481 |
} |
| 6482 |
|
| 6483 |
$gateway_label = ucfirst( (string) $result['gateway'] ); |
| 6484 |
$amount_label = ebook_store_order_money_display( |
| 6485 |
isset( $result['amount'] ) ? $result['amount'] : '', |
| 6486 |
isset( $result['currency'] ) ? $result['currency'] : '' |
| 6487 |
); |
| 6488 |
$message = sprintf( |
| 6489 |
/* translators: 1: payment gateway, 2: refunded amount */ |
| 6490 |
__( '%1$s refund created successfully for %2$s.', 'ebook-store' ), |
| 6491 |
$gateway_label, |
| 6492 |
$amount_label |
| 6493 |
); |
| 6494 |
|
| 6495 |
wp_safe_redirect( |
| 6496 |
ebook_store_get_order_admin_list_url( |
| 6497 |
'refund_succeeded', |
| 6498 |
array( |
| 6499 |
'ebook_store_order_message' => $message, |
| 6500 |
) |
| 6501 |
) |
| 6502 |
); |
| 6503 |
exit; |
| 6504 |
} |
| 6505 |
|
| 6506 |
function ebook_store_resend_email_log() { |
| 6507 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 6508 |
wp_die( esc_html__( 'You are not allowed to resend order emails.', 'ebook-store' ) ); |
| 6509 |
} |
| 6510 |
|
| 6511 |
$log_id = isset( $_GET['log_id'] ) ? absint( wp_unslash( $_GET['log_id'] ) ) : 0; |
| 6512 |
if ( $log_id < 1 || get_post_type( $log_id ) !== 'ebook_email_log' ) { |
| 6513 |
wp_die( esc_html__( 'Invalid email log entry.', 'ebook-store' ) ); |
| 6514 |
} |
| 6515 |
|
| 6516 |
check_admin_referer( 'ebook_store_resend_email_log_' . $log_id ); |
| 6517 |
|
| 6518 |
$recipient = (string) get_post_meta( $log_id, 'recipient', true ); |
| 6519 |
$subject = (string) get_post_meta( $log_id, 'subject', true ); |
| 6520 |
$body_html = (string) get_post_meta( $log_id, 'body_html', true ); |
| 6521 |
$headers = ebook_store_normalize_email_log_headers( get_post_meta( $log_id, 'headers', true ) ); |
| 6522 |
$attachments = ebook_store_normalize_email_log_attachments( get_post_meta( $log_id, 'attachments', true ) ); |
| 6523 |
$order_json = (string) get_post_meta( $log_id, 'order_snapshot', true ); |
| 6524 |
$context_json = (string) get_post_meta( $log_id, 'context_snapshot', true ); |
| 6525 |
$source = (string) get_post_meta( $log_id, 'source', true ); |
| 6526 |
$order = json_decode( $order_json, true ); |
| 6527 |
$context = json_decode( $context_json, true ); |
| 6528 |
|
| 6529 |
if ( ! is_array( $order ) ) { |
| 6530 |
$order = array(); |
| 6531 |
} |
| 6532 |
|
| 6533 |
if ( ! is_array( $context ) ) { |
| 6534 |
$context = array(); |
| 6535 |
} |
| 6536 |
|
| 6537 |
$attachments = array_values( |
| 6538 |
array_filter( |
| 6539 |
$attachments, |
| 6540 |
static function ( $path ) { |
| 6541 |
return is_string( $path ) && $path !== '' && file_exists( $path ); |
| 6542 |
} |
| 6543 |
) |
| 6544 |
); |
| 6545 |
|
| 6546 |
$mail_result = wp_mail( $recipient, $subject, $body_html, $headers, $attachments ); |
| 6547 |
|
| 6548 |
$resend_count = (int) get_post_meta( $log_id, 'resend_count', true ); |
| 6549 |
update_post_meta( $log_id, 'resend_count', $resend_count + 1 ); |
| 6550 |
update_post_meta( $log_id, 'last_resend_at', current_time( 'mysql' ) ); |
| 6551 |
update_post_meta( $log_id, 'last_resend_status', $mail_result ? 'sent' : 'failed' ); |
| 6552 |
|
| 6553 |
$new_log_id = ebook_store_create_email_log_entry( |
| 6554 |
array( |
| 6555 |
'to' => $recipient, |
| 6556 |
'subject' => $subject, |
| 6557 |
'body' => $body_html, |
| 6558 |
'headers' => $headers, |
| 6559 |
'attachments' => $attachments, |
| 6560 |
'result' => $mail_result, |
| 6561 |
'source' => 'resend', |
| 6562 |
'related_order_type' => get_post_meta( $log_id, 'related_order_type', true ), |
| 6563 |
'related_order_id' => get_post_meta( $log_id, 'related_order_id', true ), |
| 6564 |
'ebook_id' => get_post_meta( $log_id, 'ebook_id', true ), |
| 6565 |
'ebook_key' => get_post_meta( $log_id, 'ebook_key', true ), |
| 6566 |
'gateway' => get_post_meta( $log_id, 'gateway', true ), |
| 6567 |
'order' => $order, |
| 6568 |
'context' => array_merge( |
| 6569 |
$context, |
| 6570 |
array( |
| 6571 |
'resend_of' => $log_id, |
| 6572 |
'original_source' => $source, |
| 6573 |
) |
| 6574 |
), |
| 6575 |
'parent_log_id' => $log_id, |
| 6576 |
) |
| 6577 |
); |
| 6578 |
|
| 6579 |
if ( $new_log_id > 0 ) { |
| 6580 |
update_post_meta( $log_id, 'last_resend_log_id', $new_log_id ); |
| 6581 |
} |
| 6582 |
|
| 6583 |
wp_safe_redirect( |
| 6584 |
add_query_arg( |
| 6585 |
array( |
| 6586 |
'post' => $log_id, |
| 6587 |
'action' => 'edit', |
| 6588 |
'ebook_store_email_log_notice' => $mail_result ? 'resent' : 'resend_failed', |
| 6589 |
), |
| 6590 |
admin_url( 'post.php' ) |
| 6591 |
) |
| 6592 |
); |
| 6593 |
exit; |
| 6594 |
} |
| 6595 |
|
| 6596 |
add_filter( 'manage_edit-ebook_email_log_columns', 'ebook_store_set_email_log_columns' ); |
| 6597 |
add_action( 'manage_ebook_email_log_posts_custom_column', 'ebook_store_email_log_columns_output', 10, 2 ); |
| 6598 |
add_filter( 'post_row_actions', 'ebook_store_email_log_row_actions', 10, 2 ); |
| 6599 |
add_action( 'add_meta_boxes', 'ebook_store_remove_email_log_submit_box', 20 ); |
| 6600 |
add_action( 'admin_menu', 'ebook_store_cleanup_email_log_submenu', 999 ); |
| 6601 |
add_action( 'admin_init', 'ebook_store_redirect_new_email_log_entry' ); |
| 6602 |
add_action( 'admin_head', 'ebook_store_render_email_log_admin_styles' ); |
| 6603 |
add_action( 'admin_notices', 'ebook_store_email_log_admin_notice' ); |
| 6604 |
add_action( 'admin_notices', 'ebook_store_order_admin_notice' ); |
| 6605 |
add_action( 'admin_post_ebook_store_resend_email_log', 'ebook_store_resend_email_log' ); |
| 6606 |
add_action( 'admin_post_ebook_store_reset_order_downloads', 'ebook_store_reset_order_downloads' ); |
| 6607 |
add_action( 'admin_post_ebook_store_resend_order_delivery', 'ebook_store_resend_order_delivery' ); |
| 6608 |
add_action( 'admin_post_ebook_store_refund_order', 'ebook_store_refund_order' ); |
| 6609 |
|
| 6610 |
function ebook_store_get_order_verification_url( $ebook_key ) { |
| 6611 |
$ebook_key = preg_replace( '/[^a-zA-Z0-9]+/', '', (string) $ebook_key ); |
| 6612 |
|
| 6613 |
return add_query_arg( |
| 6614 |
array( |
| 6615 |
'ebook_action' => 'verify_order', |
| 6616 |
'ebook_key' => $ebook_key, |
| 6617 |
), |
| 6618 |
home_url( '/' ) |
| 6619 |
); |
| 6620 |
} |
| 6621 |
|
| 6622 |
/** |
| 6623 |
* The QR-verification key used for a Test-button copy, so the verification page |
| 6624 |
* can recognise it as a test (there is no real order behind it) instead of |
| 6625 |
* showing "record not found". Kept uppercase so it can never collide with a real |
| 6626 |
* order's md5 (lowercase-hex) key. |
| 6627 |
* |
| 6628 |
* @param int $ebook_id Ebook post ID. |
| 6629 |
* @return string |
| 6630 |
*/ |
| 6631 |
function ebook_store_test_verification_key( $ebook_id ) { |
| 6632 |
return 'EBOOKSTORETEST' . (int) $ebook_id; |
| 6633 |
} |
| 6634 |
|
| 6635 |
/** |
| 6636 |
* If a verification key is a Test-button key, return the ebook ID it was made |
| 6637 |
* from; otherwise false. |
| 6638 |
* |
| 6639 |
* @param string $key Sanitised (alphanumeric) verification key. |
| 6640 |
* @return int|false |
| 6641 |
*/ |
| 6642 |
function ebook_store_parse_test_verification_key( $key ) { |
| 6643 |
$prefix = 'EBOOKSTORETEST'; |
| 6644 |
if ( strpos( (string) $key, $prefix ) !== 0 ) { |
| 6645 |
return false; |
| 6646 |
} |
| 6647 |
return (int) substr( (string) $key, strlen( $prefix ) ); |
| 6648 |
} |
| 6649 |
|
| 6650 |
function ebook_store_render_public_order_field( $label, $value ) { |
| 6651 |
if ( $value === '' || $value === null ) { |
| 6652 |
return ''; |
| 6653 |
} |
| 6654 |
|
| 6655 |
return '<div class="ebook-store-verify__field"><span>' . esc_html( $label ) . '</span><strong>' . esc_html( $value ) . '</strong></div>'; |
| 6656 |
} |
| 6657 |
|
| 6658 |
function ebook_store_render_public_order_verification_page() { |
| 6659 |
if ( ! isset( $_GET['ebook_action'] ) || sanitize_text_field( wp_unslash( $_GET['ebook_action'] ) ) !== 'verify_order' ) { |
| 6660 |
return; |
| 6661 |
} |
| 6662 |
|
| 6663 |
$ebook_key = isset( $_GET['ebook_key'] ) ? preg_replace( '/[^a-zA-Z0-9]+/', '', wp_unslash( $_GET['ebook_key'] ) ) : ''; |
| 6664 |
|
| 6665 |
// A QR scanned from a Test-button copy carries no real order, so recognise it |
| 6666 |
// and show a "test ebook" confirmation rather than a scary "not found". |
| 6667 |
$test_ebook_id = ebook_store_parse_test_verification_key( $ebook_key ); |
| 6668 |
$is_test = ( $test_ebook_id !== false ); |
| 6669 |
|
| 6670 |
if ( $is_test ) { |
| 6671 |
$order = false; |
| 6672 |
} elseif ( $ebook_key === '' ) { |
| 6673 |
$order = false; |
| 6674 |
} else { |
| 6675 |
$order = ebook_get_order( 'ebook_key', $ebook_key ); |
| 6676 |
if ( ! $order && get_option( 'stripe_integration_enabled', 0 ) ) { |
| 6677 |
$recovered_order_id = ebook_store_recover_stripe_order_by_ebook_key( $ebook_key ); |
| 6678 |
if ( ! is_wp_error( $recovered_order_id ) && $recovered_order_id ) { |
| 6679 |
$order = ebook_get_order( 'ebook_key', $ebook_key ); |
| 6680 |
} |
| 6681 |
} |
| 6682 |
} |
| 6683 |
|
| 6684 |
$found_order = is_array( $order ) && ! empty( $order['ebook'][0] ); |
| 6685 |
status_header( ( $is_test || $found_order ) ? 200 : 404 ); |
| 6686 |
nocache_headers(); |
| 6687 |
|
| 6688 |
$site_name = wp_strip_all_tags( get_bloginfo( 'name' ) ); |
| 6689 |
if ( $is_test ) { |
| 6690 |
$page_title = __( 'Test Copy', 'ebook-store' ); |
| 6691 |
$status_label = __( 'Test copy', 'ebook-store' ); |
| 6692 |
$status_class = 'is-test'; |
| 6693 |
} else { |
| 6694 |
$page_title = $found_order ? __( 'Ebook Copy Verification', 'ebook-store' ) : __( 'Verification Record Not Found', 'ebook-store' ); |
| 6695 |
$status_label = $found_order |
| 6696 |
? ( ! empty( $order['payment_status'][0] ) ? (string) $order['payment_status'][0] : __( 'Completed', 'ebook-store' ) ) |
| 6697 |
: __( 'Missing', 'ebook-store' ); |
| 6698 |
$status_class = $found_order ? 'is-valid' : 'is-missing'; |
| 6699 |
} |
| 6700 |
|
| 6701 |
$content = '<!DOCTYPE html><html ' . get_language_attributes() . '><head><meta charset="' . esc_attr( get_bloginfo( 'charset' ) ) . '" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="robots" content="noindex,nofollow" /><title>' . esc_html( $page_title . ' - ' . $site_name ) . '</title><style> |
| 6702 |
:root{--ebook-verify-bg:#f5f7f2;--ebook-verify-card:#ffffff;--ebook-verify-ink:#163326;--ebook-verify-muted:#617565;--ebook-verify-border:#d8e3d6;--ebook-verify-green:#1b8f4d;--ebook-verify-red:#b33a2f;--ebook-verify-red-soft:#fff1ef;--ebook-verify-green-soft:#eefaf2;--ebook-verify-shadow:0 24px 60px rgba(14,36,24,.08)} |
| 6703 |
*{box-sizing:border-box}body{margin:0;font-family:Georgia,\"Times New Roman\",serif;background:radial-gradient(circle at top,#ffffff 0,#f5f7f2 45%,#edf2ea 100%);color:var(--ebook-verify-ink)} |
| 6704 |
a{color:inherit} .ebook-store-verify{max-width:980px;margin:0 auto;padding:32px 20px 56px} |
| 6705 |
.ebook-store-verify__hero{background:var(--ebook-verify-card);border:1px solid var(--ebook-verify-border);border-radius:28px;padding:28px;box-shadow:var(--ebook-verify-shadow);position:relative;overflow:hidden} |
| 6706 |
.ebook-store-verify__hero:before{content:\"\";position:absolute;inset:auto -80px -120px auto;width:240px;height:240px;border-radius:999px;background:linear-gradient(135deg,rgba(27,143,77,.12),rgba(27,143,77,0))} |
| 6707 |
.ebook-store-verify__eyebrow{display:inline-block;font:600 12px/1.2 Arial,sans-serif;letter-spacing:.14em;text-transform:uppercase;color:var(--ebook-verify-muted);margin-bottom:14px} |
| 6708 |
.ebook-store-verify__hero h1{margin:0 0 10px;font-size:clamp(2rem,4vw,3.5rem);line-height:.98} |
| 6709 |
.ebook-store-verify__hero p{margin:0;max-width:62ch;font:400 16px/1.6 Arial,sans-serif;color:var(--ebook-verify-muted)} |
| 6710 |
.ebook-store-verify__status{display:inline-flex;align-items:center;gap:10px;margin-top:18px;padding:10px 14px;border-radius:999px;font:700 13px/1 Arial,sans-serif;letter-spacing:.04em;text-transform:uppercase} |
| 6711 |
.ebook-store-verify__status.is-valid{background:var(--ebook-verify-green-soft);color:var(--ebook-verify-green)} |
| 6712 |
.ebook-store-verify__status.is-missing{background:var(--ebook-verify-red-soft);color:var(--ebook-verify-red)} |
| 6713 |
.ebook-store-verify__status.is-test{background:#fbf1de;color:#7a4d00} |
| 6714 |
.ebook-store-verify__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;margin-top:20px} |
| 6715 |
.ebook-store-verify__card{background:var(--ebook-verify-card);border:1px solid var(--ebook-verify-border);border-radius:24px;padding:22px;box-shadow:var(--ebook-verify-shadow)} |
| 6716 |
.ebook-store-verify__card h2{margin:0 0 14px;font-size:1.1rem} |
| 6717 |
.ebook-store-verify__field{display:flex;justify-content:space-between;gap:18px;padding:12px 0;border-top:1px solid #edf1eb} |
| 6718 |
.ebook-store-verify__field:first-child{border-top:0;padding-top:0} |
| 6719 |
.ebook-store-verify__field span{font:600 12px/1.3 Arial,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:var(--ebook-verify-muted)} |
| 6720 |
.ebook-store-verify__field strong{font:600 15px/1.4 Arial,sans-serif;text-align:right;max-width:65%} |
| 6721 |
.ebook-store-verify__notice{margin-top:20px;background:var(--ebook-verify-card);border:1px dashed var(--ebook-verify-border);border-radius:22px;padding:18px 20px;font:400 15px/1.7 Arial,sans-serif;color:var(--ebook-verify-muted)} |
| 6722 |
.ebook-store-verify__footer{margin-top:16px;font:400 13px/1.6 Arial,sans-serif;color:var(--ebook-verify-muted)} |
| 6723 |
@media (max-width:780px){.ebook-store-verify__grid{grid-template-columns:1fr}.ebook-store-verify__field{flex-direction:column;align-items:flex-start}.ebook-store-verify__field strong{max-width:none;text-align:left}} |
| 6724 |
</style></head><body><main class="ebook-store-verify"><section class="ebook-store-verify__hero"><span class="ebook-store-verify__eyebrow">' . esc_html__( 'Ebook Watermark Verification', 'ebook-store' ) . '</span><h1>' . esc_html( $page_title ) . '</h1><p>' . esc_html__( 'This page is generated from the QR code embedded inside a protected ebook. It helps identify the original transaction and buyer if a copy has been shared or redistributed without permission.', 'ebook-store' ) . '</p><div class="ebook-store-verify__status ' . esc_attr( $status_class ) . '">' . esc_html( $status_label ) . '</div></section>'; |
| 6725 |
|
| 6726 |
if ( $is_test ) { |
| 6727 |
$test_title = $test_ebook_id ? get_the_title( $test_ebook_id ) : ''; |
| 6728 |
$content .= '<section class="ebook-store-verify__notice"><strong>' . esc_html__( 'This is a test ebook.', 'ebook-store' ) . '</strong> '; |
| 6729 |
$content .= esc_html__( 'You are verifying a copy generated with the Test button in the store admin, not a real purchase.', 'ebook-store' ); |
| 6730 |
if ( $test_title !== '' ) { |
| 6731 |
$content .= ' ' . esc_html( |
| 6732 |
sprintf( |
| 6733 |
/* translators: 1: ebook title, 2: ebook ID. */ |
| 6734 |
__( 'It was made from "%1$s" (ebook #%2$d).', 'ebook-store' ), |
| 6735 |
$test_title, |
| 6736 |
$test_ebook_id |
| 6737 |
) |
| 6738 |
); |
| 6739 |
} |
| 6740 |
$content .= ' ' . esc_html__( 'No buyer or order is recorded for a test copy. On a real customer\'s copy, this same code opens the full order and buyer details that the watermark traces back to.', 'ebook-store' ) . '</section>'; |
| 6741 |
} elseif ( $found_order ) { |
| 6742 |
$ebook_id = isset( $order['ebook'][0] ) ? (int) $order['ebook'][0] : 0; |
| 6743 |
$ebook_title = $ebook_id ? get_the_title( $ebook_id ) : ''; |
| 6744 |
$order_id = isset( $order['order_id'] ) ? (int) $order['order_id'] : 0; |
| 6745 |
$buyer_name = trim( ( isset( $order['first_name'][0] ) ? $order['first_name'][0] : '' ) . ' ' . ( isset( $order['last_name'][0] ) ? $order['last_name'][0] : '' ) ); |
| 6746 |
$payer_email = isset( $order['payer_email'][0] ) ? (string) $order['payer_email'][0] : ''; |
| 6747 |
$payment_date = isset( $order['payment_date'][0] ) ? (string) $order['payment_date'][0] : ''; |
| 6748 |
$payment_type = isset( $order['payment_type'][0] ) ? (string) $order['payment_type'][0] : ''; |
| 6749 |
$payment_status = isset( $order['payment_status'][0] ) ? (string) $order['payment_status'][0] : ''; |
| 6750 |
$txn_id = isset( $order['txn_id'][0] ) ? (string) $order['txn_id'][0] : ''; |
| 6751 |
$gross = isset( $order['mc_gross'][0] ) ? (string) $order['mc_gross'][0] : ''; |
| 6752 |
$currency = isset( $order['mc_currency'][0] ) && $order['mc_currency'][0] !== '' ? (string) $order['mc_currency'][0] : ebook_store_get_store_currency(); |
| 6753 |
$country = isset( $order['address_country'][0] ) && $order['address_country'][0] !== '' ? (string) $order['address_country'][0] : ( isset( $order['residence_country'][0] ) ? (string) $order['residence_country'][0] : '' ); |
| 6754 |
$city = isset( $order['address_city'][0] ) ? (string) $order['address_city'][0] : ''; |
| 6755 |
$state = isset( $order['address_state'][0] ) ? (string) $order['address_state'][0] : ''; |
| 6756 |
$zip = isset( $order['address_zip'][0] ) ? (string) $order['address_zip'][0] : ''; |
| 6757 |
$address_street = isset( $order['address_street'][0] ) ? (string) $order['address_street'][0] : ''; |
| 6758 |
|
| 6759 |
$content .= '<section class="ebook-store-verify__grid">'; |
| 6760 |
$content .= '<article class="ebook-store-verify__card"><h2>' . esc_html__( 'Order Record', 'ebook-store' ) . '</h2>'; |
| 6761 |
$content .= ebook_store_render_public_order_field( __( 'Order ID', 'ebook-store' ), $order_id ? '#' . $order_id : '' ); |
| 6762 |
$content .= ebook_store_render_public_order_field( __( 'Ebook', 'ebook-store' ), $ebook_title ); |
| 6763 |
$content .= ebook_store_render_public_order_field( __( 'Ebook ID', 'ebook-store' ), $ebook_id ? (string) $ebook_id : '' ); |
| 6764 |
$content .= ebook_store_render_public_order_field( __( 'Payment date', 'ebook-store' ), $payment_date ); |
| 6765 |
$content .= ebook_store_render_public_order_field( __( 'Gateway', 'ebook-store' ), $payment_type !== '' ? $payment_type : __( 'PayPal', 'ebook-store' ) ); |
| 6766 |
$content .= ebook_store_render_public_order_field( __( 'Status', 'ebook-store' ), $payment_status ); |
| 6767 |
$content .= ebook_store_render_public_order_field( __( 'Transaction', 'ebook-store' ), $txn_id ); |
| 6768 |
$content .= ebook_store_render_public_order_field( __( 'Amount', 'ebook-store' ), trim( $gross . ' ' . $currency ) ); |
| 6769 |
$content .= '</article>'; |
| 6770 |
$content .= '<article class="ebook-store-verify__card"><h2>' . esc_html__( 'Buyer Details', 'ebook-store' ) . '</h2>'; |
| 6771 |
$content .= ebook_store_render_public_order_field( __( 'Name', 'ebook-store' ), $buyer_name ); |
| 6772 |
$content .= ebook_store_render_public_order_field( __( 'Email', 'ebook-store' ), $payer_email ); |
| 6773 |
$content .= ebook_store_render_public_order_field( __( 'Street', 'ebook-store' ), $address_street ); |
| 6774 |
$content .= ebook_store_render_public_order_field( __( 'City', 'ebook-store' ), $city ); |
| 6775 |
$content .= ebook_store_render_public_order_field( __( 'State', 'ebook-store' ), $state ); |
| 6776 |
$content .= ebook_store_render_public_order_field( __( 'Postal code', 'ebook-store' ), $zip ); |
| 6777 |
$content .= ebook_store_render_public_order_field( __( 'Country', 'ebook-store' ), $country ); |
| 6778 |
$content .= ebook_store_render_public_order_field( __( 'Order key', 'ebook-store' ), $ebook_key ); |
| 6779 |
$content .= '</article>'; |
| 6780 |
$content .= '</section>'; |
| 6781 |
$content .= '<section class="ebook-store-verify__notice"><strong>' . esc_html__( 'Traceability note:', 'ebook-store' ) . '</strong> ' . esc_html__( 'If this PDF was found on a third-party site or shared without permission, the order record above identifies the original licensed copy that the watermark was generated for.', 'ebook-store' ) . '</section>'; |
| 6782 |
} else { |
| 6783 |
$content .= '<section class="ebook-store-verify__notice"><strong>' . esc_html__( 'No active order was found for this QR code.', 'ebook-store' ) . '</strong> ' . esc_html__( 'The linked order may have been deleted, the key may be invalid, or the record is not available on this site.', 'ebook-store' ) . '</section>'; |
| 6784 |
} |
| 6785 |
|
| 6786 |
$content .= '<div class="ebook-store-verify__footer">' . esc_html( $site_name ) . '</div></main></body></html>'; |
| 6787 |
|
| 6788 |
echo $content; |
| 6789 |
exit; |
| 6790 |
} |
| 6791 |
|
| 6792 |
/** |
| 6793 |
* Clean a string that is about to be drawn into a PDF watermark. |
| 6794 |
* |
| 6795 |
* PDF text is NOT HTML, so esc_attr()/esc_html() are wrong here — they turn |
| 6796 |
* "Tom & Jerry" into "Tom & Jerry" inside the document. Just strip control |
| 6797 |
* characters and collapse whitespace. |
| 6798 |
* |
| 6799 |
* @param string $text Raw text (may contain %%merge%% codes before substitution). |
| 6800 |
* @return string |
| 6801 |
*/ |
| 6802 |
function ebook_store_sanitize_pdf_text( $text ) { |
| 6803 |
$text = (string) $text; |
| 6804 |
// Drop ASCII control characters except tab/newline, then trim. |
| 6805 |
$text = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $text ); |
| 6806 |
|
| 6807 |
return trim( (string) $text ); |
| 6808 |
} |
| 6809 |
|
| 6810 |
/** |
| 6811 |
* Resolve the buyer watermark text for an order, substituting the %%merge%% |
| 6812 |
* codes. Shared by the PDF and EPUB watermark paths so the tokens resolve the |
| 6813 |
* same way: WooCommerce-style meta first, then the built-in order meta. |
| 6814 |
* |
| 6815 |
* @param int $order_id Order post ID. |
| 6816 |
* @param string $template Raw template, e.g. "Licensed to %%first_name%% %%last_name%%". |
| 6817 |
* @return string Resolved, control-char-stripped text (NOT HTML-escaped). |
| 6818 |
*/ |
| 6819 |
function ebook_store_resolve_buyer_watermark( $order_id, $template ) { |
| 6820 |
$order_id = (int) $order_id; |
| 6821 |
$meta = $order_id > 0 ? get_post_meta( $order_id ) : array(); |
| 6822 |
$meta = is_array( $meta ) ? $meta : array(); |
| 6823 |
$mv = function ( $key ) use ( $meta ) { |
| 6824 |
return isset( $meta[ $key ][0] ) ? $meta[ $key ][0] : ''; |
| 6825 |
}; |
| 6826 |
|
| 6827 |
$tokens = array( |
| 6828 |
'payer_email' => $mv( '_billing_email' ), |
| 6829 |
'payment_date' => $mv( '_paid_date' ), |
| 6830 |
'txn_id' => $mv( '_transaction_id' ), |
| 6831 |
'first_name' => $mv( '_billing_first_name' ), |
| 6832 |
'last_name' => $mv( '_billing_last_name' ), |
| 6833 |
'residence_country' => $mv( '_billing_country' ), |
| 6834 |
); |
| 6835 |
|
| 6836 |
// Built-in (non-Woo) orders store the buyer under their own keys. Read each |
| 6837 |
// field from its own key (an earlier copy of this logic took first_name from |
| 6838 |
// the last_name meta — fixed here). |
| 6839 |
if ( $tokens['payer_email'] === '' ) { |
| 6840 |
$tokens['payer_email'] = $mv( 'payer_email' ); |
| 6841 |
$tokens['payment_date'] = $mv( 'payment_date' ); |
| 6842 |
$tokens['txn_id'] = $mv( 'txn_id' ); |
| 6843 |
$tokens['first_name'] = $mv( 'first_name' ); |
| 6844 |
$tokens['last_name'] = $mv( 'last_name' ); |
| 6845 |
$tokens['residence_country'] = $mv( 'residence_country' ); |
| 6846 |
} |
| 6847 |
|
| 6848 |
$text = ebook_store_sanitize_pdf_text( (string) $template ); |
| 6849 |
foreach ( $tokens as $key => $value ) { |
| 6850 |
if ( is_scalar( $value ) ) { |
| 6851 |
$text = str_replace( '%%' . $key . '%%', ebook_store_sanitize_pdf_text( (string) $value ), $text ); |
| 6852 |
} |
| 6853 |
} |
| 6854 |
|
| 6855 |
return $text; |
| 6856 |
} |
| 6857 |
|
| 6858 |
/** |
| 6859 |
* Build the visible watermark HTML fragment injected into EPUB content. |
| 6860 |
* |
| 6861 |
* Inline CSS only, so no stylesheet or manifest edit is needed. dir="auto" lets |
| 6862 |
* the reader lay the buyer line out right-to-left for Hebrew/Arabic names. |
| 6863 |
* |
| 6864 |
* @param string $text Buyer text (raw, not yet escaped). |
| 6865 |
* @param string $style 'subtle' | 'box'. |
| 6866 |
* @return string XML-well-formed XHTML fragment. |
| 6867 |
*/ |
| 6868 |
function ebook_store_epub_watermark_fragment( $text, $style ) { |
| 6869 |
$wm = htmlspecialchars( (string) $text, ENT_QUOTES, 'UTF-8' ); |
| 6870 |
if ( $style === 'box' ) { |
| 6871 |
$css = 'margin:1.5em 0;padding:.6em .8em;border:1px solid rgba(0,0,0,.35);border-radius:3px;font-size:.8em;line-height:1.4;text-align:center;font-family:serif'; |
| 6872 |
} else { |
| 6873 |
$css = 'margin:1.5em 0;padding:.4em 0;border-top:1px solid rgba(0,0,0,.15);font-size:.75em;line-height:1.4;opacity:.65;text-align:center;font-family:serif'; |
| 6874 |
} |
| 6875 |
|
| 6876 |
return '<div class="ebook-store-wm" dir="auto" style="' . $css . '">' . $wm . '</div>'; |
| 6877 |
} |
| 6878 |
|
| 6879 |
/** |
| 6880 |
* Insert an XHTML fragment into a content document via string ops, NOT DOM. |
| 6881 |
* |
| 6882 |
* Real content XHTML routinely contains named entities like that a strict |
| 6883 |
* DOM parser rejects, so we never DOM-parse content documents — we splice the |
| 6884 |
* fragment next to the <body> tag and leave everything else byte-for-byte intact. |
| 6885 |
* |
| 6886 |
* @param string $html The XHTML document. |
| 6887 |
* @param string $fragment The fragment to insert. |
| 6888 |
* @param string $position 'top' | 'bottom'. |
| 6889 |
* @return string|null Modified document, or null if no <body> boundary was found. |
| 6890 |
*/ |
| 6891 |
function ebook_store_epub_inject_fragment( $html, $fragment, $position ) { |
| 6892 |
$html = (string) $html; |
| 6893 |
|
| 6894 |
if ( $position === 'top' ) { |
| 6895 |
if ( preg_match( '/<body\b[^>]*>/i', $html, $m, PREG_OFFSET_CAPTURE ) ) { |
| 6896 |
$at = $m[0][1] + strlen( $m[0][0] ); |
| 6897 |
return substr( $html, 0, $at ) . $fragment . substr( $html, $at ); |
| 6898 |
} |
| 6899 |
return null; |
| 6900 |
} |
| 6901 |
|
| 6902 |
if ( preg_match( '/<\/body\s*>/i', $html, $m, PREG_OFFSET_CAPTURE ) ) { |
| 6903 |
$at = $m[0][1]; |
| 6904 |
return substr( $html, 0, $at ) . $fragment . substr( $html, $at ); |
| 6905 |
} |
| 6906 |
|
| 6907 |
return null; |
| 6908 |
} |
| 6909 |
|
| 6910 |
/** |
| 6911 |
* Relative path from a directory inside the EPUB to a file inside it, so an |
| 6912 |
* <img> in a content document (or a manifest href) can point at the QR image |
| 6913 |
* wherever the two happen to sit in the archive. |
| 6914 |
* |
| 6915 |
* @param string $from_dir Directory of the referencing file (archive path). |
| 6916 |
* @param string $to_path Target file (archive path). |
| 6917 |
* @return string Relative reference. |
| 6918 |
*/ |
| 6919 |
function ebook_store_epub_relative_path( $from_dir, $to_path ) { |
| 6920 |
$from_dir = ( $from_dir === '.' || $from_dir === '/' ) ? '' : trim( (string) $from_dir, '/' ); |
| 6921 |
$to_path = ltrim( (string) $to_path, '/' ); |
| 6922 |
$from = $from_dir === '' ? array() : explode( '/', $from_dir ); |
| 6923 |
$to = explode( '/', $to_path ); |
| 6924 |
while ( ! empty( $from ) && ! empty( $to ) && $from[0] === $to[0] ) { |
| 6925 |
array_shift( $from ); |
| 6926 |
array_shift( $to ); |
| 6927 |
} |
| 6928 |
$rel = str_repeat( '../', count( $from ) ) . implode( '/', $to ); |
| 6929 |
return $rel === '' ? basename( $to_path ) : $rel; |
| 6930 |
} |
| 6931 |
|
| 6932 |
/** |
| 6933 |
* Render a QR code to PNG bytes on a solid white background with a quiet zone, |
| 6934 |
* so it stays scannable on any reader background (including night mode). |
| 6935 |
* |
| 6936 |
* Uses the bundled TCPDF 2D-barcode generator, loaded narrowly so the EPUB path |
| 6937 |
* never pulls in the full PDF engine. Returns '' on any problem (fail-open: the |
| 6938 |
* book is still delivered, just without a QR). |
| 6939 |
* |
| 6940 |
* @param string $text The URL/text to encode. |
| 6941 |
* @param int $module_px Pixels per QR module. |
| 6942 |
* @param int $quiet Quiet-zone width in modules. |
| 6943 |
* @return string PNG bytes, or '' if a QR could not be produced. |
| 6944 |
*/ |
| 6945 |
function ebook_store_generate_qr_png( $text, $module_px = 8, $quiet = 4 ) { |
| 6946 |
$text = (string) $text; |
| 6947 |
if ( $text === '' || ! function_exists( 'imagecreatetruecolor' ) || ! function_exists( 'imagepng' ) ) { |
| 6948 |
return ''; |
| 6949 |
} |
| 6950 |
|
| 6951 |
if ( ! class_exists( 'TCPDF2DBarcode' ) ) { |
| 6952 |
$base = __DIR__ . '/fpdi/vendor/tecnickcom/tcpdf'; |
| 6953 |
$qr = $base . '/include/barcodes/qrcode.php'; |
| 6954 |
$bc = $base . '/tcpdf_barcodes_2d.php'; |
| 6955 |
if ( is_readable( $qr ) && is_readable( $bc ) ) { |
| 6956 |
require_once $qr; |
| 6957 |
require_once $bc; |
| 6958 |
} |
| 6959 |
} |
| 6960 |
if ( ! class_exists( 'TCPDF2DBarcode' ) ) { |
| 6961 |
return ''; |
| 6962 |
} |
| 6963 |
|
| 6964 |
try { |
| 6965 |
$obj = new TCPDF2DBarcode( $text, 'QRCODE,M' ); |
| 6966 |
$arr = $obj->getBarcodeArray(); |
| 6967 |
if ( empty( $arr['num_rows'] ) || empty( $arr['num_cols'] ) || empty( $arr['bcode'] ) ) { |
| 6968 |
return ''; |
| 6969 |
} |
| 6970 |
$rows = (int) $arr['num_rows']; |
| 6971 |
$cols = (int) $arr['num_cols']; |
| 6972 |
$w = ( $cols + 2 * $quiet ) * $module_px; |
| 6973 |
$h = ( $rows + 2 * $quiet ) * $module_px; |
| 6974 |
|
| 6975 |
$img = imagecreatetruecolor( $w, $h ); |
| 6976 |
$white = imagecolorallocate( $img, 255, 255, 255 ); |
| 6977 |
$black = imagecolorallocate( $img, 0, 0, 0 ); |
| 6978 |
imagefilledrectangle( $img, 0, 0, $w - 1, $h - 1, $white ); |
| 6979 |
for ( $r = 0; $r < $rows; $r++ ) { |
| 6980 |
for ( $c = 0; $c < $cols; $c++ ) { |
| 6981 |
if ( ! empty( $arr['bcode'][ $r ][ $c ] ) ) { |
| 6982 |
$x0 = ( $c + $quiet ) * $module_px; |
| 6983 |
$y0 = ( $r + $quiet ) * $module_px; |
| 6984 |
imagefilledrectangle( $img, $x0, $y0, $x0 + $module_px - 1, $y0 + $module_px - 1, $black ); |
| 6985 |
} |
| 6986 |
} |
| 6987 |
} |
| 6988 |
ob_start(); |
| 6989 |
imagepng( $img ); |
| 6990 |
$data = ob_get_clean(); |
| 6991 |
imagedestroy( $img ); |
| 6992 |
|
| 6993 |
return is_string( $data ) ? $data : ''; |
| 6994 |
} catch ( \Throwable $e ) { |
| 6995 |
return ''; |
| 6996 |
} |
| 6997 |
} |
| 6998 |
|
| 6999 |
/** |
| 7000 |
* The XHTML fragment that shows the QR image, pointing at a relative src. |
| 7001 |
* |
| 7002 |
* @param string $src Relative image reference from the referencing document. |
| 7003 |
* @return string Well-formed XHTML fragment. |
| 7004 |
*/ |
| 7005 |
function ebook_store_epub_qr_fragment( $src ) { |
| 7006 |
$src = htmlspecialchars( (string) $src, ENT_QUOTES, 'UTF-8' ); |
| 7007 |
$alt = htmlspecialchars( __( 'Scan to verify this copy', 'ebook-store' ), ENT_QUOTES, 'UTF-8' ); |
| 7008 |
return '<div class="ebook-store-qr" style="text-align:center;margin:1.2em 0">' |
| 7009 |
. '<img src="' . $src . '" alt="' . $alt . '" style="width:120px;height:120px;max-width:40%"/></div>'; |
| 7010 |
} |
| 7011 |
|
| 7012 |
/** |
| 7013 |
* Watermark an EPUB with the buyer's identity (social DRM) while keeping it a |
| 7014 |
* valid, reader-compatible book. |
| 7015 |
* |
| 7016 |
* EPUB cannot be password-encrypted and still open in e-readers, so instead the |
| 7017 |
* buyer line is injected into the content and (optionally) the OPF metadata, and |
| 7018 |
* the archive is rewritten with "mimetype" kept FIRST and STORED. This is a plain |
| 7019 |
* function that never loads the PDF stack, and it FAILS OPEN: on any problem it |
| 7020 |
* returns the untouched $src_path so a paid download is never blocked. It never |
| 7021 |
* throws. |
| 7022 |
* |
| 7023 |
* @param string $src_path Absolute path to the source .epub. |
| 7024 |
* @param string $dest_path Absolute path to write the watermarked copy to. |
| 7025 |
* @param string $watermark_text Buyer text, already merge-resolved (raw, not escaped). |
| 7026 |
* @param array $opts scope|position|style|stamp_metadata|order_id. |
| 7027 |
* @return string $dest_path on success, else $src_path. |
| 7028 |
*/ |
| 7029 |
function ebook_store_watermark_epub( $src_path, $dest_path, $watermark_text, $opts = array() ) { |
| 7030 |
if ( ! class_exists( 'ZipArchive' ) || ! class_exists( 'DOMDocument' ) || ! is_readable( $src_path ) ) { |
| 7031 |
return $src_path; |
| 7032 |
} |
| 7033 |
|
| 7034 |
$log = function ( $message, $context = array() ) { |
| 7035 |
if ( function_exists( 'ebook_store_log' ) ) { |
| 7036 |
ebook_store_log( 'epub', $message, $context ); |
| 7037 |
} |
| 7038 |
}; |
| 7039 |
|
| 7040 |
// The archive is held in memory ~twice while repacking, and an OOM is |
| 7041 |
// uncatchable, so refuse very large books rather than 500 the download. |
| 7042 |
$max = (int) apply_filters( 'ebook_store_epub_watermark_max_bytes', 64 * 1024 * 1024 ); |
| 7043 |
$size = @filesize( $src_path ); |
| 7044 |
if ( $size !== false && $size > $max ) { |
| 7045 |
$log( 'too large, serving source', array( 'bytes' => $size ) ); |
| 7046 |
return $src_path; |
| 7047 |
} |
| 7048 |
if ( function_exists( 'ebook_store_raise_pdf_memory' ) ) { |
| 7049 |
ebook_store_raise_pdf_memory(); |
| 7050 |
} |
| 7051 |
|
| 7052 |
try { |
| 7053 |
$scope = in_array( isset( $opts['scope'] ) ? $opts['scope'] : '', array( 'first', 'every', 'colophon' ), true ) ? $opts['scope'] : 'first'; |
| 7054 |
$position = in_array( isset( $opts['position'] ) ? $opts['position'] : '', array( 'top', 'bottom' ), true ) ? $opts['position'] : 'bottom'; |
| 7055 |
$style = in_array( isset( $opts['style'] ) ? $opts['style'] : '', array( 'subtle', 'box' ), true ) ? $opts['style'] : 'subtle'; |
| 7056 |
$stamp = ! empty( $opts['stamp_metadata'] ); |
| 7057 |
$order_id = isset( $opts['order_id'] ) ? (int) $opts['order_id'] : 0; |
| 7058 |
|
| 7059 |
// QR code carries its own placement so the buyer can put it where they like. |
| 7060 |
$qr_scope = in_array( isset( $opts['qr_scope'] ) ? $opts['qr_scope'] : '', array( 'first', 'every', 'colophon' ), true ) ? $opts['qr_scope'] : 'first'; |
| 7061 |
$qr_position = in_array( isset( $opts['qr_position'] ) ? $opts['qr_position'] : '', array( 'top', 'bottom' ), true ) ? $opts['qr_position'] : 'bottom'; |
| 7062 |
$qr_text = isset( $opts['qr_text'] ) ? trim( (string) $opts['qr_text'] ) : ''; |
| 7063 |
$qr_enabled = ! empty( $opts['qr'] ) && $qr_text !== ''; |
| 7064 |
|
| 7065 |
$text = ebook_store_sanitize_pdf_text( (string) $watermark_text ); |
| 7066 |
if ( $text === '' && ! $qr_enabled && ! $stamp ) { |
| 7067 |
return $src_path; // nothing to add |
| 7068 |
} |
| 7069 |
|
| 7070 |
// --- STEP 1: read the whole archive into memory, then close it. --- |
| 7071 |
$in = new ZipArchive(); |
| 7072 |
if ( $in->open( $src_path ) !== true ) { |
| 7073 |
return $src_path; |
| 7074 |
} |
| 7075 |
$entries = array(); |
| 7076 |
$order = array(); |
| 7077 |
for ( $i = 0; $i < $in->numFiles; $i++ ) { |
| 7078 |
$stat = $in->statIndex( $i ); |
| 7079 |
if ( $stat === false ) { |
| 7080 |
$in->close(); |
| 7081 |
return $src_path; |
| 7082 |
} |
| 7083 |
$name = $stat['name']; |
| 7084 |
if ( $name === '' || substr( $name, -1 ) === '/' ) { |
| 7085 |
continue; // directory marker |
| 7086 |
} |
| 7087 |
$content = $in->getFromIndex( $i ); |
| 7088 |
if ( $content === false ) { |
| 7089 |
$in->close(); |
| 7090 |
return $src_path; // corrupt entry: never ship a hollow book |
| 7091 |
} |
| 7092 |
if ( ! isset( $entries[ $name ] ) ) { |
| 7093 |
$order[] = $name; |
| 7094 |
} |
| 7095 |
$entries[ $name ] = $content; |
| 7096 |
} |
| 7097 |
$in->close(); |
| 7098 |
|
| 7099 |
if ( ! isset( $entries['mimetype'] ) || trim( $entries['mimetype'] ) !== 'application/epub+zip' ) { |
| 7100 |
return $src_path; // not a real EPUB, don't touch it |
| 7101 |
} |
| 7102 |
|
| 7103 |
// --- STEP 2: never rewrite signed or DRM-encrypted containers. --- |
| 7104 |
if ( isset( $entries['META-INF/signatures.xml'] ) ) { |
| 7105 |
$log( 'signed container, serving source' ); |
| 7106 |
return $src_path; |
| 7107 |
} |
| 7108 |
if ( isset( $entries['META-INF/encryption.xml'] ) ) { |
| 7109 |
$enc = $entries['META-INF/encryption.xml']; |
| 7110 |
$font_obfuscation_only = ( strpos( $enc, 'http://www.idpf.org/2008/embedding' ) !== false || strpos( $enc, 'http://ns.adobe.com/pdf/enc#RC' ) !== false ) |
| 7111 |
&& stripos( $enc, 'xmlenc#aes' ) === false |
| 7112 |
&& stripos( $enc, 'adept' ) === false; |
| 7113 |
if ( ! $font_obfuscation_only ) { |
| 7114 |
$log( 'encrypted/DRM container, serving source' ); |
| 7115 |
return $src_path; |
| 7116 |
} |
| 7117 |
// Font obfuscation: every entry name/bytes are preserved verbatim and |
| 7118 |
// dc:identifier is never touched, so the deobfuscation key still derives. |
| 7119 |
} |
| 7120 |
|
| 7121 |
// --- STEP 3: container.xml -> OPF path (clean XML, DOM-safe). --- |
| 7122 |
$opf_path = ''; |
| 7123 |
if ( isset( $entries['META-INF/container.xml'] ) ) { |
| 7124 |
$cx = new DOMDocument(); |
| 7125 |
$prev = libxml_use_internal_errors( true ); |
| 7126 |
$loaded = $cx->loadXML( $entries['META-INF/container.xml'], LIBXML_NONET ); |
| 7127 |
libxml_clear_errors(); |
| 7128 |
libxml_use_internal_errors( $prev ); |
| 7129 |
if ( $loaded ) { |
| 7130 |
foreach ( $cx->getElementsByTagName( 'rootfile' ) as $rf ) { |
| 7131 |
$fp = $rf->getAttribute( 'full-path' ); |
| 7132 |
if ( $fp !== '' ) { |
| 7133 |
$opf_path = $fp; |
| 7134 |
break; |
| 7135 |
} |
| 7136 |
} |
| 7137 |
} |
| 7138 |
} |
| 7139 |
if ( $opf_path === '' || ! isset( $entries[ $opf_path ] ) ) { |
| 7140 |
return $src_path; |
| 7141 |
} |
| 7142 |
$opf_dir = trim( dirname( $opf_path ), '/.' ); |
| 7143 |
$resolve = function ( $href ) use ( $opf_dir ) { |
| 7144 |
$href = preg_replace( '/[?#].*$/', '', (string) $href ); |
| 7145 |
$path = ( $opf_dir === '' ? '' : $opf_dir . '/' ) . $href; |
| 7146 |
$out = array(); |
| 7147 |
foreach ( explode( '/', $path ) as $seg ) { |
| 7148 |
if ( $seg === '' || $seg === '.' ) { |
| 7149 |
continue; |
| 7150 |
} |
| 7151 |
if ( $seg === '..' ) { |
| 7152 |
array_pop( $out ); |
| 7153 |
continue; |
| 7154 |
} |
| 7155 |
$out[] = $seg; |
| 7156 |
} |
| 7157 |
return implode( '/', $out ); |
| 7158 |
}; |
| 7159 |
|
| 7160 |
// --- STEP 4: parse the OPF (clean XML), read version, manifest, spine. --- |
| 7161 |
$opf = new DOMDocument(); |
| 7162 |
$prev = libxml_use_internal_errors( true ); |
| 7163 |
$opf_loaded = $opf->loadXML( $entries[ $opf_path ], LIBXML_NONET ); |
| 7164 |
libxml_clear_errors(); |
| 7165 |
libxml_use_internal_errors( $prev ); |
| 7166 |
if ( ! $opf_loaded ) { |
| 7167 |
return $src_path; |
| 7168 |
} |
| 7169 |
|
| 7170 |
$manifest_href = array(); |
| 7171 |
$manifest_media = array(); |
| 7172 |
foreach ( $opf->getElementsByTagName( 'item' ) as $item ) { |
| 7173 |
$id = $item->getAttribute( 'id' ); |
| 7174 |
if ( $id === '' ) { |
| 7175 |
continue; |
| 7176 |
} |
| 7177 |
$manifest_href[ $id ] = $resolve( $item->getAttribute( 'href' ) ); |
| 7178 |
$manifest_media[ $id ] = $item->getAttribute( 'media-type' ); |
| 7179 |
} |
| 7180 |
$spine_docs = array(); |
| 7181 |
foreach ( $opf->getElementsByTagName( 'itemref' ) as $iref ) { |
| 7182 |
$idref = $iref->getAttribute( 'idref' ); |
| 7183 |
if ( $idref === '' || ! isset( $manifest_href[ $idref ] ) ) { |
| 7184 |
continue; |
| 7185 |
} |
| 7186 |
$href = $manifest_href[ $idref ]; |
| 7187 |
$media = isset( $manifest_media[ $idref ] ) ? $manifest_media[ $idref ] : ''; |
| 7188 |
if ( $media === 'application/xhtml+xml' && isset( $entries[ $href ] ) ) { |
| 7189 |
$spine_docs[] = $href; |
| 7190 |
} |
| 7191 |
} |
| 7192 |
|
| 7193 |
$applied = false; |
| 7194 |
$opf_dirty = false; |
| 7195 |
|
| 7196 |
$manifest_node = $opf->getElementsByTagName( 'manifest' )->item( 0 ); |
| 7197 |
$spine_node = $opf->getElementsByTagName( 'spine' )->item( 0 ); |
| 7198 |
|
| 7199 |
// Which documents a scope targets: a list of content docs, or the string |
| 7200 |
// 'colophon' (a dedicated page we append). |
| 7201 |
$targets_for = function ( $target_scope ) use ( $spine_docs ) { |
| 7202 |
if ( $target_scope === 'colophon' ) { |
| 7203 |
return 'colophon'; |
| 7204 |
} |
| 7205 |
if ( empty( $spine_docs ) ) { |
| 7206 |
return array(); |
| 7207 |
} |
| 7208 |
return $target_scope === 'every' ? $spine_docs : array( $spine_docs[0] ); |
| 7209 |
}; |
| 7210 |
|
| 7211 |
// Prepare the QR image up front (fail-open: no QR if it can't be made). |
| 7212 |
$qr_png = ''; |
| 7213 |
$qr_abs = ''; |
| 7214 |
if ( $qr_enabled ) { |
| 7215 |
$qr_png = ebook_store_generate_qr_png( $qr_text ); |
| 7216 |
if ( $qr_png === '' ) { |
| 7217 |
$qr_enabled = false; |
| 7218 |
$log( 'QR unavailable (no GD/barcode support), delivering without QR' ); |
| 7219 |
} else { |
| 7220 |
$n = 0; |
| 7221 |
do { |
| 7222 |
$qr_rel = 'ebook-store-qr' . ( $n === 0 ? '' : '-' . $n ) . '.png'; |
| 7223 |
$qr_abs = $resolve( $qr_rel ); |
| 7224 |
$n++; |
| 7225 |
} while ( isset( $entries[ $qr_abs ] ) && $n < 50 ); |
| 7226 |
} |
| 7227 |
} |
| 7228 |
|
| 7229 |
$text_targets = $text !== '' ? $targets_for( $scope ) : array(); |
| 7230 |
$qr_targets = $qr_enabled ? $targets_for( $qr_scope ) : array(); |
| 7231 |
$qr_referenced = false; |
| 7232 |
|
| 7233 |
// --- STEP 5: inject text and/or QR into existing content documents. --- |
| 7234 |
$content_targets = array(); |
| 7235 |
if ( is_array( $text_targets ) ) { |
| 7236 |
$content_targets = array_merge( $content_targets, $text_targets ); |
| 7237 |
} |
| 7238 |
if ( is_array( $qr_targets ) ) { |
| 7239 |
$content_targets = array_merge( $content_targets, $qr_targets ); |
| 7240 |
} |
| 7241 |
$content_targets = array_values( array_unique( $content_targets ) ); |
| 7242 |
|
| 7243 |
foreach ( $content_targets as $doc ) { |
| 7244 |
$html = $entries[ $doc ]; |
| 7245 |
$changed = false; |
| 7246 |
|
| 7247 |
if ( is_array( $text_targets ) && in_array( $doc, $text_targets, true ) ) { |
| 7248 |
$new = ebook_store_epub_inject_fragment( $html, ebook_store_epub_watermark_fragment( $text, $style ), $position ); |
| 7249 |
if ( $new !== null ) { |
| 7250 |
$html = $new; |
| 7251 |
$changed = true; |
| 7252 |
} else { |
| 7253 |
$log( 'no <body> boundary, doc left unmarked', array( 'doc' => $doc ) ); |
| 7254 |
} |
| 7255 |
} |
| 7256 |
|
| 7257 |
if ( is_array( $qr_targets ) && in_array( $doc, $qr_targets, true ) ) { |
| 7258 |
$qr_src = ebook_store_epub_relative_path( dirname( $doc ), $qr_abs ); |
| 7259 |
$new = ebook_store_epub_inject_fragment( $html, ebook_store_epub_qr_fragment( $qr_src ), $qr_position ); |
| 7260 |
if ( $new !== null ) { |
| 7261 |
$html = $new; |
| 7262 |
$changed = true; |
| 7263 |
$qr_referenced = true; |
| 7264 |
} else { |
| 7265 |
$log( 'no <body> boundary, QR not placed', array( 'doc' => $doc ) ); |
| 7266 |
} |
| 7267 |
} |
| 7268 |
|
| 7269 |
if ( $changed ) { |
| 7270 |
$entries[ $doc ] = $html; |
| 7271 |
$applied = true; |
| 7272 |
} |
| 7273 |
} |
| 7274 |
|
| 7275 |
// --- STEP 6: colophon page carrying whatever targets 'colophon'. --- |
| 7276 |
if ( ( $text_targets === 'colophon' || $qr_targets === 'colophon' ) && $manifest_node && $spine_node ) { |
| 7277 |
$n = 0; |
| 7278 |
do { |
| 7279 |
$rel = 'ebook-store-licence' . ( $n === 0 ? '' : '-' . $n ) . '.xhtml'; |
| 7280 |
$abs = $resolve( $rel ); |
| 7281 |
$n++; |
| 7282 |
} while ( isset( $entries[ $abs ] ) && $n < 50 ); |
| 7283 |
|
| 7284 |
$body = ''; |
| 7285 |
if ( $text_targets === 'colophon' && $text !== '' ) { |
| 7286 |
$body .= ebook_store_epub_watermark_fragment( $text, 'box' ); |
| 7287 |
} |
| 7288 |
if ( $qr_targets === 'colophon' ) { |
| 7289 |
$body .= ebook_store_epub_qr_fragment( ebook_store_epub_relative_path( dirname( $abs ), $qr_abs ) ); |
| 7290 |
$qr_referenced = true; |
| 7291 |
} |
| 7292 |
|
| 7293 |
$page = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" |
| 7294 |
. '<!DOCTYPE html>' . "\n" |
| 7295 |
. '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="utf-8"/><title>' |
| 7296 |
. htmlspecialchars( __( 'Licence', 'ebook-store' ), ENT_QUOTES, 'UTF-8' ) |
| 7297 |
. '</title></head><body>' . $body . '</body></html>'; |
| 7298 |
|
| 7299 |
$mid = 'ebook-store-licence'; |
| 7300 |
$existing_ids = array_keys( $manifest_href ); |
| 7301 |
$k = 0; |
| 7302 |
while ( in_array( $mid, $existing_ids, true ) ) { |
| 7303 |
$k++; |
| 7304 |
$mid = 'ebook-store-licence-' . $k; |
| 7305 |
} |
| 7306 |
|
| 7307 |
$entries[ $abs ] = $page; |
| 7308 |
$order[] = $abs; |
| 7309 |
|
| 7310 |
$item_el = $opf->createElement( 'item' ); |
| 7311 |
$item_el->setAttribute( 'id', $mid ); |
| 7312 |
$item_el->setAttribute( 'href', $rel ); |
| 7313 |
$item_el->setAttribute( 'media-type', 'application/xhtml+xml' ); |
| 7314 |
$manifest_node->appendChild( $item_el ); |
| 7315 |
|
| 7316 |
$iref_el = $opf->createElement( 'itemref' ); |
| 7317 |
$iref_el->setAttribute( 'idref', $mid ); |
| 7318 |
$spine_node->appendChild( $iref_el ); |
| 7319 |
|
| 7320 |
$applied = true; |
| 7321 |
$opf_dirty = true; |
| 7322 |
} elseif ( $text_targets === 'colophon' || $qr_targets === 'colophon' ) { |
| 7323 |
$log( 'no manifest/spine, cannot add colophon' ); |
| 7324 |
} |
| 7325 |
|
| 7326 |
// --- Register the QR image in the archive + manifest (once, if used). --- |
| 7327 |
if ( $qr_referenced && $manifest_node ) { |
| 7328 |
$entries[ $qr_abs ] = $qr_png; |
| 7329 |
$order[] = $qr_abs; |
| 7330 |
|
| 7331 |
$qr_id = 'ebook-store-qr'; |
| 7332 |
$existing_ids = array(); |
| 7333 |
foreach ( $opf->getElementsByTagName( 'item' ) as $it ) { |
| 7334 |
$existing_ids[] = $it->getAttribute( 'id' ); |
| 7335 |
} |
| 7336 |
$k = 0; |
| 7337 |
while ( in_array( $qr_id, $existing_ids, true ) ) { |
| 7338 |
$k++; |
| 7339 |
$qr_id = 'ebook-store-qr-' . $k; |
| 7340 |
} |
| 7341 |
$qr_item = $opf->createElement( 'item' ); |
| 7342 |
$qr_item->setAttribute( 'id', $qr_id ); |
| 7343 |
$qr_item->setAttribute( 'href', ebook_store_epub_relative_path( $opf_dir, $qr_abs ) ); |
| 7344 |
$qr_item->setAttribute( 'media-type', 'image/png' ); |
| 7345 |
$manifest_node->appendChild( $qr_item ); |
| 7346 |
|
| 7347 |
$opf_dirty = true; |
| 7348 |
} |
| 7349 |
|
| 7350 |
// --- STEP 7: metadata stamp (DOM auto-escapes; pass raw text). --- |
| 7351 |
if ( $stamp && $text !== '' ) { |
| 7352 |
$metadata = $opf->getElementsByTagName( 'metadata' )->item( 0 ); |
| 7353 |
if ( $metadata ) { |
| 7354 |
$dc_ns = 'http://purl.org/dc/elements/1.1/'; |
| 7355 |
|
| 7356 |
// Stamp the resolved buyer line verbatim; it already reads as a |
| 7357 |
// licence statement, so we don't prepend our own prefix (which |
| 7358 |
// would double up on the default "Licensed to ..." template). |
| 7359 |
$contributor = $opf->createElementNS( $dc_ns, 'dc:contributor' ); |
| 7360 |
$contributor->appendChild( $opf->createTextNode( $text ) ); |
| 7361 |
$metadata->appendChild( $contributor ); |
| 7362 |
|
| 7363 |
$has_rights = false; |
| 7364 |
foreach ( $metadata->getElementsByTagName( 'rights' ) as $unused ) { |
| 7365 |
$has_rights = true; |
| 7366 |
break; |
| 7367 |
} |
| 7368 |
if ( ! $has_rights ) { |
| 7369 |
$rights = $opf->createElementNS( $dc_ns, 'dc:rights' ); |
| 7370 |
$rights->appendChild( $opf->createTextNode( $text ) ); |
| 7371 |
$metadata->appendChild( $rights ); |
| 7372 |
} |
| 7373 |
|
| 7374 |
$meta_wm = $opf->createElement( 'meta' ); |
| 7375 |
$meta_wm->setAttribute( 'name', 'ebook-store:watermark' ); |
| 7376 |
$meta_wm->setAttribute( 'content', $text ); |
| 7377 |
$metadata->appendChild( $meta_wm ); |
| 7378 |
|
| 7379 |
if ( $order_id > 0 ) { |
| 7380 |
$meta_order = $opf->createElement( 'meta' ); |
| 7381 |
$meta_order->setAttribute( 'name', 'ebook-store:order' ); |
| 7382 |
$meta_order->setAttribute( 'content', (string) $order_id ); |
| 7383 |
$metadata->appendChild( $meta_order ); |
| 7384 |
} |
| 7385 |
|
| 7386 |
$applied = true; |
| 7387 |
$opf_dirty = true; |
| 7388 |
} |
| 7389 |
} |
| 7390 |
|
| 7391 |
// Re-serialize the OPF only if we changed it. |
| 7392 |
if ( $opf_dirty ) { |
| 7393 |
$xml = $opf->saveXML(); |
| 7394 |
if ( $xml !== false && $xml !== '' ) { |
| 7395 |
$entries[ $opf_path ] = $xml; // saveXML()===false must never blank the OPF |
| 7396 |
} |
| 7397 |
} |
| 7398 |
|
| 7399 |
if ( ! $applied ) { |
| 7400 |
return $src_path; // nothing actually changed; don't rewrite |
| 7401 |
} |
| 7402 |
|
| 7403 |
// --- STEP 8: repack, mimetype FIRST + STORED (the make-or-break rule). --- |
| 7404 |
$out = new ZipArchive(); |
| 7405 |
if ( $out->open( $dest_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) !== true ) { |
| 7406 |
return $src_path; |
| 7407 |
} |
| 7408 |
$out->addFromString( 'mimetype', 'application/epub+zip' ); |
| 7409 |
// Class constant only — the global ZIP_CM_STORE is undefined on this build. |
| 7410 |
$out->setCompressionName( 'mimetype', ZipArchive::CM_STORE ); |
| 7411 |
foreach ( $order as $name ) { |
| 7412 |
if ( $name === 'mimetype' || ! isset( $entries[ $name ] ) || ! is_string( $entries[ $name ] ) ) { |
| 7413 |
continue; |
| 7414 |
} |
| 7415 |
if ( ! $out->addFromString( $name, $entries[ $name ] ) ) { |
| 7416 |
$out->close(); |
| 7417 |
@unlink( $dest_path ); |
| 7418 |
return $src_path; |
| 7419 |
} |
| 7420 |
} |
| 7421 |
if ( $out->close() !== true || ! file_exists( $dest_path ) ) { |
| 7422 |
@unlink( $dest_path ); |
| 7423 |
return $src_path; |
| 7424 |
} |
| 7425 |
|
| 7426 |
// --- STEP 9: verify the invariant held, else discard and fail open. --- |
| 7427 |
$verify = new ZipArchive(); |
| 7428 |
if ( $verify->open( $dest_path ) !== true ) { |
| 7429 |
@unlink( $dest_path ); |
| 7430 |
return $src_path; |
| 7431 |
} |
| 7432 |
$first = $verify->statIndex( 0 ); |
| 7433 |
$verify->close(); |
| 7434 |
if ( ! $first || $first['name'] !== 'mimetype' || (int) $first['comp_method'] !== ZipArchive::CM_STORE ) { |
| 7435 |
@unlink( $dest_path ); |
| 7436 |
return $src_path; |
| 7437 |
} |
| 7438 |
|
| 7439 |
return $dest_path; |
| 7440 |
} catch ( \Throwable $e ) { |
| 7441 |
if ( file_exists( $dest_path ) ) { |
| 7442 |
@unlink( $dest_path ); |
| 7443 |
} |
| 7444 |
$log( 'watermark failed', array( 'err' => $e->getMessage() ) ); |
| 7445 |
return $src_path; |
| 7446 |
} |
| 7447 |
} |
| 7448 |
|
| 7449 |
function ebook_encrypt_pdf($r = null, $post_id = null) { |
| 7450 |
//@ini_set('display_errors',1); |
| 7451 |
//@error_reporting(E_ALL); |
| 7452 |
if ($r == false) { |
| 7453 |
$r = $_REQUEST; |
| 7454 |
} |
| 7455 |
global $ebook_email_delivery, $ebook_qr_text, $ebook_png_path, $ebook_pngname, $attachment, $pdfHeaderText; |
| 7456 |
require_once('fpdi/qrcode.class.php'); |
| 7457 |
|
| 7458 |
// Resolve the order this encryption belongs to first, so the output can go in |
| 7459 |
// that order's protected cache directory (in uploads, HTTP-denied) rather than |
| 7460 |
// the web-accessible plugin folder — and so a missing folder can't fatal. |
| 7461 |
$target_order_id = 0; |
| 7462 |
if ( ! empty( $r['order_id'] ) ) { |
| 7463 |
$target_order_id = (int) $r['order_id']; |
| 7464 |
} elseif ( ! empty( $ebook_email_delivery['order']['order_id'] ) ) { |
| 7465 |
$target_order_id = (int) $ebook_email_delivery['order']['order_id']; |
| 7466 |
} elseif ( ! empty( $post_id ) ) { |
| 7467 |
$target_order_id = (int) $post_id; |
| 7468 |
} |
| 7469 |
|
| 7470 |
$cache_dir = ebook_store_get_cache_dir( $target_order_id ); |
| 7471 |
if ( $cache_dir === '' ) { |
| 7472 |
// Uploads unusable — fall back to the system temp dir, never the web root. |
| 7473 |
$cache_dir = untrailingslashit( get_temp_dir() ); |
| 7474 |
} |
| 7475 |
|
| 7476 |
$ebook_qr_text = ebook_store_get_order_verification_url( isset( $r['ebook_key'] ) ? $r['ebook_key'] : '' ); |
| 7477 |
// Watermark text is drawn into a PDF, not HTML — esc_attr() would leak entities |
| 7478 |
// ("Tom & Jerry" -> "Tom & Jerry"). Strip control chars instead. |
| 7479 |
$pdfHeaderText = ebook_store_sanitize_pdf_text( (string) get_option('buyer_info_text') ); |
| 7480 |
|
| 7481 |
foreach ($r as $k => $v) { |
| 7482 |
if ( is_scalar( $v ) ) { |
| 7483 |
$pdfHeaderText = str_replace("%%$k%%", ebook_store_sanitize_pdf_text( (string) $v ), $pdfHeaderText); |
| 7484 |
} |
| 7485 |
} |
| 7486 |
// The QR code is now drawn natively by TCPDF as vectors; no PNG is fetched |
| 7487 |
// from an external service, and no GD / allow_url_fopen is needed. |
| 7488 |
if (@is_array($attachment)) { |
| 7489 |
@$file = $attachment[0]['file']; |
| 7490 |
} |
| 7491 |
if (@!$file) { |
| 7492 |
$file = $ebook_email_delivery['attachment'][0]['file']; |
| 7493 |
} |
| 7494 |
// ($target_order_id was resolved at the top so the cache dir could be built.) |
| 7495 |
$password = ebook_store_get_password_for_order($target_order_id, array( |
| 7496 |
'password' => isset($r['password']) ? $r['password'] : '', |
| 7497 |
'payer_email' => isset($r['payer_email']) ? $r['payer_email'] : '', |
| 7498 |
'md5_nonce' => isset($r['md5_nonce']) ? $r['md5_nonce'] : '', |
| 7499 |
'ebook_key' => isset($r['ebook_key']) ? $r['ebook_key'] : '', |
| 7500 |
)); |
| 7501 |
|
| 7502 |
if (isset($ebook_email_delivery['order']) && is_array($ebook_email_delivery['order'])) { |
| 7503 |
$ebook_email_delivery['order']['password'] = $password; |
| 7504 |
} |
| 7505 |
|
| 7506 |
//free files encrypt with user email? |
| 7507 |
|
| 7508 |
|
| 7509 |
//$password = $r['payer_email']; |
| 7510 |
// if (get_option('ebook_store_random_password') == 1) { |
| 7511 |
// if ($ebook_store_random_password != '') { |
| 7512 |
// $r['password'] = $ebook_store_random_password; |
| 7513 |
// } |
| 7514 |
// if (@$r['password'] != '') { |
| 7515 |
// $password = $r['password']; |
| 7516 |
// } |
| 7517 |
// } |
| 7518 |
|
| 7519 |
// if (get_option('ebook_store_blank_password') == 1) { |
| 7520 |
// $r['password'] = ''; |
| 7521 |
// $password = ''; |
| 7522 |
// } |
| 7523 |
|
| 7524 |
//random password for non logged in users |
| 7525 |
// if (!is_user_logged_in() && get_option('formEnabled') < 1) { |
| 7526 |
// if (get_option('ebook_store_random_password') == 1) { |
| 7527 |
// $password = substr(md5(rand()),0,8); |
| 7528 |
// //$password = 'proba'; |
| 7529 |
// $ebook_store_random_password = $password; |
| 7530 |
// if ($post_id != null) { |
| 7531 |
// @update_post_meta($post_id,'password',$password); |
| 7532 |
// } |
| 7533 |
|
| 7534 |
// // Additional code to handle the generated password |
| 7535 |
// } |
| 7536 |
//} |
| 7537 |
// if (is_user_logged_in()) { |
| 7538 |
// if (get_option('ebook_store_random_password') == 1) { |
| 7539 |
// $password = substr(md5(rand()),0,8); |
| 7540 |
// //$password = 'proba'; |
| 7541 |
// $ebook_store_random_password = $password; |
| 7542 |
// if ($post_id != null) { |
| 7543 |
// @update_post_meta($post_id,'password',$password); |
| 7544 |
// } |
| 7545 |
|
| 7546 |
// // Additional code to handle the generated password |
| 7547 |
// } else { |
| 7548 |
// if (get_option('ebook_store_blank_password') < 1) { |
| 7549 |
// $current_user = wp_get_current_user(); |
| 7550 |
// $password = $current_user->user_email; |
| 7551 |
// if ($post_id != null) { |
| 7552 |
// @update_post_meta($post_id,'password',$password); |
| 7553 |
// } |
| 7554 |
// } |
| 7555 |
// } |
| 7556 |
// } |
| 7557 |
|
| 7558 |
//wp_die($password . ' post id ' . $post_id); |
| 7559 |
//error_log('password ' . $password); |
| 7560 |
|
| 7561 |
$owner_password = (string) get_option('ebook_store_owner_password'); |
| 7562 |
|
| 7563 |
// Write the finished, buyer-watermarked PDF into the protected cache dir (in |
| 7564 |
// uploads, HTTP-denied), NOT the plugin folder — those files carry the buyer's |
| 7565 |
// email/IP in the watermark and were previously fetchable, since the plugin |
| 7566 |
// .htaccess denies .txt/.log but not .pdf. |
| 7567 |
$destfile = $cache_dir . '/' . basename( $file ); |
| 7568 |
|
| 7569 |
|
| 7570 |
if ($target_order_id) { |
| 7571 |
update_post_meta($target_order_id,'encrypted_pdf','in_progress'); |
| 7572 |
} |
| 7573 |
ebook_store_raise_pdf_memory(); |
| 7574 |
$pdf = new QRPDF(); |
| 7575 |
$pdf->watermarkText = $pdfHeaderText; |
| 7576 |
$pdf->qrText = $ebook_qr_text; |
| 7577 |
$pdf->stampCount = 0; |
| 7578 |
try { |
| 7579 |
$pagecount = $pdf->setSourceFile($file); |
| 7580 |
} catch (Exception $e) { |
| 7581 |
wp_die( esc_html( sprintf( |
| 7582 |
/* translators: %s: PDF import error. */ |
| 7583 |
__( 'This PDF could not be processed for protection. Please re-save it as a standard PDF or turn encryption off. Details: %s', 'ebook-store' ), |
| 7584 |
$e->getMessage() |
| 7585 |
) ) ); |
| 7586 |
} |
| 7587 |
|
| 7588 |
for ($loop = 1; $loop <= $pagecount; $loop++) { |
| 7589 |
$tplidx = $pdf->importPage($loop); |
| 7590 |
$size = $pdf->getTemplateSize($tplidx); |
| 7591 |
$pdf->AddPage($size['orientation'], array($size['width'], $size['height'])); |
| 7592 |
$pdf->useTemplate($tplidx); |
| 7593 |
$pdf->stampCurrentPage(); |
| 7594 |
} |
| 7595 |
|
| 7596 |
$pdf->applyProtection($password, $owner_password); |
| 7597 |
$pdf->Output($destfile, 'F'); |
| 7598 |
if ($target_order_id) { |
| 7599 |
update_post_meta($target_order_id,'encrypted_pdf',wp_slash($destfile)); |
| 7600 |
} |
| 7601 |
//make sure enc file is attached |
| 7602 |
//file_put_contents('ebook-store-password-debug.txt', $password . "\n" . print_r($r, true), FILE_APPEND); |
| 7603 |
|
| 7604 |
$ebook_email_delivery['attachment'][0]['file'] = $destfile; |
| 7605 |
$isPdf = true; |
| 7606 |
return $destfile; |
| 7607 |
} |
| 7608 |
function ebook_store_missing_gd() { |
| 7609 |
?> |
| 7610 |
<div class="updated"> |
| 7611 |
<p><?php _e( 'Your hosting provide is missing GD PNG support, please contact your provider to enable GD in order to be able to process PNG files.', 'ebook-store' ); ?></p> |
| 7612 |
</div> |
| 7613 |
<?php |
| 7614 |
} |
| 7615 |
function ebook_store_admin_notice() { |
| 7616 |
?> |
| 7617 |
<div class="updated"> |
| 7618 |
<p><?php _e( 'You need to create a "Thank you" landing page with the text/shortcode <input type="text" size=20 value="[ebook_thank_you]" />, where you want the "Thank You" page content to appear, then select that page in Ebook Store options under Settings menu. |
| 7619 |
<br /> |
| 7620 |
<p class="submit"><input type="button" name="button" id="button" class="button button-primary" value="Fix automatically" onClick="window.location = \'options-general.php?page=ebook_options.php&task=fixThankYouPage\';"></p> |
| 7621 |
', 'ebook-store' ); ?></p> |
| 7622 |
</div> |
| 7623 |
<?php |
| 7624 |
} |
| 7625 |
function ebook_store_admin_notice_paypal() { |
| 7626 |
if ( ! ebook_store_is_paypal_enabled() ) { |
| 7627 |
return; |
| 7628 |
} |
| 7629 |
?> |
| 7630 |
<div class="updated"> |
| 7631 |
<p><?php _e( 'You have not yet filled in your <b>PayPal account</b>. Please do that in <a href="options-general.php?page=ebook_options.php&tab=PayPal">Settings > Ebook Store > PayPal</a> in order to be able to receive payments.', 'ebooks-store' ); ?></p> |
| 7632 |
</div> |
| 7633 |
<?php |
| 7634 |
} |
| 7635 |
function ebook_store_admin_notice_autocompleteorders() { |
| 7636 |
?> |
| 7637 |
<div class="updated"> |
| 7638 |
<p><?php _e( 'If you plan to use Ebook Store + WooCommerce integration, we recommend installing the free plugin <a target="_blank" href="https://wordpress.org/plugins/autocomplete-woocommerce-orders/">Autocomplete WooCommerce Orders</a> so you can set it up to automatically complete orders for virtual goods to avoid processing status.', 'ebooks-store' ); ?></p> |
| 7639 |
</div> |
| 7640 |
<?php |
| 7641 |
} |
| 7642 |
|
| 7643 |
function ebook_store_set_messages($messages) { |
| 7644 |
global $post, $post_ID; |
| 7645 |
$post_type = get_post_type( $post_ID ); |
| 7646 |
|
| 7647 |
$obj = get_post_type_object($post_type); |
| 7648 |
$singular = $obj->labels->singular_name; |
| 7649 |
if ($post_type == 'ebook') { |
| 7650 |
$messages[$post_type][1] = 'Ebook has been saved! Now copy and paste this code in the article / page where you want the ebook order form to appear: <input onClick="" type="text" size=30 value=\'[ebook_store ebook_id="' . $post_ID . '"]\'>'; |
| 7651 |
} |
| 7652 |
return $messages; |
| 7653 |
} |
| 7654 |
function ebook_store_remove_parmelink( $return ) { |
| 7655 |
if (@$post_id == false) { |
| 7656 |
$post_id = get_the_ID(); |
| 7657 |
} |
| 7658 |
if ('ebook' === get_post_type( $post_id )) { |
| 7659 |
$return = ''; |
| 7660 |
} |
| 7661 |
return $return; |
| 7662 |
} |
| 7663 |
|
| 7664 |
function ebook_admin_css() { |
| 7665 |
global $post_type; |
| 7666 |
$post_types = array( |
| 7667 |
/* set post types */ |
| 7668 |
'ebook', |
| 7669 |
); |
| 7670 |
if(in_array($post_type, $post_types)) |
| 7671 |
echo '<style type="text/css">#post-preview, #view-post-btn{display: none;}</style>'; |
| 7672 |
} |
| 7673 |
function ebook_store_set_columns( $columns ) { |
| 7674 |
|
| 7675 |
$columns = array( |
| 7676 |
'cb' => '<input type="checkbox" />', |
| 7677 |
'cover' => __( 'Cover', 'ebook-store' ), |
| 7678 |
'title' => __( 'Ebook', 'ebook-store' ), |
| 7679 |
'date' => __( 'Date', 'ebook-store' ), |
| 7680 |
'price' => __( 'Price', 'ebook-store' ), |
| 7681 |
'formats' => __( 'Formats', 'ebook-store' ), |
| 7682 |
'sales' => __( 'Sales', 'ebook-store' ), |
| 7683 |
'embed_code' => __('Shortcode & Links', 'ebook-store') |
| 7684 |
); |
| 7685 |
|
| 7686 |
return $columns; |
| 7687 |
} |
| 7688 |
|
| 7689 |
function ebook_store_columns_output( $column, $post_id ) { |
| 7690 |
global $post; |
| 7691 |
$ebook = new EbookStoreEbook($post_id); |
| 7692 |
switch( $column ) { |
| 7693 |
case 'embed_code': |
| 7694 |
$shortcode_id = 'ebook-shortcode-' . $post_id; |
| 7695 |
$link_id = 'ebook-direct-link-' . $post_id; |
| 7696 |
$shortcode = '[ebook_store ebook_id="' . $post_id . '"]'; |
| 7697 |
$order_url = add_query_arg( 'task', 'direct_order', get_permalink( $post_id ) ); |
| 7698 |
|
| 7699 |
echo '<div class="ebook-list-shortcuts">'; |
| 7700 |
echo '<div class="ebook-list-shortcuts__field">'; |
| 7701 |
echo '<span class="ebook-list-shortcuts__label">' . esc_html__( 'Shortcode', 'ebook-store' ) . '</span>'; |
| 7702 |
echo '<div class="ebook-list-shortcuts__control">'; |
| 7703 |
echo '<input type="text" id="' . esc_attr( $shortcode_id ) . '" readonly value="' . esc_attr( $shortcode ) . '" />'; |
| 7704 |
echo '<button type="button" class="button button-secondary ebook-list-copy" data-copy-target="#' . esc_attr( $shortcode_id ) . '" data-copied-text="' . esc_attr__( 'Copied!', 'ebook-store' ) . '">' . esc_html__( 'Copy', 'ebook-store' ) . '</button>'; |
| 7705 |
echo '</div>'; |
| 7706 |
echo '</div>'; |
| 7707 |
echo '<div class="ebook-list-shortcuts__field">'; |
| 7708 |
echo '<span class="ebook-list-shortcuts__label">' . esc_html__( 'Direct order link', 'ebook-store' ) . '</span>'; |
| 7709 |
echo '<div class="ebook-list-shortcuts__control">'; |
| 7710 |
echo '<input type="text" id="' . esc_attr( $link_id ) . '" readonly value="' . esc_attr( $order_url ) . '" />'; |
| 7711 |
echo '<button type="button" class="button button-secondary ebook-list-copy" data-copy-target="#' . esc_attr( $link_id ) . '" data-copied-text="' . esc_attr__( 'Copied!', 'ebook-store' ) . '">' . esc_html__( 'Copy', 'ebook-store' ) . '</button>'; |
| 7712 |
echo '</div>'; |
| 7713 |
echo '</div>'; |
| 7714 |
echo '</div>'; |
| 7715 |
break; |
| 7716 |
case 'sales': |
| 7717 |
$sales_index = ebook_store_get_ebook_sales_index(); |
| 7718 |
$sales = isset( $sales_index[ $post_id ] ) ? $sales_index[ $post_id ] : array( |
| 7719 |
'orders' => 0, |
| 7720 |
'total' => 0.0, |
| 7721 |
'last_7d' => 0.0, |
| 7722 |
'last_30d' => 0.0, |
| 7723 |
'latest_order_gmt' => '', |
| 7724 |
); |
| 7725 |
$currency = ebook_store_get_store_currency(); |
| 7726 |
$latest_order = $sales['latest_order_gmt'] !== '' ? human_time_diff( strtotime( $sales['latest_order_gmt'] . ' UTC' ), current_time( 'timestamp', true ) ) : __( 'No orders yet', 'ebook-store' ); |
| 7727 |
|
| 7728 |
echo '<div class="ebook-list-sales">'; |
| 7729 |
echo '<strong class="ebook-list-sales__amount">' . esc_html( ebook_store_order_money_display( $sales['total'], $currency ) ) . '</strong>'; |
| 7730 |
echo '<div class="ebook-list-sales__pills">'; |
| 7731 |
echo '<span class="ebook-list-pill is-green">' . esc_html( sprintf( __( '%s orders', 'ebook-store' ), number_format_i18n( $sales['orders'] ) ) ) . '</span>'; |
| 7732 |
echo '<span class="ebook-list-pill">' . esc_html( sprintf( __( '7d %s', 'ebook-store' ), ebook_store_order_money_display( $sales['last_7d'], $currency ) ) ) . '</span>'; |
| 7733 |
echo '<span class="ebook-list-pill is-warm">' . esc_html( sprintf( __( '30d %s', 'ebook-store' ), ebook_store_order_money_display( $sales['last_30d'], $currency ) ) ) . '</span>'; |
| 7734 |
echo '</div>'; |
| 7735 |
echo '<div class="ebook-list-sales__meta">' . esc_html( $sales['latest_order_gmt'] !== '' ? sprintf( __( 'Latest order %s ago', 'ebook-store' ), $latest_order ) : $latest_order ) . '</div>'; |
| 7736 |
echo '</div>'; |
| 7737 |
break; |
| 7738 |
case 'price': |
| 7739 |
$ebook = get_post_meta( get_the_ID(), 'ebook', true ); |
| 7740 |
$price = 0.0; |
| 7741 |
if ( ! is_array( $ebook ) ) { |
| 7742 |
echo '<div class="ebook-list-price"><strong class="ebook-list-price__amount">-</strong></div>'; |
| 7743 |
break; |
| 7744 |
} |
| 7745 |
if ( isset( $ebook['ebook_price'] ) ) { |
| 7746 |
$price = (float) $ebook['ebook_price']; |
| 7747 |
} |
| 7748 |
echo '<div class="ebook-list-price">'; |
| 7749 |
echo '<strong class="ebook-list-price__amount">' . esc_html( ebook_store_order_money_display( $price, ebook_store_get_store_currency() ) ) . '</strong>'; |
| 7750 |
echo '<div class="ebook-list-price__pills">'; |
| 7751 |
echo '<span class="ebook-list-pill ' . esc_attr( $price > 0 ? 'is-green' : 'is-warm' ) . '">' . esc_html( $price > 0 ? __( 'Paid title', 'ebook-store' ) : __( 'Free title', 'ebook-store' ) ) . '</span>'; |
| 7752 |
if ( ! empty( $ebook['donate_or_download'] ) && $ebook['donate_or_download'] === 'donate' ) { |
| 7753 |
echo '<span class="ebook-list-pill">' . esc_html__( 'Donation mode', 'ebook-store' ) . '</span>'; |
| 7754 |
} |
| 7755 |
echo '</div>'; |
| 7756 |
echo '</div>'; |
| 7757 |
break; |
| 7758 |
case 'cover': |
| 7759 |
$cover = get_post_meta( get_the_ID(), 'ebook_wp_custom_attachment_cover', true ); |
| 7760 |
$cover = is_array( $cover ) ? $cover : array(); |
| 7761 |
|
| 7762 |
echo '<div class="ebook-list-cover">'; |
| 7763 |
echo '<div class="ebook-list-cover__frame">'; |
| 7764 |
if ( ! empty( $cover['url'] ) ) { |
| 7765 |
echo '<img src="' . esc_url( $cover['url'] ) . '" alt="" />'; |
| 7766 |
} else { |
| 7767 |
echo '<span class="ebook-list-cover__placeholder">' . esc_html__( 'No cover uploaded', 'ebook-store' ) . '</span>'; |
| 7768 |
} |
| 7769 |
echo '</div>'; |
| 7770 |
echo '<span class="ebook-list-status">' . esc_html( get_post_status( $post_id ) ) . '</span>'; |
| 7771 |
echo '</div>'; |
| 7772 |
|
| 7773 |
break; |
| 7774 |
case 'formats': |
| 7775 |
$formats_found = array(); |
| 7776 |
if ( is_array( $ebook->files ) ) { |
| 7777 |
foreach ( array_keys( $ebook->files ) as $key => $value ) { |
| 7778 |
if ( strpos( $value, '_size' ) === false ) { |
| 7779 |
$format = $value; |
| 7780 |
$icon_url = plugins_url( 'img/' . $value . '.png', __FILE__ ); |
| 7781 |
$title = basename( $ebook->files[ $format ] ) . ' ' . $ebook->human_filesize( $ebook->files[ $format . '_size' ] ); |
| 7782 |
$formats_found[] = '<span class="ebook-list-formats__item"><img title="' . esc_attr( $title ) . '" width="30" src="' . esc_url( $icon_url ) . '" alt="' . esc_attr( strtoupper( $format ) ) . '" /></span>'; |
| 7783 |
} |
| 7784 |
} |
| 7785 |
echo '<div class="ebook-list-formats">'; |
| 7786 |
echo implode( '', $formats_found ); |
| 7787 |
if ( empty( $formats_found ) ) { |
| 7788 |
echo '<span class="ebook-list-pill">' . esc_html__( 'No files attached', 'ebook-store' ) . '</span>'; |
| 7789 |
} |
| 7790 |
echo '</div>'; |
| 7791 |
$testable = array(); |
| 7792 |
if ( ! empty( $ebook->files['pdf'] ) && file_exists( $ebook->files['pdf'] ) ) { |
| 7793 |
$testable['pdf'] = __( 'PDF', 'ebook-store' ); |
| 7794 |
} |
| 7795 |
if ( ! empty( $ebook->files['epub'] ) && file_exists( $ebook->files['epub'] ) ) { |
| 7796 |
$testable['epub'] = __( 'ePub', 'ebook-store' ); |
| 7797 |
} |
| 7798 |
if ( ! empty( $testable ) ) { |
| 7799 |
$ebook_test_link = function ( $format ) use ( $post_id ) { |
| 7800 |
return wp_nonce_url( |
| 7801 |
admin_url( 'admin-post.php?action=ebook_store_test_encryption&ebook_id=' . $post_id . '&format=' . $format ), |
| 7802 |
'ebook_store_test_encryption_' . $post_id |
| 7803 |
); |
| 7804 |
}; |
| 7805 |
if ( count( $testable ) === 1 ) { |
| 7806 |
$only = array_key_first( $testable ); |
| 7807 |
$label = $only === 'epub' |
| 7808 |
? __( 'Test watermark', 'ebook-store' ) |
| 7809 |
: __( 'Test encryption (password: test)', 'ebook-store' ); |
| 7810 |
echo '<div class="ebook-format-test-link"><a class="button button-small" href="' . esc_url( $ebook_test_link( $only ) ) . '">' . esc_html( $label ) . '</a></div>'; |
| 7811 |
} else { |
| 7812 |
// Both formats present: one Test control that asks which file to use. |
| 7813 |
echo '<details class="ebook-format-test">'; |
| 7814 |
echo '<summary class="button button-small">' . esc_html__( 'Test protection…', 'ebook-store' ) . '</summary>'; |
| 7815 |
echo '<div class="ebook-format-test__menu">'; |
| 7816 |
echo '<span class="ebook-format-test__prompt">' . esc_html__( 'Test with which file?', 'ebook-store' ) . '</span>'; |
| 7817 |
foreach ( $testable as $format => $flabel ) { |
| 7818 |
/* translators: %s: file format, e.g. PDF or ePub. */ |
| 7819 |
echo '<a class="button button-small" href="' . esc_url( $ebook_test_link( $format ) ) . '">' . esc_html( sprintf( __( 'Test %s', 'ebook-store' ), $flabel ) ) . '</a>'; |
| 7820 |
} |
| 7821 |
echo '</div></details>'; |
| 7822 |
} |
| 7823 |
} |
| 7824 |
} |
| 7825 |
break; |
| 7826 |
|
| 7827 |
/* Just break out of the switch statement for everything else. */ |
| 7828 |
default : |
| 7829 |
break; |
| 7830 |
} |
| 7831 |
} |
| 7832 |
add_action('admin_post_ebook_store_test_encryption', 'ebook_store_test_encryption'); |
| 7833 |
add_action('admin_post_nopriv_ebook_store_test_encryption', 'ebook_store_test_encryption'); |
| 7834 |
function ebook_store_pre_get_shortlink( $false, $post_id ) { |
| 7835 |
return 'ebook' === get_post_type( $post_id ) ? '' : $false; |
| 7836 |
} |
| 7837 |
function ebook_mime_types($mime_types){ |
| 7838 |
// These have to be the real types. fileinfo reports an ePub as |
| 7839 |
// application/epub+zip, and when the map here said octet-stream instead, |
| 7840 |
// wp_check_filetype_and_ext() saw a mismatch and returned ext=false and |
| 7841 |
// type=false for a perfectly valid book — which is what made the uploader |
| 7842 |
// answer "that file type is not allowed here" for every .epub. |
| 7843 |
$mime_types['epub'] = 'application/epub+zip'; |
| 7844 |
$mime_types['mobi'] = 'application/x-mobipocket-ebook'; |
| 7845 |
$mime_types['txt'] = 'text/plain'; |
| 7846 |
$mime_types['mp3'] = 'audio/mpeg'; |
| 7847 |
|
| 7848 |
return $mime_types; |
| 7849 |
} |
| 7850 |
/** |
| 7851 |
* The directory that holds captured checkout-form data. |
| 7852 |
* |
| 7853 |
* @return string Absolute path with a trailing slash, or '' on failure. |
| 7854 |
*/ |
| 7855 |
function ebook_store_get_form_data_dir() { |
| 7856 |
static $dir = null; |
| 7857 |
|
| 7858 |
if ( $dir !== null ) { |
| 7859 |
return $dir; |
| 7860 |
} |
| 7861 |
|
| 7862 |
$uploads = wp_upload_dir(); |
| 7863 |
if ( ! empty( $uploads['error'] ) ) { |
| 7864 |
$dir = ''; |
| 7865 |
return $dir; |
| 7866 |
} |
| 7867 |
|
| 7868 |
$dir = trailingslashit( $uploads['basedir'] ) . 'ebook_store_data/'; |
| 7869 |
|
| 7870 |
if ( ! is_dir( $dir ) && ! wp_mkdir_p( $dir ) ) { |
| 7871 |
$dir = ''; |
| 7872 |
return $dir; |
| 7873 |
} |
| 7874 |
|
| 7875 |
// Buyer names and email addresses live here; they must not be fetchable. |
| 7876 |
if ( ! file_exists( $dir . '.htaccess' ) ) { |
| 7877 |
@file_put_contents( $dir . '.htaccess', "Require all denied\n<IfModule !mod_authz_core.c>\nDeny from all\n</IfModule>\n" ); |
| 7878 |
} |
| 7879 |
if ( ! file_exists( $dir . 'index.php' ) ) { |
| 7880 |
@file_put_contents( $dir . 'index.php', "<?php\n// Silence is golden.\n" ); |
| 7881 |
} |
| 7882 |
|
| 7883 |
return $dir; |
| 7884 |
} |
| 7885 |
|
| 7886 |
/** |
| 7887 |
* Turn a request-supplied nonce into a safe, fixed-shape filename. |
| 7888 |
* |
| 7889 |
* The read side used to interpolate this value straight into a path, giving an |
| 7890 |
* arbitrary-file-read primitive via ?md5_nonce=../../../etc/passwd. |
| 7891 |
* |
| 7892 |
* @param string $md5_nonce Value from the request. |
| 7893 |
* @return string Filename, or '' when the value is not a plausible token. |
| 7894 |
*/ |
| 7895 |
function ebook_store_get_form_filename( $md5_nonce ) { |
| 7896 |
$md5_nonce = preg_replace( '/[^a-zA-Z0-9]/', '', (string) $md5_nonce ); |
| 7897 |
|
| 7898 |
if ( $md5_nonce === '' || strlen( $md5_nonce ) > 64 ) { |
| 7899 |
return ''; |
| 7900 |
} |
| 7901 |
|
| 7902 |
return 'ebook_store_form_' . $md5_nonce . '.json'; |
| 7903 |
} |
| 7904 |
|
| 7905 |
function ebook_store_formContent() { |
| 7906 |
$task = isset( $_REQUEST['task'] ) ? sanitize_key( wp_unslash( $_REQUEST['task'] ) ) : ''; |
| 7907 |
|
| 7908 |
if ( $task === 'formContent' ) { |
| 7909 |
// The stored markup is authored by an administrator, but it is served to |
| 7910 |
// anonymous visitors, so it still goes through wp_kses_post(). |
| 7911 |
echo wp_kses_post( get_option( 'formContent' ) ); |
| 7912 |
die(); |
| 7913 |
} |
| 7914 |
|
| 7915 |
if ( $task === 'ebook_store_form_input' ) { |
| 7916 |
ebook_store_save_form( isset( $_POST['md5_nonce'] ) ? wp_unslash( $_POST['md5_nonce'] ) : '', $_POST ); |
| 7917 |
} |
| 7918 |
} |
| 7919 |
/** |
| 7920 |
* Persist the buyer details captured by the checkout form. |
| 7921 |
* |
| 7922 |
* Called from an unauthenticated request (the buyer has not logged in), so it |
| 7923 |
* is deliberately narrow: a fixed filename shape, a size cap, a field cap, and |
| 7924 |
* a rate limit so it cannot be used to fill the disk. |
| 7925 |
* |
| 7926 |
* @param string $md5_nonce Order token. |
| 7927 |
* @param array $data Posted fields. |
| 7928 |
*/ |
| 7929 |
function ebook_store_save_form( $md5_nonce, $data ) { |
| 7930 |
$filename = ebook_store_get_form_filename( $md5_nonce ); |
| 7931 |
$dir = ebook_store_get_form_data_dir(); |
| 7932 |
|
| 7933 |
if ( $filename === '' || $dir === '' ) { |
| 7934 |
status_header( 400 ); |
| 7935 |
die( esc_html__( 'Invalid request.', 'ebook-store' ) ); |
| 7936 |
} |
| 7937 |
|
| 7938 |
// One write per token per minute is plenty for a real checkout, and it stops |
| 7939 |
// this endpoint being used as an unlimited disk-fill primitive. |
| 7940 |
$rate_key = 'ebook_store_form_rl_' . md5( $filename ); |
| 7941 |
if ( get_transient( $rate_key ) ) { |
| 7942 |
status_header( 429 ); |
| 7943 |
die( esc_html__( 'Please wait a moment and try again.', 'ebook-store' ) ); |
| 7944 |
} |
| 7945 |
set_transient( $rate_key, 1, MINUTE_IN_SECONDS ); |
| 7946 |
|
| 7947 |
$json = array(); |
| 7948 |
$fields = 0; |
| 7949 |
$skip = array( 'task', 'md5_nonce' ); |
| 7950 |
|
| 7951 |
foreach ( (array) $data as $k => $v ) { |
| 7952 |
if ( ++$fields > 50 ) { |
| 7953 |
break; |
| 7954 |
} |
| 7955 |
$key = sanitize_key( $k ); |
| 7956 |
if ( $key === '' || in_array( $key, $skip, true ) || ! is_scalar( $v ) ) { |
| 7957 |
continue; |
| 7958 |
} |
| 7959 |
$json[ $key ] = sanitize_text_field( substr( (string) $v, 0, 500 ) ); |
| 7960 |
} |
| 7961 |
|
| 7962 |
if ( file_put_contents( $dir . $filename, wp_json_encode( $json ), LOCK_EX ) === false ) { |
| 7963 |
status_header( 500 ); |
| 7964 |
die( esc_html__( 'The form data could not be saved.', 'ebook-store' ) ); |
| 7965 |
} |
| 7966 |
|
| 7967 |
status_header( 200 ); |
| 7968 |
die( esc_html__( 'Form data saved successfully.', 'ebook-store' ) ); |
| 7969 |
} |
| 7970 |
|
| 7971 |
/** |
| 7972 |
* Read back the buyer details captured at checkout. |
| 7973 |
* |
| 7974 |
* This used to read from get_temp_dir() with no '.json' suffix, i.e. a |
| 7975 |
* different directory and a different filename than ebook_store_save_form() |
| 7976 |
* writes, so it always returned false and every order fell back to |
| 7977 |
* "Anonymous User". |
| 7978 |
* |
| 7979 |
* @param string $md5_nonce Order token. |
| 7980 |
* @return object|false |
| 7981 |
*/ |
| 7982 |
function ebook_store_get_form( $md5_nonce ) { |
| 7983 |
$filename = ebook_store_get_form_filename( $md5_nonce ); |
| 7984 |
$dir = ebook_store_get_form_data_dir(); |
| 7985 |
|
| 7986 |
if ( $filename === '' || $dir === '' ) { |
| 7987 |
return false; |
| 7988 |
} |
| 7989 |
|
| 7990 |
$path = $dir . $filename; |
| 7991 |
if ( ! is_readable( $path ) ) { |
| 7992 |
return false; |
| 7993 |
} |
| 7994 |
|
| 7995 |
$form_data = file_get_contents( $path ); |
| 7996 |
if ( ! $form_data ) { |
| 7997 |
return false; |
| 7998 |
} |
| 7999 |
|
| 8000 |
$decoded = json_decode( $form_data ); |
| 8001 |
|
| 8002 |
return $decoded === null ? false : $decoded; |
| 8003 |
} |
| 8004 |
function ebook_store_get_mailchimp_lists() { |
| 8005 |
$api_key = trim( (string) get_option( 'mailchimp_api_key' ) ); |
| 8006 |
if ( $api_key === '' ) { |
| 8007 |
return false; |
| 8008 |
} |
| 8009 |
|
| 8010 |
$response = ebook_store_mailchimp_request( |
| 8011 |
'GET', |
| 8012 |
'/lists', |
| 8013 |
array( |
| 8014 |
'count' => 1000, |
| 8015 |
'fields' => 'lists.id,lists.name,total_items', |
| 8016 |
) |
| 8017 |
); |
| 8018 |
|
| 8019 |
if ( is_wp_error( $response ) || ! isset( $response['lists'] ) || ! is_array( $response['lists'] ) ) { |
| 8020 |
return false; |
| 8021 |
} |
| 8022 |
|
| 8023 |
return array_map( |
| 8024 |
static function( $list ) { |
| 8025 |
return (object) array( |
| 8026 |
'id' => isset( $list['id'] ) ? (string) $list['id'] : '', |
| 8027 |
'name' => isset( $list['name'] ) ? (string) $list['name'] : '', |
| 8028 |
); |
| 8029 |
}, |
| 8030 |
$response['lists'] |
| 8031 |
); |
| 8032 |
} |
| 8033 |
function ebook_store_get_brevo_lists() { |
| 8034 |
$api_key = trim( (string) get_option( 'brevo_api_key' ) ); |
| 8035 |
if ( $api_key === '' ) { |
| 8036 |
return false; |
| 8037 |
} |
| 8038 |
|
| 8039 |
$lists = array(); |
| 8040 |
$offset = 0; |
| 8041 |
$limit = 50; |
| 8042 |
|
| 8043 |
do { |
| 8044 |
$response = ebook_store_brevo_request( |
| 8045 |
'GET', |
| 8046 |
'/contacts/lists', |
| 8047 |
array( |
| 8048 |
'limit' => $limit, |
| 8049 |
'offset' => $offset, |
| 8050 |
) |
| 8051 |
); |
| 8052 |
|
| 8053 |
if ( is_wp_error( $response ) || ! isset( $response['lists'] ) || ! is_array( $response['lists'] ) ) { |
| 8054 |
return false; |
| 8055 |
} |
| 8056 |
|
| 8057 |
$batch = $response['lists']; |
| 8058 |
$lists = array_merge( $lists, $batch ); |
| 8059 |
$offset += count( $batch ); |
| 8060 |
$total = isset( $response['count'] ) ? (int) $response['count'] : count( $lists ); |
| 8061 |
} while ( ! empty( $batch ) && $offset < $total ); |
| 8062 |
|
| 8063 |
return array_map( |
| 8064 |
static function( $list ) { |
| 8065 |
return (object) array( |
| 8066 |
'id' => isset( $list['id'] ) ? (string) $list['id'] : '', |
| 8067 |
'name' => isset( $list['name'] ) ? (string) $list['name'] : '', |
| 8068 |
); |
| 8069 |
}, |
| 8070 |
$lists |
| 8071 |
); |
| 8072 |
} |
| 8073 |
function ebook_store_get_mailchimp_subscribe($email) { |
| 8074 |
//error_log('MAILCHIMP Subscribing ' . $email); |
| 8075 |
$list_id = trim( (string) get_option( 'mailchimp_lists' ) ); |
| 8076 |
$integration_enabled = (string) get_option( 'mailchimp_integration_enabled', $list_id !== '' ? '1' : '' ); |
| 8077 |
if ( $integration_enabled !== '1' ) { |
| 8078 |
return false; |
| 8079 |
} |
| 8080 |
|
| 8081 |
if ( $list_id === '' ) { |
| 8082 |
return false; |
| 8083 |
} |
| 8084 |
|
| 8085 |
$api_key = trim( (string) get_option( 'mailchimp_api_key' ) ); |
| 8086 |
if ( $api_key === '' ) { |
| 8087 |
return false; |
| 8088 |
} |
| 8089 |
|
| 8090 |
$email = sanitize_email( (string) $email ); |
| 8091 |
if ( $email === '' ) { |
| 8092 |
return false; |
| 8093 |
} |
| 8094 |
|
| 8095 |
$subscriber_hash = md5( strtolower( $email ) ); |
| 8096 |
|
| 8097 |
return ebook_store_mailchimp_request( |
| 8098 |
'PUT', |
| 8099 |
'/lists/' . rawurlencode( $list_id ) . '/members/' . $subscriber_hash, |
| 8100 |
array(), |
| 8101 |
array( |
| 8102 |
'email_address' => $email, |
| 8103 |
'status_if_new' => 'subscribed', |
| 8104 |
'status' => 'subscribed', |
| 8105 |
) |
| 8106 |
); |
| 8107 |
} |
| 8108 |
function ebook_store_get_brevo_subscribe( $email ) { |
| 8109 |
$list_id = trim( (string) get_option( 'brevo_lists' ) ); |
| 8110 |
$integration_enabled = (string) get_option( 'brevo_integration_enabled', $list_id !== '' ? '1' : '' ); |
| 8111 |
if ( $integration_enabled !== '1' ) { |
| 8112 |
return false; |
| 8113 |
} |
| 8114 |
|
| 8115 |
if ( $list_id === '' || ! ctype_digit( $list_id ) ) { |
| 8116 |
return false; |
| 8117 |
} |
| 8118 |
|
| 8119 |
$api_key = trim( (string) get_option( 'brevo_api_key' ) ); |
| 8120 |
if ( $api_key === '' ) { |
| 8121 |
return false; |
| 8122 |
} |
| 8123 |
|
| 8124 |
$email = sanitize_email( (string) $email ); |
| 8125 |
if ( $email === '' ) { |
| 8126 |
return false; |
| 8127 |
} |
| 8128 |
|
| 8129 |
return ebook_store_brevo_request( |
| 8130 |
'POST', |
| 8131 |
'/contacts', |
| 8132 |
array(), |
| 8133 |
array( |
| 8134 |
'email' => $email, |
| 8135 |
'listIds' => array( (int) $list_id ), |
| 8136 |
'emailBlacklisted' => false, |
| 8137 |
'smsBlacklisted' => false, |
| 8138 |
'updateEnabled' => true, |
| 8139 |
) |
| 8140 |
); |
| 8141 |
} |
| 8142 |
|
| 8143 |
function ebook_store_subscribe_buyer_to_marketing_lists( $email ) { |
| 8144 |
$email = sanitize_email( (string) $email ); |
| 8145 |
if ( $email === '' ) { |
| 8146 |
return false; |
| 8147 |
} |
| 8148 |
|
| 8149 |
$results = array(); |
| 8150 |
$results['mailchimp'] = ebook_store_get_mailchimp_subscribe( $email ); |
| 8151 |
$results['brevo'] = ebook_store_get_brevo_subscribe( $email ); |
| 8152 |
|
| 8153 |
return $results; |
| 8154 |
} |
| 8155 |
|
| 8156 |
function ebook_store_get_mailchimp_server_prefix( $api_key ) { |
| 8157 |
$api_key = trim( (string) $api_key ); |
| 8158 |
if ( $api_key === '' || strpos( $api_key, '-' ) === false ) { |
| 8159 |
return ''; |
| 8160 |
} |
| 8161 |
|
| 8162 |
$parts = explode( '-', $api_key ); |
| 8163 |
$server_prefix = strtolower( trim( (string) end( $parts ) ) ); |
| 8164 |
|
| 8165 |
return preg_match( '/^[a-z0-9]+$/', $server_prefix ) ? $server_prefix : ''; |
| 8166 |
} |
| 8167 |
|
| 8168 |
function ebook_store_mailchimp_request( $method, $path, $query = array(), $body = null ) { |
| 8169 |
$api_key = trim( (string) get_option( 'mailchimp_api_key' ) ); |
| 8170 |
if ( $api_key === '' ) { |
| 8171 |
return new WP_Error( 'mailchimp_missing_api_key', 'Mailchimp API key not configured.' ); |
| 8172 |
} |
| 8173 |
|
| 8174 |
$server_prefix = ebook_store_get_mailchimp_server_prefix( $api_key ); |
| 8175 |
if ( $server_prefix === '' ) { |
| 8176 |
return new WP_Error( 'mailchimp_invalid_api_key', 'Mailchimp API key is missing a valid server prefix.' ); |
| 8177 |
} |
| 8178 |
|
| 8179 |
$url = 'https://' . $server_prefix . '.api.mailchimp.com/3.0' . $path; |
| 8180 |
if ( ! empty( $query ) ) { |
| 8181 |
$url = add_query_arg( $query, $url ); |
| 8182 |
} |
| 8183 |
|
| 8184 |
$args = array( |
| 8185 |
'method' => strtoupper( (string) $method ), |
| 8186 |
'timeout' => 20, |
| 8187 |
'headers' => array( |
| 8188 |
'Authorization' => 'Basic ' . base64_encode( 'anystring:' . $api_key ), |
| 8189 |
'Accept' => 'application/json', |
| 8190 |
), |
| 8191 |
); |
| 8192 |
|
| 8193 |
if ( null !== $body ) { |
| 8194 |
$args['headers']['Content-Type'] = 'application/json'; |
| 8195 |
$args['body'] = wp_json_encode( $body ); |
| 8196 |
} |
| 8197 |
|
| 8198 |
$response = wp_remote_request( $url, $args ); |
| 8199 |
if ( is_wp_error( $response ) ) { |
| 8200 |
return $response; |
| 8201 |
} |
| 8202 |
|
| 8203 |
$status_code = (int) wp_remote_retrieve_response_code( $response ); |
| 8204 |
$response_body = wp_remote_retrieve_body( $response ); |
| 8205 |
$data = $response_body !== '' ? json_decode( $response_body, true ) : array(); |
| 8206 |
|
| 8207 |
if ( $status_code < 200 || $status_code >= 300 ) { |
| 8208 |
$message = is_array( $data ) && ! empty( $data['detail'] ) ? (string) $data['detail'] : 'Mailchimp request failed.'; |
| 8209 |
return new WP_Error( 'mailchimp_api_error', $message, array( 'status' => $status_code, 'response' => $data ) ); |
| 8210 |
} |
| 8211 |
|
| 8212 |
return is_array( $data ) ? $data : array(); |
| 8213 |
} |
| 8214 |
function ebook_store_brevo_request( $method, $path, $query = array(), $body = null ) { |
| 8215 |
$api_key = trim( (string) get_option( 'brevo_api_key' ) ); |
| 8216 |
if ( $api_key === '' ) { |
| 8217 |
return new WP_Error( 'brevo_missing_api_key', 'Brevo API key not configured.' ); |
| 8218 |
} |
| 8219 |
|
| 8220 |
$url = 'https://api.brevo.com/v3' . $path; |
| 8221 |
if ( ! empty( $query ) ) { |
| 8222 |
$url = add_query_arg( $query, $url ); |
| 8223 |
} |
| 8224 |
|
| 8225 |
$args = array( |
| 8226 |
'method' => strtoupper( (string) $method ), |
| 8227 |
'timeout' => 20, |
| 8228 |
'headers' => array( |
| 8229 |
'api-key' => $api_key, |
| 8230 |
'Accept' => 'application/json', |
| 8231 |
), |
| 8232 |
); |
| 8233 |
|
| 8234 |
if ( null !== $body ) { |
| 8235 |
$args['headers']['Content-Type'] = 'application/json'; |
| 8236 |
$args['body'] = wp_json_encode( $body ); |
| 8237 |
} |
| 8238 |
|
| 8239 |
$response = wp_remote_request( $url, $args ); |
| 8240 |
if ( is_wp_error( $response ) ) { |
| 8241 |
return $response; |
| 8242 |
} |
| 8243 |
|
| 8244 |
$status_code = (int) wp_remote_retrieve_response_code( $response ); |
| 8245 |
$response_body = wp_remote_retrieve_body( $response ); |
| 8246 |
$data = $response_body !== '' ? json_decode( $response_body, true ) : array(); |
| 8247 |
|
| 8248 |
if ( $status_code < 200 || $status_code >= 300 ) { |
| 8249 |
$message = 'Brevo request failed.'; |
| 8250 |
if ( is_array( $data ) ) { |
| 8251 |
if ( ! empty( $data['message'] ) ) { |
| 8252 |
$message = (string) $data['message']; |
| 8253 |
} elseif ( ! empty( $data['code'] ) ) { |
| 8254 |
$message = (string) $data['code']; |
| 8255 |
} |
| 8256 |
} |
| 8257 |
|
| 8258 |
return new WP_Error( 'brevo_api_error', $message, array( 'status' => $status_code, 'response' => $data ) ); |
| 8259 |
} |
| 8260 |
|
| 8261 |
return is_array( $data ) ? $data : array(); |
| 8262 |
} |
| 8263 |
function ebook_wp_embed_ebook() { |
| 8264 |
wp_enqueue_script( 'ebook_store_settings', plugins_url( '/js/ebook_store_settings.js' , __FILE__ ), array(), '1.0.0', true ); |
| 8265 |
?> |
| 8266 |
<div class="ebook_store_embed_all_items" style=" display:inline-block; clear:both; max-height:200px; overflow-x:auto; width:100%;"> |
| 8267 |
<?php |
| 8268 |
$new = new WP_Query('post_type=ebook'); |
| 8269 |
while ($new->have_posts()) : $new->the_post(); |
| 8270 |
@$ebooks_found++; |
| 8271 |
$img_cover = get_post_meta(get_the_ID(), 'ebook_wp_custom_attachment_cover', true); |
| 8272 |
$ebook = get_post_meta(get_the_ID(), 'ebook', true); |
| 8273 |
$c = new Currencies(); |
| 8274 |
$price = $c->getSymbol(get_option('paypal_currency','USD')) . number_format((float)$ebook['ebook_price'],2) |
| 8275 |
?> |
| 8276 |
<div class="ebook_store_embed_ebook_item" style="width:100%; clear:both;"> |
| 8277 |
<?php |
| 8278 |
if (!is_array($img_cover)) { |
| 8279 |
$img_cover = array(); |
| 8280 |
} |
| 8281 |
if (@$img_cover['url'] != '') { |
| 8282 |
?><img src="<?php echo $img_cover['url']; ?>" onClick="ebook_store_embed_code(<?php echo get_the_ID(); ?>);" style="background:gray; width:45px; height:65px; float: left; margin:10px; cursor: pointer; cursor: hand;" /> |
| 8283 |
<?php |
| 8284 |
} else { |
| 8285 |
?> |
| 8286 |
<div style="background:gray; width:45px; height:65px; float: left; margin:10px;" onClick="ebook_store_embed_code(<?php echo get_the_ID(); ?>);"></div> |
| 8287 |
<?php |
| 8288 |
} |
| 8289 |
|
| 8290 |
?><span class="ebook_store_embed_ebook_item_title" style="margin-top: 11px;display: inline-block;"> |
| 8291 |
<a href="javascript:void(0);" onClick="ebook_store_embed_code(<?php echo get_the_ID(); ?>);" title="Click to embed this ebook in the post"><b><?php echo get_the_title() ?></b></a> |
| 8292 |
<a href="post.php?post=<?php echo get_the_ID(); ?>&action=edit" title="Click to edit this ebook in a new window." target="_blank"><small>[Edit]</small></a> |
| 8293 |
<br />Form: <input onClick="this.select()" readonly type="text" size="29" value='[ebook_store ebook_id="<?php echo get_the_ID(); ?>"]' /> |
| 8294 |
<label style="white-space: nowrap;">Buy Button Only: <input onClick="this.select()" readonly type="text" size="29" value='[ebook_store_buy ebook_id="<?php echo get_the_ID(); ?>"]' /></label> |
| 8295 |
|
| 8296 |
</span> |
| 8297 |
<p> |
| 8298 |
Price: <?php echo $price; ?> Download type: <?php echo $ebook['donate_or_download']; ?> |
| 8299 |
|
| 8300 |
</p> |
| 8301 |
</div> |
| 8302 |
<?php |
| 8303 |
endwhile; |
| 8304 |
if (@$ebooks_found == 0) { |
| 8305 |
echo "<p>You have not created any ebooks yet, please click <a href='post-new.php?post_type=ebook'>here</a> to start.</p>"; |
| 8306 |
} |
| 8307 |
?> |
| 8308 |
</div> |
| 8309 |
<?php |
| 8310 |
} |
| 8311 |
/** |
| 8312 |
* Kept as a no-op shim: third-party code and older templates call this directly. |
| 8313 |
* Registration now happens once in includes/ebook-store-assets.php and the |
| 8314 |
* stylesheet is enqueued only where store markup is actually rendered. |
| 8315 |
*/ |
| 8316 |
function ebookstorestylesheet() { |
| 8317 |
ebook_store_enqueue_frontend_styles(); |
| 8318 |
} |
| 8319 |
function ebook_store_post_type_view( $content ) { |
| 8320 |
// is_singular() asks about the main query, not about the post whose content |
| 8321 |
// is being filtered. Without the in_the_loop()/is_main_query() guard, a |
| 8322 |
// related-posts widget or an excerpt on a single-ebook page also had its |
| 8323 |
// content replaced by the buy form. |
| 8324 |
if ( ! in_the_loop() || ! is_main_query() ) { |
| 8325 |
return $content; |
| 8326 |
} |
| 8327 |
|
| 8328 |
if ( get_post_type() !== 'ebook' ) { |
| 8329 |
return $content; |
| 8330 |
} |
| 8331 |
|
| 8332 |
return ebook_store( get_the_ID() ); |
| 8333 |
} |
| 8334 |
function ebook_download_links($ebook_order, $free = 0) { |
| 8335 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 8336 |
$locale = ebook_store_get_locale_strings(); |
| 8337 |
$formats_locale = ebook_store_get_format_labels(); |
| 8338 |
if ($free == 0) { |
| 8339 |
$action_name = 'download'; |
| 8340 |
} else { |
| 8341 |
$action_name = 'download_free'; |
| 8342 |
} |
| 8343 |
$ebook_id = $ebook_order['ebook'][0]; |
| 8344 |
$loop = array('mobi','txt','epub','zip', 'mp3', 'mp4'); |
| 8345 |
$meta = get_post_meta($ebook_id); |
| 8346 |
$links = array(); |
| 8347 |
$post = get_post($ebook_order['ebook'][0]); |
| 8348 |
@$slug = $post->post_name; |
| 8349 |
foreach ($loop as $l) { |
| 8350 |
if (@is_array($meta['ebook_wp_custom_attachment_' . $l])) { |
| 8351 |
$book = unserialize($meta['ebook_wp_custom_attachment_' . $l][0]); |
| 8352 |
$file = $book['file']; |
| 8353 |
$ebook_key = (strlen(@$ebook_order['ebook_key'][0]) == 32 ? @$ebook_order['ebook_key'][0] : @$ebook_order['ebook_key_array'][0]); |
| 8354 |
$order_id = isset( $ebook_order['order_id'][0] ) ? absint( $ebook_order['order_id'][0] ) : ( isset( $ebook_order['order_id'] ) ? absint( $ebook_order['order_id'] ) : 0 ); |
| 8355 |
$link_args = array( |
| 8356 |
'format' => $l, |
| 8357 |
'ebook_key' => $ebook_key, |
| 8358 |
'action' => $action_name, |
| 8359 |
'md5_nonce' => @$ebook_order['md5_nonce'][0], |
| 8360 |
); |
| 8361 |
if ( $order_id > 0 ) { |
| 8362 |
$link_args['order_id'] = $order_id; |
| 8363 |
} |
| 8364 |
|
| 8365 |
$link = add_query_arg($link_args,get_permalink($ebook_order['ebook'][0])); |
| 8366 |
$link = remove_query_arg('p',$link); |
| 8367 |
$link = add_query_arg(array('ebook' => $slug),$link); |
| 8368 |
|
| 8369 |
@$links[] = "<a href=\"$link\">" . $formats_locale[$l] . " Format</a>"; |
| 8370 |
} |
| 8371 |
} |
| 8372 |
if (count($links) == 0) { |
| 8373 |
$links[] = 'No other file formats are available for this ebook.'; |
| 8374 |
} |
| 8375 |
//error_log($link); |
| 8376 |
return $links; |
| 8377 |
} |
| 8378 |
function ebook_download_links_bonus($ebook_order, $free = 0) { |
| 8379 |
if (@is_array($ebook_order['ebook_key']) == false) { |
| 8380 |
foreach ($ebook_order as $k => $v) { |
| 8381 |
if (is_array($v)) { |
| 8382 |
$v = $v[0]; |
| 8383 |
} |
| 8384 |
@$new_order[$k][0] = $v; |
| 8385 |
} |
| 8386 |
$ebook_order = $new_order; |
| 8387 |
} |
| 8388 |
//error_log(__LINE__ . ' ' . print_r($ebook_order,true)); |
| 8389 |
|
| 8390 |
$ebook_id = $ebook_order['ebook'][0]; |
| 8391 |
$ebook_bonus = get_post_meta($ebook_id, 'ebook', true); |
| 8392 |
$ebook_bonus = @$ebook_bonus['ebook_bonus']; |
| 8393 |
$links = array(); |
| 8394 |
if (is_array($ebook_bonus)) { |
| 8395 |
if (count($ebook_bonus) > 0) { |
| 8396 |
foreach ($ebook_bonus as $ebook_id) { |
| 8397 |
$post = get_post($ebook_id); |
| 8398 |
$ebook_key = (strlen($ebook_order['ebook_key'][0]) == 32 ? $ebook_order['ebook_key'][0] : $ebook_order['ebook_key_array'][0]); |
| 8399 |
// $link //= add_query_arg(array('ebook_key' => $ebook_key,'ebook_id' => $ebook_id,'action' => 'ebook_bonus_download', 'md5_nonce' => @$ebook_order['md5_nonce'][0]),get_permalink($ebook_id)); |
| 8400 |
$link = ebook_download_link($ebook_order, 0, 1, $ebook_id); |
| 8401 |
$links[] = "<a href=\"$link\" target=\"_blank\">" . $post->post_title ."</a>"; |
| 8402 |
} |
| 8403 |
} |
| 8404 |
|
| 8405 |
} |
| 8406 |
//$links[] = '<pre>'.print_r($ebook_order,true) . '</pre>'; |
| 8407 |
return $links; |
| 8408 |
} |
| 8409 |
function ebook_store_stats($ebook_id, $days) { |
| 8410 |
$seconds = 86400 * $days; |
| 8411 |
if ($seconds == 0) { |
| 8412 |
$seconds = 157680000; |
| 8413 |
} |
| 8414 |
$args = array( |
| 8415 |
'post_type' => 'ebook_order', |
| 8416 |
'meta_query' => array( |
| 8417 |
array( |
| 8418 |
'key' => 'ebook', |
| 8419 |
'value' => $ebook_id, |
| 8420 |
) |
| 8421 |
), |
| 8422 |
'date_query' => array( |
| 8423 |
array( |
| 8424 |
'column' => 'post_date_gmt', |
| 8425 |
'after' => $seconds . ' seconds ago', |
| 8426 |
), |
| 8427 |
)); |
| 8428 |
// The Query |
| 8429 |
$the_query = new WP_Query( $args ); |
| 8430 |
$out = 0; |
| 8431 |
// The Loop |
| 8432 |
if ( $the_query->have_posts() ) { |
| 8433 |
// echo '<ul>'; |
| 8434 |
while ( $the_query->have_posts() ) { |
| 8435 |
$the_query->the_post(); |
| 8436 |
$id = get_the_ID(); |
| 8437 |
//echo "$id<br/>"; |
| 8438 |
$meta = get_post_meta($id); |
| 8439 |
//print_r($meta); |
| 8440 |
$out += $meta['mc_gross'][0]; |
| 8441 |
// echo '<li>' . get_the_title() . '</li>'; |
| 8442 |
} |
| 8443 |
// echo '</ul>'; |
| 8444 |
} else { |
| 8445 |
// no posts found |
| 8446 |
} |
| 8447 |
/* Restore original Post Data */ |
| 8448 |
wp_reset_postdata(); |
| 8449 |
return $out; |
| 8450 |
} |
| 8451 |
|
| 8452 |
/** |
| 8453 |
* Sales totals bucketed by date, computed in SQL. |
| 8454 |
* |
| 8455 |
* Both report functions used to run WP_Query with posts_per_page => -1 and then |
| 8456 |
* call get_post_meta() three times per row inside the loop. Because the PayPal |
| 8457 |
* IPN handler writes one meta row per field, a store with 10k orders hydrated |
| 8458 |
* ~10k WP_Post objects plus several hundred thousand meta rows in a single |
| 8459 |
* request. That does not get slow, it exhausts the memory limit and white-screens |
| 8460 |
* the reports page — and the cache is flushed on every incoming order, so the |
| 8461 |
* expensive path is the normal path on a busy store. |
| 8462 |
* |
| 8463 |
* @param int $ebook_id Limit to one ebook, or 0 for the whole store. |
| 8464 |
* @param string $format 'Y-m-d' for daily buckets, 'Y-m' for monthly. |
| 8465 |
* @param string $since Anything strtotime() understands, e.g. '30 days ago'. |
| 8466 |
* @return array bucket => total. |
| 8467 |
*/ |
| 8468 |
function ebook_store_get_sales_buckets( $ebook_id, $format, $since ) { |
| 8469 |
global $wpdb; |
| 8470 |
|
| 8471 |
$ebook_id = absint( $ebook_id ); |
| 8472 |
$after = gmdate( 'Y-m-d H:i:s', strtotime( $since, current_time( 'timestamp', true ) ) ); |
| 8473 |
|
| 8474 |
// MySQL's DATE_FORMAT equivalents of the PHP formats used by the callers. |
| 8475 |
$sql_format = $format === 'Y-m' ? '%Y-%m' : '%Y-%m-%d'; |
| 8476 |
|
| 8477 |
// Prefer the gateway's payment_date when present, fall back to the post date, |
| 8478 |
// so an order recorded late still lands in the right bucket. |
| 8479 |
$join = " INNER JOIN {$wpdb->postmeta} gross ON ( gross.post_id = p.ID AND gross.meta_key = 'mc_gross' )"; |
| 8480 |
$join .= " LEFT JOIN {$wpdb->postmeta} pdate ON ( pdate.post_id = p.ID AND pdate.meta_key = 'payment_date' )"; |
| 8481 |
$join .= " LEFT JOIN {$wpdb->postmeta} pstatus ON ( pstatus.post_id = p.ID AND pstatus.meta_key = 'payment_status' )"; |
| 8482 |
|
| 8483 |
$where = $wpdb->prepare( " WHERE p.post_type = 'ebook_order' AND p.post_status = 'publish' AND p.post_date_gmt >= %s", $after ); |
| 8484 |
$where .= " AND ( pstatus.meta_value IS NULL OR LOWER( pstatus.meta_value ) NOT IN ( 'refunded', 'reversed', 'canceled_reversal', 'denied', 'failed', 'voided', 'expired', 'partially_refunded' ) )"; |
| 8485 |
|
| 8486 |
if ( $ebook_id > 0 ) { |
| 8487 |
$join .= " INNER JOIN {$wpdb->postmeta} ebook ON ( ebook.post_id = p.ID AND ebook.meta_key = 'ebook' )"; |
| 8488 |
$where .= $wpdb->prepare( ' AND ebook.meta_value = %d', $ebook_id ); |
| 8489 |
} |
| 8490 |
|
| 8491 |
// payment_date is stored in whatever shape the gateway sent: PayPal uses |
| 8492 |
// "11:51:05 Apr 02, 2026 PDT", the built-in checkout writes "04/02/2026 11:51:05". |
| 8493 |
// Try both, and fall back to post_date_gmt, which is always well formed. |
| 8494 |
$paid_at = "COALESCE( |
| 8495 |
STR_TO_DATE( pdate.meta_value, '%H:%i:%s %b %d, %Y' ), |
| 8496 |
STR_TO_DATE( pdate.meta_value, '%m/%d/%Y %H:%i:%s' ), |
| 8497 |
p.post_date_gmt |
| 8498 |
)"; |
| 8499 |
|
| 8500 |
$bucket = "DATE_FORMAT( {$paid_at}, '{$sql_format}' )"; |
| 8501 |
|
| 8502 |
$rows = $wpdb->get_results( |
| 8503 |
"SELECT {$bucket} AS bucket, SUM( gross.meta_value + 0 ) AS total |
| 8504 |
FROM {$wpdb->posts} p |
| 8505 |
{$join} |
| 8506 |
{$where} |
| 8507 |
GROUP BY bucket", |
| 8508 |
ARRAY_A |
| 8509 |
); |
| 8510 |
|
| 8511 |
$out = array(); |
| 8512 |
foreach ( (array) $rows as $row ) { |
| 8513 |
if ( ! empty( $row['bucket'] ) ) { |
| 8514 |
$out[ $row['bucket'] ] = (float) $row['total']; |
| 8515 |
} |
| 8516 |
} |
| 8517 |
|
| 8518 |
return $out; |
| 8519 |
} |
| 8520 |
|
| 8521 |
function ebook_store_stats_report_30days( $ebook_id = 0 ) { |
| 8522 |
$out = array(); |
| 8523 |
|
| 8524 |
for ( $i = 29; $i >= 0; $i-- ) { |
| 8525 |
$timestamp = current_time( 'timestamp', true ) - ( DAY_IN_SECONDS * $i ); |
| 8526 |
$out[ gmdate( 'Y-m-d', $timestamp ) ] = 0; |
| 8527 |
} |
| 8528 |
|
| 8529 |
foreach ( ebook_store_get_sales_buckets( $ebook_id, 'Y-m-d', '30 days ago' ) as $bucket => $total ) { |
| 8530 |
$out[ $bucket ] = $total; |
| 8531 |
} |
| 8532 |
|
| 8533 |
return $out; |
| 8534 |
} |
| 8535 |
|
| 8536 |
function ebook_store_stats_report_2years( $ebook_id = 0 ) { |
| 8537 |
$out = array(); |
| 8538 |
|
| 8539 |
for ( $i = 23; $i >= 0; $i-- ) { |
| 8540 |
$timestamp = strtotime( gmdate( 'Y-m-01 00:00:00', current_time( 'timestamp', true ) ) . " -{$i} months" ); |
| 8541 |
$out[ gmdate( 'Y-m', $timestamp ) ] = 0; |
| 8542 |
} |
| 8543 |
|
| 8544 |
foreach ( ebook_store_get_sales_buckets( $ebook_id, 'Y-m', '24 months ago' ) as $bucket => $total ) { |
| 8545 |
$out[ $bucket ] = $total; |
| 8546 |
} |
| 8547 |
|
| 8548 |
return $out; |
| 8549 |
} |
| 8550 |
|
| 8551 |
function ebook_store_get_reports_data( $ebook_id = 0 ) { |
| 8552 |
$ebook_id = absint( $ebook_id ); |
| 8553 |
$cache_key = ebook_store_get_reports_cache_key( $ebook_id ); |
| 8554 |
$cached = get_transient( $cache_key ); |
| 8555 |
if ( false !== $cached && is_array( $cached ) ) { |
| 8556 |
return $cached; |
| 8557 |
} |
| 8558 |
|
| 8559 |
global $wpdb; |
| 8560 |
|
| 8561 |
$query = " |
| 8562 |
SELECT |
| 8563 |
p.ID, |
| 8564 |
p.post_date_gmt, |
| 8565 |
status.meta_value AS payment_status, |
| 8566 |
gross.meta_value AS mc_gross, |
| 8567 |
fee.meta_value AS mc_fee, |
| 8568 |
currency.meta_value AS mc_currency, |
| 8569 |
ebook.meta_value AS ebook_id, |
| 8570 |
item.meta_value AS item_name, |
| 8571 |
quantity.meta_value AS quantity, |
| 8572 |
payment_type.meta_value AS payment_type, |
| 8573 |
payer_email.meta_value AS payer_email |
| 8574 |
FROM {$wpdb->posts} p |
| 8575 |
LEFT JOIN {$wpdb->postmeta} status ON (p.ID = status.post_id AND status.meta_key = 'payment_status') |
| 8576 |
LEFT JOIN {$wpdb->postmeta} gross ON (p.ID = gross.post_id AND gross.meta_key = 'mc_gross') |
| 8577 |
LEFT JOIN {$wpdb->postmeta} fee ON (p.ID = fee.post_id AND fee.meta_key = 'mc_fee') |
| 8578 |
LEFT JOIN {$wpdb->postmeta} currency ON (p.ID = currency.post_id AND currency.meta_key = 'mc_currency') |
| 8579 |
LEFT JOIN {$wpdb->postmeta} ebook ON (p.ID = ebook.post_id AND ebook.meta_key = 'ebook') |
| 8580 |
LEFT JOIN {$wpdb->postmeta} item ON (p.ID = item.post_id AND item.meta_key = 'item_name') |
| 8581 |
LEFT JOIN {$wpdb->postmeta} quantity ON (p.ID = quantity.post_id AND quantity.meta_key = 'quantity') |
| 8582 |
LEFT JOIN {$wpdb->postmeta} payment_type ON (p.ID = payment_type.post_id AND payment_type.meta_key = 'payment_type') |
| 8583 |
LEFT JOIN {$wpdb->postmeta} payer_email ON (p.ID = payer_email.post_id AND payer_email.meta_key = 'payer_email') |
| 8584 |
WHERE p.post_type = 'ebook_order' |
| 8585 |
AND p.post_status = 'publish' |
| 8586 |
"; |
| 8587 |
|
| 8588 |
if ( $ebook_id > 0 ) { |
| 8589 |
$query .= $wpdb->prepare( " AND ebook.meta_value = %d", $ebook_id ); |
| 8590 |
} |
| 8591 |
|
| 8592 |
$query .= " ORDER BY p.post_date_gmt DESC"; |
| 8593 |
|
| 8594 |
$rows = $wpdb->get_results( $query, ARRAY_A ); |
| 8595 |
|
| 8596 |
$default_currency = ebook_store_get_store_currency(); |
| 8597 |
$currency = $default_currency; |
| 8598 |
$gateway_totals = array(); |
| 8599 |
$status_totals = array(); |
| 8600 |
$currency_totals = array(); |
| 8601 |
$recent_orders = array(); |
| 8602 |
$top_ebooks = array(); |
| 8603 |
$item_totals = array(); |
| 8604 |
$currencies_seen = array(); |
| 8605 |
$now_gmt = current_time( 'timestamp', true ); |
| 8606 |
$last_24h_cutoff = $now_gmt - DAY_IN_SECONDS; |
| 8607 |
$last_7d_cutoff = $now_gmt - ( 7 * DAY_IN_SECONDS ); |
| 8608 |
$summary = array( |
| 8609 |
'total_sales' => 0.0, |
| 8610 |
'total_profit' => 0.0, |
| 8611 |
'last_24h_sales' => 0.0, |
| 8612 |
'last_7d_sales' => 0.0, |
| 8613 |
'total_orders' => count( $rows ), |
| 8614 |
'reportable_orders' => 0, |
| 8615 |
'paid_orders' => 0, |
| 8616 |
'free_orders' => 0, |
| 8617 |
'average_order' => 0.0, |
| 8618 |
'latest_order_gmt' => '', |
| 8619 |
'latest_order_id' => 0, |
| 8620 |
'latest_order_amount' => 0.0, |
| 8621 |
'currency' => $default_currency, |
| 8622 |
'mixed_currencies' => false, |
| 8623 |
'currency_count' => 0, |
| 8624 |
'top_item' => array( |
| 8625 |
'label' => __( 'No ebook sales yet', 'ebook-store' ), |
| 8626 |
'units' => 0, |
| 8627 |
'orders' => 0, |
| 8628 |
'revenue' => 0.0, |
| 8629 |
'ebook_id' => 0, |
| 8630 |
), |
| 8631 |
); |
| 8632 |
|
| 8633 |
foreach ( $rows as $row ) { |
| 8634 |
$status = trim( (string) $row['payment_status'] ); |
| 8635 |
$status_key = $status !== '' ? sanitize_key( $status ) : 'unknown'; |
| 8636 |
if ( ! isset( $status_totals[ $status_key ] ) ) { |
| 8637 |
$status_totals[ $status_key ] = array( |
| 8638 |
'label' => $status !== '' ? $status : __( 'Unknown', 'ebook-store' ), |
| 8639 |
'count' => 0, |
| 8640 |
); |
| 8641 |
} |
| 8642 |
$status_totals[ $status_key ]['count']++; |
| 8643 |
|
| 8644 |
if ( ebook_store_is_negative_order_status( $status ) ) { |
| 8645 |
continue; |
| 8646 |
} |
| 8647 |
|
| 8648 |
$summary['reportable_orders']++; |
| 8649 |
|
| 8650 |
$gross = isset( $row['mc_gross'] ) ? (float) $row['mc_gross'] : 0.0; |
| 8651 |
$fee = isset( $row['mc_fee'] ) ? (float) $row['mc_fee'] : 0.0; |
| 8652 |
$gateway = sanitize_key( (string) $row['payment_type'] ); |
| 8653 |
$gateway_label = $gateway !== '' ? ucfirst( str_replace( '_', ' ', $gateway ) ) : __( 'Manual / unknown', 'ebook-store' ); |
| 8654 |
$order_currency = trim( (string) $row['mc_currency'] ); |
| 8655 |
$order_currency = $order_currency !== '' ? $order_currency : $currency; |
| 8656 |
$timestamp = ! empty( $row['post_date_gmt'] ) ? strtotime( $row['post_date_gmt'] . ' UTC' ) : false; |
| 8657 |
$quantity = isset( $row['quantity'] ) && is_numeric( $row['quantity'] ) && (float) $row['quantity'] > 0 ? (float) $row['quantity'] : 1.0; |
| 8658 |
$row_ebook_id = isset( $row['ebook_id'] ) ? absint( $row['ebook_id'] ) : 0; |
| 8659 |
$item_name = trim( isset( $row['item_name'] ) ? (string) $row['item_name'] : '' ); |
| 8660 |
|
| 8661 |
$summary['total_sales'] += $gross; |
| 8662 |
$summary['total_profit'] += ( $gross - $fee ); |
| 8663 |
|
| 8664 |
if ( $gross > 0 ) { |
| 8665 |
$summary['paid_orders']++; |
| 8666 |
} else { |
| 8667 |
$summary['free_orders']++; |
| 8668 |
} |
| 8669 |
|
| 8670 |
if ( $order_currency !== '' ) { |
| 8671 |
$currencies_seen[ $order_currency ] = true; |
| 8672 |
if ( $summary['currency'] === '' || $summary['currency'] === $default_currency ) { |
| 8673 |
$summary['currency'] = $order_currency; |
| 8674 |
} |
| 8675 |
$currency = $summary['currency']; |
| 8676 |
} |
| 8677 |
|
| 8678 |
if ( $timestamp ) { |
| 8679 |
if ( $timestamp >= $last_24h_cutoff ) { |
| 8680 |
$summary['last_24h_sales'] += $gross; |
| 8681 |
} |
| 8682 |
if ( $timestamp >= $last_7d_cutoff ) { |
| 8683 |
$summary['last_7d_sales'] += $gross; |
| 8684 |
} |
| 8685 |
if ( $summary['latest_order_gmt'] === '' || strtotime( $summary['latest_order_gmt'] . ' UTC' ) < $timestamp ) { |
| 8686 |
$summary['latest_order_gmt'] = gmdate( 'Y-m-d H:i:s', $timestamp ); |
| 8687 |
$summary['latest_order_id'] = (int) $row['ID']; |
| 8688 |
$summary['latest_order_amount'] = $gross; |
| 8689 |
} |
| 8690 |
} |
| 8691 |
|
| 8692 |
$item_key = $row_ebook_id > 0 ? 'ebook:' . $row_ebook_id : 'item:' . md5( $item_name !== '' ? $item_name : __( 'Unknown ebook', 'ebook-store' ) ); |
| 8693 |
if ( ! isset( $item_totals[ $item_key ] ) ) { |
| 8694 |
$item_totals[ $item_key ] = array( |
| 8695 |
'ebook_id' => $row_ebook_id, |
| 8696 |
'title' => $row_ebook_id > 0 ? get_the_title( $row_ebook_id ) : '', |
| 8697 |
'orders' => 0, |
| 8698 |
'total' => 0.0, |
| 8699 |
'last_7d' => 0.0, |
| 8700 |
'last_30d' => 0.0, |
| 8701 |
'units' => 0.0, |
| 8702 |
'edit_url' => $row_ebook_id > 0 ? get_edit_post_link( $row_ebook_id, 'raw' ) : '', |
| 8703 |
); |
| 8704 |
if ( $item_totals[ $item_key ]['title'] === '' ) { |
| 8705 |
$item_totals[ $item_key ]['title'] = $item_name !== '' ? $item_name : __( 'Untitled ebook', 'ebook-store' ); |
| 8706 |
} |
| 8707 |
} |
| 8708 |
|
| 8709 |
$item_totals[ $item_key ]['orders']++; |
| 8710 |
$item_totals[ $item_key ]['total'] += $gross; |
| 8711 |
$item_totals[ $item_key ]['units'] += $quantity; |
| 8712 |
if ( $timestamp && $timestamp >= $last_7d_cutoff ) { |
| 8713 |
$item_totals[ $item_key ]['last_7d'] += $gross; |
| 8714 |
} |
| 8715 |
if ( $timestamp && $timestamp >= ( $now_gmt - ( 30 * DAY_IN_SECONDS ) ) ) { |
| 8716 |
$item_totals[ $item_key ]['last_30d'] += $gross; |
| 8717 |
} |
| 8718 |
|
| 8719 |
if ( ! isset( $gateway_totals[ $gateway ?: 'manual' ] ) ) { |
| 8720 |
$gateway_totals[ $gateway ?: 'manual' ] = array( |
| 8721 |
'label' => $gateway_label, |
| 8722 |
'count' => 0, |
| 8723 |
'revenue' => 0.0, |
| 8724 |
); |
| 8725 |
} |
| 8726 |
|
| 8727 |
$gateway_totals[ $gateway ?: 'manual' ]['count']++; |
| 8728 |
$gateway_totals[ $gateway ?: 'manual' ]['revenue'] += $gross; |
| 8729 |
|
| 8730 |
if ( ! isset( $currency_totals[ $order_currency ] ) ) { |
| 8731 |
$currency_totals[ $order_currency ] = array( |
| 8732 |
'label' => $order_currency, |
| 8733 |
'count' => 0, |
| 8734 |
'revenue' => 0.0, |
| 8735 |
); |
| 8736 |
} |
| 8737 |
|
| 8738 |
$currency_totals[ $order_currency ]['count']++; |
| 8739 |
$currency_totals[ $order_currency ]['revenue'] += $gross; |
| 8740 |
|
| 8741 |
if ( count( $recent_orders ) < 8 ) { |
| 8742 |
$recent_orders[] = array( |
| 8743 |
'order_id' => (int) $row['ID'], |
| 8744 |
'date_gmt' => (string) $row['post_date_gmt'], |
| 8745 |
'status' => $status !== '' ? $status : __( 'Unknown', 'ebook-store' ), |
| 8746 |
'amount' => $gross, |
| 8747 |
'fee' => $fee, |
| 8748 |
'currency' => $order_currency, |
| 8749 |
'email' => (string) $row['payer_email'], |
| 8750 |
'gateway' => $gateway_label, |
| 8751 |
'ebook_id' => $row_ebook_id, |
| 8752 |
'ebook_title' => $row_ebook_id > 0 ? get_the_title( $row_ebook_id ) : ( (string) $row['item_name'] !== '' ? (string) $row['item_name'] : __( 'Unknown ebook', 'ebook-store' ) ), |
| 8753 |
'edit_url' => get_edit_post_link( (int) $row['ID'], 'raw' ), |
| 8754 |
); |
| 8755 |
} |
| 8756 |
} |
| 8757 |
|
| 8758 |
if ( $summary['paid_orders'] > 0 ) { |
| 8759 |
$summary['average_order'] = $summary['total_sales'] / $summary['paid_orders']; |
| 8760 |
} |
| 8761 |
|
| 8762 |
$summary['currency_count'] = count( $currencies_seen ); |
| 8763 |
$summary['mixed_currencies'] = $summary['currency_count'] > 1; |
| 8764 |
|
| 8765 |
if ( ! empty( $item_totals ) ) { |
| 8766 |
uasort( |
| 8767 |
$item_totals, |
| 8768 |
static function ( $left, $right ) { |
| 8769 |
if ( $left['total'] === $right['total'] ) { |
| 8770 |
return $right['orders'] <=> $left['orders']; |
| 8771 |
} |
| 8772 |
return $right['total'] <=> $left['total']; |
| 8773 |
} |
| 8774 |
); |
| 8775 |
$top_ebooks = array_values( $item_totals ); |
| 8776 |
$summary['top_item'] = array( |
| 8777 |
'label' => $top_ebooks[0]['title'], |
| 8778 |
'units' => $top_ebooks[0]['units'], |
| 8779 |
'orders' => $top_ebooks[0]['orders'], |
| 8780 |
'revenue' => $top_ebooks[0]['total'], |
| 8781 |
'ebook_id' => $top_ebooks[0]['ebook_id'], |
| 8782 |
); |
| 8783 |
if ( $ebook_id === 0 ) { |
| 8784 |
$top_ebooks = array_slice( $top_ebooks, 0, 8 ); |
| 8785 |
} |
| 8786 |
} |
| 8787 |
|
| 8788 |
uasort( |
| 8789 |
$gateway_totals, |
| 8790 |
static function ( $left, $right ) { |
| 8791 |
if ( $left['revenue'] === $right['revenue'] ) { |
| 8792 |
return $right['count'] <=> $left['count']; |
| 8793 |
} |
| 8794 |
return $right['revenue'] <=> $left['revenue']; |
| 8795 |
} |
| 8796 |
); |
| 8797 |
|
| 8798 |
uasort( |
| 8799 |
$status_totals, |
| 8800 |
static function ( $left, $right ) { |
| 8801 |
return $right['count'] <=> $left['count']; |
| 8802 |
} |
| 8803 |
); |
| 8804 |
|
| 8805 |
uasort( |
| 8806 |
$currency_totals, |
| 8807 |
static function ( $left, $right ) { |
| 8808 |
return $right['revenue'] <=> $left['revenue']; |
| 8809 |
} |
| 8810 |
); |
| 8811 |
|
| 8812 |
$daily = ebook_store_stats_report_30days( $ebook_id ); |
| 8813 |
$monthly = ebook_store_stats_report_2years( $ebook_id ); |
| 8814 |
if ( is_array( $daily ) ) { |
| 8815 |
ksort( $daily ); |
| 8816 |
} |
| 8817 |
if ( is_array( $monthly ) ) { |
| 8818 |
ksort( $monthly ); |
| 8819 |
} |
| 8820 |
|
| 8821 |
$data = array( |
| 8822 |
'summary' => $summary, |
| 8823 |
'currency' => $currency, |
| 8824 |
'ebook_id' => $ebook_id, |
| 8825 |
'ebook_title' => $ebook_id > 0 ? get_the_title( $ebook_id ) : '', |
| 8826 |
'daily' => is_array( $daily ) ? $daily : array(), |
| 8827 |
'monthly' => is_array( $monthly ) ? $monthly : array(), |
| 8828 |
'gateways' => array_values( $gateway_totals ), |
| 8829 |
'statuses' => array_values( $status_totals ), |
| 8830 |
'currencies' => array_values( $currency_totals ), |
| 8831 |
'top_ebooks' => $top_ebooks, |
| 8832 |
'recent_orders' => $recent_orders, |
| 8833 |
); |
| 8834 |
|
| 8835 |
set_transient( $cache_key, $data, 10 * MINUTE_IN_SECONDS ); |
| 8836 |
|
| 8837 |
return $data; |
| 8838 |
} |
| 8839 |
|
| 8840 |
function ebook_store_get_setup_wizard_url( $args = array() ) { |
| 8841 |
$url = admin_url( 'edit.php?post_type=ebook&page=ebook-store-setup-wizard' ); |
| 8842 |
|
| 8843 |
if ( ! empty( $args ) ) { |
| 8844 |
$url = add_query_arg( $args, $url ); |
| 8845 |
} |
| 8846 |
|
| 8847 |
return $url; |
| 8848 |
} |
| 8849 |
|
| 8850 |
function ebook_store_get_template_editor_url( $args = array() ) { |
| 8851 |
$url = admin_url( 'edit.php?post_type=ebook&page=ebook-store-template-editor' ); |
| 8852 |
|
| 8853 |
if ( ! empty( $args ) ) { |
| 8854 |
$url = add_query_arg( $args, $url ); |
| 8855 |
} |
| 8856 |
|
| 8857 |
return $url; |
| 8858 |
} |
| 8859 |
|
| 8860 |
function ebook_store_get_add_issue_url( $args = array() ) { |
| 8861 |
$url = admin_url( 'edit.php?post_type=ebook&page=ebook-store-add-issue-page' ); |
| 8862 |
|
| 8863 |
if ( ! empty( $args ) ) { |
| 8864 |
$url = add_query_arg( $args, $url ); |
| 8865 |
} |
| 8866 |
|
| 8867 |
return $url; |
| 8868 |
} |
| 8869 |
|
| 8870 |
function ebook_store_sanitize_issue_purchase_period( $raw_period, $default = '1 year' ) { |
| 8871 |
$period = strtolower( trim( preg_replace( '/[^0-9a-z\s]/i', '', (string) $raw_period ) ) ); |
| 8872 |
|
| 8873 |
if ( $period === '' ) { |
| 8874 |
$period = $default; |
| 8875 |
} |
| 8876 |
|
| 8877 |
$timestamp = strtotime( $period . ' ago', current_time( 'timestamp', true ) ); |
| 8878 |
if ( false === $timestamp ) { |
| 8879 |
$period = $default; |
| 8880 |
} |
| 8881 |
|
| 8882 |
return $period; |
| 8883 |
} |
| 8884 |
|
| 8885 |
function ebook_store_get_issue_candidates( $source_ebook_id, $target_ebook_id, $purchase_period ) { |
| 8886 |
$source_ebook_id = absint( $source_ebook_id ); |
| 8887 |
$target_ebook_id = absint( $target_ebook_id ); |
| 8888 |
$purchase_period = ebook_store_sanitize_issue_purchase_period( $purchase_period ); |
| 8889 |
$cutoff_timestamp = strtotime( $purchase_period . ' ago', current_time( 'timestamp', true ) ); |
| 8890 |
|
| 8891 |
$result = array( |
| 8892 |
'purchase_period' => $purchase_period, |
| 8893 |
'cutoff_timestamp' => $cutoff_timestamp, |
| 8894 |
'orders' => array(), |
| 8895 |
'eligible' => array(), |
| 8896 |
'skipped_existing' => array(), |
| 8897 |
'skipped_invalid' => array(), |
| 8898 |
'skipped_negative' => array(), |
| 8899 |
'counts' => array( |
| 8900 |
'total' => 0, |
| 8901 |
'eligible' => 0, |
| 8902 |
'skipped_existing' => 0, |
| 8903 |
'skipped_invalid' => 0, |
| 8904 |
'skipped_negative' => 0, |
| 8905 |
), |
| 8906 |
); |
| 8907 |
|
| 8908 |
if ( $source_ebook_id < 1 ) { |
| 8909 |
return $result; |
| 8910 |
} |
| 8911 |
|
| 8912 |
$query = new WP_Query( |
| 8913 |
array( |
| 8914 |
'post_type' => 'ebook_order', |
| 8915 |
'post_status' => 'publish', |
| 8916 |
'posts_per_page' => -1, |
| 8917 |
'orderby' => 'date', |
| 8918 |
'order' => 'DESC', |
| 8919 |
'meta_query' => array( |
| 8920 |
'relation' => 'AND', |
| 8921 |
array( |
| 8922 |
'key' => 'ebook', |
| 8923 |
'value' => $source_ebook_id, |
| 8924 |
), |
| 8925 |
array( |
| 8926 |
'relation' => 'OR', |
| 8927 |
array( |
| 8928 |
'key' => 'issue_delivery_kind', |
| 8929 |
'compare' => 'NOT EXISTS', |
| 8930 |
), |
| 8931 |
array( |
| 8932 |
'key' => 'issue_delivery_kind', |
| 8933 |
'value' => 'new_issue', |
| 8934 |
'compare' => '!=', |
| 8935 |
), |
| 8936 |
), |
| 8937 |
), |
| 8938 |
'date_query' => array( |
| 8939 |
array( |
| 8940 |
'column' => 'post_date_gmt', |
| 8941 |
'after' => gmdate( 'Y-m-d H:i:s', $cutoff_timestamp ), |
| 8942 |
'inclusive' => true, |
| 8943 |
), |
| 8944 |
), |
| 8945 |
) |
| 8946 |
); |
| 8947 |
|
| 8948 |
$orders = array(); |
| 8949 |
$source_order_ids = array(); |
| 8950 |
|
| 8951 |
if ( $query->have_posts() ) { |
| 8952 |
while ( $query->have_posts() ) { |
| 8953 |
$query->the_post(); |
| 8954 |
$order_id = get_the_ID(); |
| 8955 |
$status = (string) get_post_meta( $order_id, 'payment_status', true ); |
| 8956 |
$email = sanitize_email( (string) get_post_meta( $order_id, 'payer_email', true ) ); |
| 8957 |
$item_name = (string) get_post_meta( $order_id, 'item_name', true ); |
| 8958 |
$order_date = (string) get_post_meta( $order_id, 'payment_date', true ); |
| 8959 |
$order_timestamp = $order_date !== '' ? strtotime( $order_date ) : false; |
| 8960 |
if ( ! $order_timestamp ) { |
| 8961 |
$order_timestamp = strtotime( get_post_field( 'post_date_gmt', $order_id ) . ' UTC' ); |
| 8962 |
} |
| 8963 |
|
| 8964 |
$order = array( |
| 8965 |
'source_order_id' => $order_id, |
| 8966 |
'first_name' => (string) get_post_meta( $order_id, 'first_name', true ), |
| 8967 |
'last_name' => (string) get_post_meta( $order_id, 'last_name', true ), |
| 8968 |
'payer_email' => $email, |
| 8969 |
'user_id' => absint( get_post_meta( $order_id, 'user_id', true ) ), |
| 8970 |
'residence_country' => (string) get_post_meta( $order_id, 'residence_country', true ), |
| 8971 |
'status' => $status !== '' ? $status : __( 'Unknown', 'ebook-store' ), |
| 8972 |
'item_name' => $item_name !== '' ? $item_name : get_the_title( $source_ebook_id ), |
| 8973 |
'order_date' => $order_date, |
| 8974 |
'order_timestamp' => $order_timestamp, |
| 8975 |
'order_edit_url' => get_edit_post_link( $order_id, 'raw' ), |
| 8976 |
'already_sent' => false, |
| 8977 |
'existing_issue_order_id' => 0, |
| 8978 |
); |
| 8979 |
|
| 8980 |
$orders[ $order_id ] = $order; |
| 8981 |
$source_order_ids[] = $order_id; |
| 8982 |
} |
| 8983 |
} |
| 8984 |
wp_reset_postdata(); |
| 8985 |
|
| 8986 |
$existing_map = array(); |
| 8987 |
if ( $target_ebook_id > 0 && ! empty( $source_order_ids ) ) { |
| 8988 |
$existing_query = new WP_Query( |
| 8989 |
array( |
| 8990 |
'post_type' => 'ebook_order', |
| 8991 |
'post_status' => 'publish', |
| 8992 |
'posts_per_page' => -1, |
| 8993 |
'fields' => 'ids', |
| 8994 |
'meta_query' => array( |
| 8995 |
array( |
| 8996 |
'key' => 'ebook', |
| 8997 |
'value' => $target_ebook_id, |
| 8998 |
), |
| 8999 |
array( |
| 9000 |
'key' => 'issue_delivery_kind', |
| 9001 |
'value' => 'new_issue', |
| 9002 |
), |
| 9003 |
array( |
| 9004 |
'key' => 'issue_source_order_id', |
| 9005 |
'value' => $source_order_ids, |
| 9006 |
'compare' => 'IN', |
| 9007 |
), |
| 9008 |
), |
| 9009 |
) |
| 9010 |
); |
| 9011 |
|
| 9012 |
if ( $existing_query->have_posts() ) { |
| 9013 |
foreach ( $existing_query->posts as $issue_order_id ) { |
| 9014 |
$source_order_id = absint( get_post_meta( $issue_order_id, 'issue_source_order_id', true ) ); |
| 9015 |
if ( $source_order_id > 0 ) { |
| 9016 |
$existing_map[ $source_order_id ] = (int) $issue_order_id; |
| 9017 |
} |
| 9018 |
} |
| 9019 |
} |
| 9020 |
wp_reset_postdata(); |
| 9021 |
} |
| 9022 |
|
| 9023 |
foreach ( $orders as $order_id => $order ) { |
| 9024 |
$result['counts']['total']++; |
| 9025 |
|
| 9026 |
if ( ebook_store_is_negative_order_status( $order['status'] ) ) { |
| 9027 |
$result['skipped_negative'][] = $order; |
| 9028 |
$result['counts']['skipped_negative']++; |
| 9029 |
continue; |
| 9030 |
} |
| 9031 |
|
| 9032 |
if ( $order['payer_email'] === '' ) { |
| 9033 |
$result['skipped_invalid'][] = $order; |
| 9034 |
$result['counts']['skipped_invalid']++; |
| 9035 |
continue; |
| 9036 |
} |
| 9037 |
|
| 9038 |
if ( isset( $existing_map[ $order_id ] ) ) { |
| 9039 |
$order['already_sent'] = true; |
| 9040 |
$order['existing_issue_order_id'] = $existing_map[ $order_id ]; |
| 9041 |
$result['skipped_existing'][] = $order; |
| 9042 |
$result['counts']['skipped_existing']++; |
| 9043 |
continue; |
| 9044 |
} |
| 9045 |
|
| 9046 |
$result['eligible'][] = $order; |
| 9047 |
$result['counts']['eligible']++; |
| 9048 |
} |
| 9049 |
|
| 9050 |
$result['orders'] = array_values( $orders ); |
| 9051 |
|
| 9052 |
return $result; |
| 9053 |
} |
| 9054 |
|
| 9055 |
function ebook_store_process_new_issue_batch( $source_ebook_id, $target_ebook_id, $purchase_period, $offset = 0, $limit = 20 ) { |
| 9056 |
$source_ebook_id = absint( $source_ebook_id ); |
| 9057 |
$target_ebook_id = absint( $target_ebook_id ); |
| 9058 |
$offset = max( 0, absint( $offset ) ); |
| 9059 |
$limit = max( 1, absint( $limit ) ); |
| 9060 |
$preview = ebook_store_get_issue_candidates( $source_ebook_id, $target_ebook_id, $purchase_period ); |
| 9061 |
$eligible = isset( $preview['eligible'] ) ? $preview['eligible'] : array(); |
| 9062 |
$batch = array_slice( $eligible, $offset, $limit ); |
| 9063 |
$created = array(); |
| 9064 |
$failed = array(); |
| 9065 |
|
| 9066 |
foreach ( $batch as $order ) { |
| 9067 |
$new_order_data = array( |
| 9068 |
'first_name' => $order['first_name'], |
| 9069 |
'last_name' => $order['last_name'], |
| 9070 |
'mc_gross' => 0, |
| 9071 |
'ebook' => $target_ebook_id, |
| 9072 |
'payer_email' => $order['payer_email'], |
| 9073 |
'mc_fee' => 0, |
| 9074 |
'item_name' => get_the_title( $target_ebook_id ), |
| 9075 |
'payment_date' => date( 'm/d/Y H:i:s' ), |
| 9076 |
'txn_id' => 'Issue Delivery', |
| 9077 |
'residence_country' => $order['residence_country'], |
| 9078 |
'md5_nonce' => md5( microtime() . mt_rand( 1, 99999999 ) . NONCE_KEY ), |
| 9079 |
'user_id' => isset( $order['user_id'] ) ? $order['user_id'] : 0, |
| 9080 |
'payment_status' => 'Completed', |
| 9081 |
'payment_type' => 'new_issue', |
| 9082 |
); |
| 9083 |
|
| 9084 |
$created_order_id = ebook_store_add_order( $new_order_data, true, false ); |
| 9085 |
|
| 9086 |
if ( $created_order_id ) { |
| 9087 |
update_post_meta( $created_order_id, 'issue_delivery_kind', 'new_issue' ); |
| 9088 |
update_post_meta( $created_order_id, 'issue_source_order_id', $order['source_order_id'] ); |
| 9089 |
update_post_meta( $created_order_id, 'issue_source_ebook_id', $source_ebook_id ); |
| 9090 |
update_post_meta( $created_order_id, 'issue_target_ebook_id', $target_ebook_id ); |
| 9091 |
update_post_meta( $created_order_id, 'issue_purchase_period', $preview['purchase_period'] ); |
| 9092 |
$created[] = array( |
| 9093 |
'new_order_id' => $created_order_id, |
| 9094 |
'source_order_id' => $order['source_order_id'], |
| 9095 |
'payer_email' => $order['payer_email'], |
| 9096 |
); |
| 9097 |
} else { |
| 9098 |
$failed[] = $order; |
| 9099 |
} |
| 9100 |
} |
| 9101 |
|
| 9102 |
return array( |
| 9103 |
'preview' => $preview, |
| 9104 |
'offset' => $offset, |
| 9105 |
'limit' => $limit, |
| 9106 |
'processed' => count( $batch ), |
| 9107 |
'created' => $created, |
| 9108 |
'failed' => $failed, |
| 9109 |
'next_offset' => $offset + count( $batch ), |
| 9110 |
'completed' => ( $offset + count( $batch ) ) >= count( $eligible ), |
| 9111 |
); |
| 9112 |
} |
| 9113 |
|
| 9114 |
function ebook_store_is_wordfence_active() { |
| 9115 |
if ( ! function_exists( 'is_plugin_active' ) ) { |
| 9116 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 9117 |
} |
| 9118 |
|
| 9119 |
return function_exists( 'is_plugin_active' ) && is_plugin_active( 'wordfence/wordfence.php' ); |
| 9120 |
} |
| 9121 |
|
| 9122 |
function ebook_store_get_template_directory_path() { |
| 9123 |
return trailingslashit( plugin_dir_path( __FILE__ ) ) . 'templates/'; |
| 9124 |
} |
| 9125 |
|
| 9126 |
function ebook_store_get_legacy_custom_template_directory_path() { |
| 9127 |
return ebook_store_get_template_directory_path(); |
| 9128 |
} |
| 9129 |
|
| 9130 |
function ebook_store_get_custom_template_directory_path() { |
| 9131 |
$uploads = wp_upload_dir(); |
| 9132 |
if ( ! empty( $uploads['basedir'] ) ) { |
| 9133 |
return trailingslashit( $uploads['basedir'] ) . 'ebook-store/templates/'; |
| 9134 |
} |
| 9135 |
|
| 9136 |
return trailingslashit( WP_CONTENT_DIR ) . 'uploads/ebook-store/templates/'; |
| 9137 |
} |
| 9138 |
|
| 9139 |
function ebook_store_ensure_custom_template_directory() { |
| 9140 |
$directory = ebook_store_get_custom_template_directory_path(); |
| 9141 |
if ( is_dir( $directory ) ) { |
| 9142 |
return $directory; |
| 9143 |
} |
| 9144 |
|
| 9145 |
if ( wp_mkdir_p( $directory ) ) { |
| 9146 |
return $directory; |
| 9147 |
} |
| 9148 |
|
| 9149 |
return ''; |
| 9150 |
} |
| 9151 |
|
| 9152 |
function ebook_store_sync_custom_template_name( $template_code, $template_name ) { |
| 9153 |
$template_code = (string) $template_code; |
| 9154 |
$template_name = trim( (string) $template_name ); |
| 9155 |
if ( $template_name === '' ) { |
| 9156 |
return $template_code; |
| 9157 |
} |
| 9158 |
|
| 9159 |
if ( preg_match( '/Template Name:\s*(.+)$/mi', $template_code ) ) { |
| 9160 |
return (string) preg_replace( '/Template Name:\s*(.+)$/mi', 'Template Name: ' . $template_name, $template_code, 1 ); |
| 9161 |
} |
| 9162 |
|
| 9163 |
return "/*\nTemplate Name: " . $template_name . "\n*/\n" . $template_code; |
| 9164 |
} |
| 9165 |
|
| 9166 |
function ebook_store_get_unique_custom_template_identity( $preferred_name, $preferred_key = '' ) { |
| 9167 |
$preferred_name = trim( (string) $preferred_name ); |
| 9168 |
$preferred_key = sanitize_key( (string) $preferred_key ); |
| 9169 |
if ( $preferred_name === '' ) { |
| 9170 |
$preferred_name = __( 'Custom template', 'ebook-store' ); |
| 9171 |
} |
| 9172 |
if ( $preferred_key === '' ) { |
| 9173 |
$preferred_key = sanitize_title( $preferred_name ); |
| 9174 |
} |
| 9175 |
if ( $preferred_key === '' ) { |
| 9176 |
$preferred_key = 'custom-template'; |
| 9177 |
} |
| 9178 |
|
| 9179 |
$editable = ebook_store_get_template_editor_files(); |
| 9180 |
$directory = ebook_store_get_custom_template_directory_path(); |
| 9181 |
$name = $preferred_name; |
| 9182 |
$key = $preferred_key; |
| 9183 |
$index = 2; |
| 9184 |
|
| 9185 |
while ( isset( $editable[ $key ] ) || file_exists( $directory . $key . '.php' ) ) { |
| 9186 |
$key = $preferred_key . '-' . $index; |
| 9187 |
$name = $preferred_name . ' ' . $index; |
| 9188 |
$index++; |
| 9189 |
} |
| 9190 |
|
| 9191 |
return array( |
| 9192 |
'name' => $name, |
| 9193 |
'key' => $key, |
| 9194 |
); |
| 9195 |
} |
| 9196 |
|
| 9197 |
function ebook_store_create_custom_template_from_code( $template_name, $template_code, $preferred_key = '' ) { |
| 9198 |
$directory = ebook_store_ensure_custom_template_directory(); |
| 9199 |
if ( $directory === '' ) { |
| 9200 |
return new WP_Error( 'ebook_store_template_directory_unavailable', __( 'WordPress could not create the custom template storage folder inside uploads.', 'ebook-store' ) ); |
| 9201 |
} |
| 9202 |
|
| 9203 |
$identity = ebook_store_get_unique_custom_template_identity( $template_name, $preferred_key ); |
| 9204 |
$template_file = $directory . $identity['key'] . '.php'; |
| 9205 |
$template_code = ebook_store_sync_custom_template_name( $template_code, $identity['name'] ); |
| 9206 |
$syntax_check = ebook_store_validate_template_php_syntax( $template_code ); |
| 9207 |
if ( is_wp_error( $syntax_check ) ) { |
| 9208 |
return $syntax_check; |
| 9209 |
} |
| 9210 |
|
| 9211 |
$result = @file_put_contents( $template_file, $template_code ); |
| 9212 |
if ( $result === false ) { |
| 9213 |
return new WP_Error( 'ebook_store_template_create_failed', __( 'WordPress could not save the custom template file.', 'ebook-store' ) ); |
| 9214 |
} |
| 9215 |
|
| 9216 |
return array( |
| 9217 |
'key' => $identity['key'], |
| 9218 |
'name' => $identity['name'], |
| 9219 |
'file' => $template_file, |
| 9220 |
'code' => $template_code, |
| 9221 |
); |
| 9222 |
} |
| 9223 |
|
| 9224 |
function ebook_store_migrate_legacy_custom_templates() { |
| 9225 |
static $migrated = false; |
| 9226 |
if ( $migrated ) { |
| 9227 |
return; |
| 9228 |
} |
| 9229 |
$migrated = true; |
| 9230 |
|
| 9231 |
$legacy_dir = ebook_store_get_legacy_custom_template_directory_path(); |
| 9232 |
$custom_dir = ebook_store_ensure_custom_template_directory(); |
| 9233 |
if ( $custom_dir === '' || ! is_dir( $legacy_dir ) ) { |
| 9234 |
return; |
| 9235 |
} |
| 9236 |
|
| 9237 |
$core_files = array_map( 'basename', wp_list_pluck( ebook_store_get_core_form_templates(), 'file' ) ); |
| 9238 |
foreach ( (array) glob( $legacy_dir . '*.php' ) as $file ) { |
| 9239 |
$basename = basename( $file ); |
| 9240 |
if ( in_array( $basename, $core_files, true ) ) { |
| 9241 |
continue; |
| 9242 |
} |
| 9243 |
|
| 9244 |
$destination = $custom_dir . $basename; |
| 9245 |
if ( file_exists( $destination ) ) { |
| 9246 |
continue; |
| 9247 |
} |
| 9248 |
|
| 9249 |
@copy( $file, $destination ); |
| 9250 |
} |
| 9251 |
} |
| 9252 |
|
| 9253 |
function ebook_store_get_core_form_templates() { |
| 9254 |
return array( |
| 9255 |
'modern' => array( |
| 9256 |
'label' => __( 'Modern', 'ebook-store' ), |
| 9257 |
'file' => ebook_store_get_template_directory_path() . 'modern.php', |
| 9258 |
'core' => true, |
| 9259 |
), |
| 9260 |
'2026' => array( |
| 9261 |
'label' => __( '2026', 'ebook-store' ), |
| 9262 |
'file' => ebook_store_get_template_directory_path() . '2026.php', |
| 9263 |
'core' => true, |
| 9264 |
), |
| 9265 |
'hello-elementor-2026' => array( |
| 9266 |
'label' => __( 'Hello Elementor 2026', 'ebook-store' ), |
| 9267 |
'file' => ebook_store_get_template_directory_path() . 'hello-elementor-2026.php', |
| 9268 |
'core' => true, |
| 9269 |
), |
| 9270 |
'legacy' => array( |
| 9271 |
'label' => __( 'Legacy', 'ebook-store' ), |
| 9272 |
'file' => ebook_store_get_template_directory_path() . 'current.php', |
| 9273 |
'core' => true, |
| 9274 |
), |
| 9275 |
); |
| 9276 |
} |
| 9277 |
|
| 9278 |
function ebook_store_get_core_template_keys() { |
| 9279 |
return array_keys( ebook_store_get_core_form_templates() ); |
| 9280 |
} |
| 9281 |
|
| 9282 |
function ebook_store_template_editor_humanize_key( $key ) { |
| 9283 |
$key = trim( (string) $key ); |
| 9284 |
if ( $key === '' ) { |
| 9285 |
return __( 'Custom template', 'ebook-store' ); |
| 9286 |
} |
| 9287 |
|
| 9288 |
return ucwords( str_replace( array( '-', '_' ), ' ', $key ) ); |
| 9289 |
} |
| 9290 |
|
| 9291 |
function ebook_store_can_access_template_editor() { |
| 9292 |
if ( is_multisite() ) { |
| 9293 |
return is_super_admin(); |
| 9294 |
} |
| 9295 |
|
| 9296 |
return current_user_can( 'manage_options' ); |
| 9297 |
} |
| 9298 |
|
| 9299 |
function ebook_store_get_template_header_label( $file ) { |
| 9300 |
if ( ! file_exists( $file ) ) { |
| 9301 |
return ''; |
| 9302 |
} |
| 9303 |
|
| 9304 |
$contents = (string) @file_get_contents( $file, false, null, 0, 2048 ); |
| 9305 |
if ( $contents === '' ) { |
| 9306 |
return ''; |
| 9307 |
} |
| 9308 |
|
| 9309 |
if ( preg_match( '/Template Name:\s*(.+)$/mi', $contents, $matches ) ) { |
| 9310 |
return trim( $matches[1] ); |
| 9311 |
} |
| 9312 |
|
| 9313 |
return ''; |
| 9314 |
} |
| 9315 |
|
| 9316 |
function ebook_store_get_custom_form_templates() { |
| 9317 |
ebook_store_migrate_legacy_custom_templates(); |
| 9318 |
$template_dir = ebook_store_get_custom_template_directory_path(); |
| 9319 |
$core_templates = ebook_store_get_core_form_templates(); |
| 9320 |
$core_files = array_map( 'basename', wp_list_pluck( $core_templates, 'file' ) ); |
| 9321 |
$templates = array(); |
| 9322 |
|
| 9323 |
foreach ( (array) glob( $template_dir . '*.php' ) as $file ) { |
| 9324 |
$basename = basename( $file ); |
| 9325 |
if ( in_array( $basename, $core_files, true ) ) { |
| 9326 |
continue; |
| 9327 |
} |
| 9328 |
|
| 9329 |
$key = sanitize_key( basename( $basename, '.php' ) ); |
| 9330 |
if ( $key === '' ) { |
| 9331 |
continue; |
| 9332 |
} |
| 9333 |
|
| 9334 |
$label = ebook_store_get_template_header_label( $file ); |
| 9335 |
if ( $label === '' ) { |
| 9336 |
$label = ebook_store_template_editor_humanize_key( $key ); |
| 9337 |
} |
| 9338 |
|
| 9339 |
$templates[ $key ] = array( |
| 9340 |
'label' => $label, |
| 9341 |
'file' => $file, |
| 9342 |
'core' => false, |
| 9343 |
); |
| 9344 |
} |
| 9345 |
|
| 9346 |
ksort( $templates ); |
| 9347 |
|
| 9348 |
return $templates; |
| 9349 |
} |
| 9350 |
|
| 9351 |
function ebook_store_get_template_usage_stats( $template_key ) { |
| 9352 |
$template_key = sanitize_key( (string) $template_key ); |
| 9353 |
$stats = array( |
| 9354 |
'ebooks' => 0, |
| 9355 |
'default' => false, |
| 9356 |
); |
| 9357 |
|
| 9358 |
if ( $template_key === '' ) { |
| 9359 |
return $stats; |
| 9360 |
} |
| 9361 |
|
| 9362 |
$ebook_ids = get_posts( |
| 9363 |
array( |
| 9364 |
'post_type' => 'ebook', |
| 9365 |
'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ), |
| 9366 |
'posts_per_page' => -1, |
| 9367 |
'fields' => 'ids', |
| 9368 |
'no_found_rows' => true, |
| 9369 |
) |
| 9370 |
); |
| 9371 |
|
| 9372 |
foreach ( (array) $ebook_ids as $ebook_id ) { |
| 9373 |
$ebook_meta = get_post_meta( (int) $ebook_id, 'ebook', true ); |
| 9374 |
$current_template = ''; |
| 9375 |
if ( is_array( $ebook_meta ) && ! empty( $ebook_meta['form_template'] ) ) { |
| 9376 |
$current_template = (string) $ebook_meta['form_template']; |
| 9377 |
} |
| 9378 |
|
| 9379 |
if ( function_exists( 'ebook_store_resolve_form_template' ) && ebook_store_resolve_form_template( $current_template ) === $template_key ) { |
| 9380 |
$stats['ebooks']++; |
| 9381 |
} |
| 9382 |
} |
| 9383 |
|
| 9384 |
$default_template = function_exists( 'ebook_store_resolve_form_template' ) ? ebook_store_resolve_form_template( get_option( 'ebook_store_form_template', '2026' ) ) : sanitize_key( (string) get_option( 'ebook_store_form_template', '2026' ) ); |
| 9385 |
$stats['default'] = $default_template === $template_key; |
| 9386 |
|
| 9387 |
return $stats; |
| 9388 |
} |
| 9389 |
|
| 9390 |
function ebook_store_reassign_template_usage( $from_template, $to_template ) { |
| 9391 |
$from_template = sanitize_key( (string) $from_template ); |
| 9392 |
$to_template = sanitize_key( (string) $to_template ); |
| 9393 |
$result = array( |
| 9394 |
'ebook_count' => 0, |
| 9395 |
'default_updated' => false, |
| 9396 |
); |
| 9397 |
|
| 9398 |
if ( $from_template === '' || $to_template === '' || $from_template === $to_template ) { |
| 9399 |
return $result; |
| 9400 |
} |
| 9401 |
|
| 9402 |
$ebook_ids = get_posts( |
| 9403 |
array( |
| 9404 |
'post_type' => 'ebook', |
| 9405 |
'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ), |
| 9406 |
'posts_per_page' => -1, |
| 9407 |
'fields' => 'ids', |
| 9408 |
'no_found_rows' => true, |
| 9409 |
) |
| 9410 |
); |
| 9411 |
|
| 9412 |
foreach ( (array) $ebook_ids as $ebook_id ) { |
| 9413 |
$ebook_id = (int) $ebook_id; |
| 9414 |
$ebook_meta = get_post_meta( $ebook_id, 'ebook', true ); |
| 9415 |
if ( ! is_array( $ebook_meta ) ) { |
| 9416 |
$ebook_meta = array(); |
| 9417 |
} |
| 9418 |
|
| 9419 |
$current_template = isset( $ebook_meta['form_template'] ) ? (string) $ebook_meta['form_template'] : ''; |
| 9420 |
$resolved_template = function_exists( 'ebook_store_resolve_form_template' ) ? ebook_store_resolve_form_template( $current_template ) : sanitize_key( $current_template ); |
| 9421 |
if ( $resolved_template !== $from_template ) { |
| 9422 |
continue; |
| 9423 |
} |
| 9424 |
|
| 9425 |
$ebook_meta['form_template'] = $to_template; |
| 9426 |
update_post_meta( $ebook_id, 'ebook', $ebook_meta ); |
| 9427 |
$result['ebook_count']++; |
| 9428 |
} |
| 9429 |
|
| 9430 |
$default_template = function_exists( 'ebook_store_resolve_form_template' ) ? ebook_store_resolve_form_template( get_option( 'ebook_store_form_template', '2026' ) ) : sanitize_key( (string) get_option( 'ebook_store_form_template', '2026' ) ); |
| 9431 |
if ( $default_template === $from_template ) { |
| 9432 |
update_option( 'ebook_store_form_template', $to_template ); |
| 9433 |
$result['default_updated'] = true; |
| 9434 |
} |
| 9435 |
|
| 9436 |
return $result; |
| 9437 |
} |
| 9438 |
|
| 9439 |
function ebook_store_is_core_template( $template_key ) { |
| 9440 |
return in_array( sanitize_key( (string) $template_key ), ebook_store_get_core_template_keys(), true ); |
| 9441 |
} |
| 9442 |
|
| 9443 |
function ebook_store_get_template_editor_files() { |
| 9444 |
$templates = function_exists( 'ebook_store_get_form_templates' ) ? ebook_store_get_form_templates() : array(); |
| 9445 |
$editable = array(); |
| 9446 |
|
| 9447 |
foreach ( $templates as $template_key => $template ) { |
| 9448 |
if ( empty( $template['file'] ) || ! file_exists( $template['file'] ) ) { |
| 9449 |
continue; |
| 9450 |
} |
| 9451 |
|
| 9452 |
$editable[ $template_key ] = array( |
| 9453 |
'key' => (string) $template_key, |
| 9454 |
'label' => isset( $template['label'] ) ? $template['label'] : ucfirst( $template_key ), |
| 9455 |
'file' => $template['file'], |
| 9456 |
'filename' => basename( $template['file'] ), |
| 9457 |
'core' => ! empty( $template['core'] ), |
| 9458 |
); |
| 9459 |
} |
| 9460 |
|
| 9461 |
return $editable; |
| 9462 |
} |
| 9463 |
|
| 9464 |
function ebook_store_get_template_editor_current_key( $requested = '' ) { |
| 9465 |
$requested = sanitize_key( (string) $requested ); |
| 9466 |
$editable = ebook_store_get_template_editor_files(); |
| 9467 |
|
| 9468 |
if ( $requested !== '' && isset( $editable[ $requested ] ) ) { |
| 9469 |
return $requested; |
| 9470 |
} |
| 9471 |
|
| 9472 |
$keys = array_keys( $editable ); |
| 9473 |
|
| 9474 |
return ! empty( $keys ) ? $keys[0] : ''; |
| 9475 |
} |
| 9476 |
|
| 9477 |
function ebook_store_get_empty_template_scaffold( $template_name ) { |
| 9478 |
$template_name = trim( (string) $template_name ); |
| 9479 |
|
| 9480 |
return "<?php\n/*\nTemplate Name: " . $template_name . "\n*/\nif ( ! defined( 'ABSPATH' ) ) {\n\texit;\n}\n\n\$items = isset( \$context['items'] ) && is_array( \$context['items'] ) ? \$context['items'] : array();\n?>\n<div class=\"ebook-store-template-custom\">\n\t<?php foreach ( \$items as \$item ) : ?>\n\t\t<article class=\"ebook-custom-card\">\n\t\t\t<h3><?php echo esc_html( \$item['title'] ); ?></h3>\n\t\t\t<div><?php echo \$item['content_html']; ?></div>\n\t\t\t<?php echo \$item['form_html']; ?>\n\t\t</article>\n\t<?php endforeach; ?>\n</div>\n"; |
| 9481 |
} |
| 9482 |
|
| 9483 |
function ebook_store_validate_template_php_syntax( $template_code ) { |
| 9484 |
$template_code = (string) $template_code; |
| 9485 |
|
| 9486 |
if ( trim( $template_code ) === '' ) { |
| 9487 |
return new WP_Error( 'ebook_store_template_empty', __( 'Template code cannot be empty.', 'ebook-store' ) ); |
| 9488 |
} |
| 9489 |
|
| 9490 |
if ( strpos( $template_code, '<?php' ) === false ) { |
| 9491 |
return new WP_Error( 'ebook_store_template_missing_php', __( 'Template code must contain PHP tags so it can be loaded safely by the plugin.', 'ebook-store' ) ); |
| 9492 |
} |
| 9493 |
|
| 9494 |
if ( ! function_exists( 'shell_exec' ) ) { |
| 9495 |
return true; |
| 9496 |
} |
| 9497 |
|
| 9498 |
$php_binary = defined( 'PHP_BINARY' ) && PHP_BINARY ? PHP_BINARY : ''; |
| 9499 |
if ( ! $php_binary || ! is_executable( $php_binary ) ) { |
| 9500 |
$php_binary = trim( (string) @shell_exec( 'command -v php 2>/dev/null' ) ); |
| 9501 |
} |
| 9502 |
|
| 9503 |
if ( $php_binary === '' ) { |
| 9504 |
return true; |
| 9505 |
} |
| 9506 |
|
| 9507 |
$temp_file = wp_tempnam( 'ebook-store-template-lint' ); |
| 9508 |
if ( ! $temp_file ) { |
| 9509 |
return true; |
| 9510 |
} |
| 9511 |
|
| 9512 |
@file_put_contents( $temp_file, $template_code ); |
| 9513 |
$command = escapeshellarg( $php_binary ) . ' -l ' . escapeshellarg( $temp_file ) . ' 2>&1'; |
| 9514 |
$output = @shell_exec( $command ); |
| 9515 |
@unlink( $temp_file ); |
| 9516 |
|
| 9517 |
if ( ! is_string( $output ) ) { |
| 9518 |
return true; |
| 9519 |
} |
| 9520 |
|
| 9521 |
if ( stripos( $output, 'No syntax errors detected' ) !== false ) { |
| 9522 |
return true; |
| 9523 |
} |
| 9524 |
|
| 9525 |
$message = trim( preg_replace( '/\s+in\s+.+?on line\s+/i', ' on line ', $output ) ); |
| 9526 |
if ( $message === '' ) { |
| 9527 |
$message = __( 'PHP syntax validation failed for the template code.', 'ebook-store' ); |
| 9528 |
} |
| 9529 |
|
| 9530 |
return new WP_Error( 'ebook_store_template_syntax_error', $message ); |
| 9531 |
} |
| 9532 |
|
| 9533 |
function ebook_store_template_editor_create_template() { |
| 9534 |
$template_name = isset( $_POST['new_template_name'] ) ? sanitize_text_field( wp_unslash( $_POST['new_template_name'] ) ) : ''; |
| 9535 |
$template_source = isset( $_POST['new_template_source'] ) ? sanitize_key( wp_unslash( $_POST['new_template_source'] ) ) : ''; |
| 9536 |
$template_key = sanitize_title( $template_name ); |
| 9537 |
$editable = ebook_store_get_template_editor_files(); |
| 9538 |
|
| 9539 |
if ( $template_name === '' ) { |
| 9540 |
return array( |
| 9541 |
'errors' => array( __( 'Please enter a template name.', 'ebook-store' ) ), |
| 9542 |
); |
| 9543 |
} |
| 9544 |
|
| 9545 |
if ( $template_key === '' ) { |
| 9546 |
return array( |
| 9547 |
'errors' => array( __( 'The template name must contain letters or numbers.', 'ebook-store' ) ), |
| 9548 |
); |
| 9549 |
} |
| 9550 |
|
| 9551 |
if ( isset( $editable[ $template_key ] ) ) { |
| 9552 |
return array( |
| 9553 |
'errors' => array( __( 'A template with that name already exists.', 'ebook-store' ) ), |
| 9554 |
); |
| 9555 |
} |
| 9556 |
|
| 9557 |
$template_dir = ebook_store_ensure_custom_template_directory(); |
| 9558 |
if ( $template_dir === '' ) { |
| 9559 |
return array( |
| 9560 |
'errors' => array( __( 'WordPress could not create the custom template storage folder inside uploads.', 'ebook-store' ) ), |
| 9561 |
); |
| 9562 |
} |
| 9563 |
|
| 9564 |
$template_file = $template_dir . $template_key . '.php'; |
| 9565 |
if ( file_exists( $template_file ) ) { |
| 9566 |
return array( |
| 9567 |
'errors' => array( __( 'A template file with that slug already exists.', 'ebook-store' ) ), |
| 9568 |
); |
| 9569 |
} |
| 9570 |
|
| 9571 |
$template_code = ''; |
| 9572 |
if ( $template_source !== '' && $template_source !== 'none' && isset( $editable[ $template_source ] ) && file_exists( $editable[ $template_source ]['file'] ) ) { |
| 9573 |
$template_code = (string) file_get_contents( $editable[ $template_source ]['file'] ); |
| 9574 |
if ( preg_match( '/Template Name:\s*(.+)$/mi', $template_code ) ) { |
| 9575 |
$template_code = preg_replace( '/Template Name:\s*(.+)$/mi', 'Template Name: ' . $template_name, $template_code, 1 ); |
| 9576 |
} |
| 9577 |
} else { |
| 9578 |
$template_code = ebook_store_get_empty_template_scaffold( $template_name ); |
| 9579 |
} |
| 9580 |
|
| 9581 |
$syntax_check = ebook_store_validate_template_php_syntax( $template_code ); |
| 9582 |
if ( is_wp_error( $syntax_check ) ) { |
| 9583 |
return array( |
| 9584 |
'errors' => array( $syntax_check->get_error_message() ), |
| 9585 |
); |
| 9586 |
} |
| 9587 |
|
| 9588 |
$result = @file_put_contents( $template_file, $template_code ); |
| 9589 |
if ( $result === false ) { |
| 9590 |
return array( |
| 9591 |
'errors' => array( __( 'WordPress could not create the new template file.', 'ebook-store' ) ), |
| 9592 |
); |
| 9593 |
} |
| 9594 |
|
| 9595 |
wp_safe_redirect( |
| 9596 |
ebook_store_get_template_editor_url( |
| 9597 |
array( |
| 9598 |
'template' => $template_key, |
| 9599 |
'created' => 1, |
| 9600 |
) |
| 9601 |
) |
| 9602 |
); |
| 9603 |
exit; |
| 9604 |
} |
| 9605 |
|
| 9606 |
function ebook_store_template_editor_delete_template() { |
| 9607 |
$template_key = ebook_store_get_template_editor_current_key( isset( $_POST['template_key'] ) ? wp_unslash( $_POST['template_key'] ) : '' ); |
| 9608 |
$editable = ebook_store_get_template_editor_files(); |
| 9609 |
|
| 9610 |
if ( $template_key === '' || ! isset( $editable[ $template_key ] ) ) { |
| 9611 |
return array( |
| 9612 |
'errors' => array( __( 'The selected template could not be found.', 'ebook-store' ) ), |
| 9613 |
); |
| 9614 |
} |
| 9615 |
|
| 9616 |
if ( ! empty( $editable[ $template_key ]['core'] ) ) { |
| 9617 |
return array( |
| 9618 |
'errors' => array( __( 'Built-in templates cannot be deleted.', 'ebook-store' ) ), |
| 9619 |
'template_key' => $template_key, |
| 9620 |
); |
| 9621 |
} |
| 9622 |
|
| 9623 |
if ( ! file_exists( $editable[ $template_key ]['file'] ) || ! is_writable( $editable[ $template_key ]['file'] ) ) { |
| 9624 |
return array( |
| 9625 |
'errors' => array( __( 'That template file could not be deleted because it is missing or not writable.', 'ebook-store' ) ), |
| 9626 |
'template_key' => $template_key, |
| 9627 |
); |
| 9628 |
} |
| 9629 |
|
| 9630 |
$result = @unlink( $editable[ $template_key ]['file'] ); |
| 9631 |
if ( ! $result ) { |
| 9632 |
return array( |
| 9633 |
'errors' => array( __( 'WordPress could not delete that template file.', 'ebook-store' ) ), |
| 9634 |
'template_key' => $template_key, |
| 9635 |
); |
| 9636 |
} |
| 9637 |
|
| 9638 |
wp_safe_redirect( |
| 9639 |
ebook_store_get_template_editor_url( |
| 9640 |
array( |
| 9641 |
'template' => 'modern', |
| 9642 |
'deleted' => 1, |
| 9643 |
) |
| 9644 |
) |
| 9645 |
); |
| 9646 |
exit; |
| 9647 |
} |
| 9648 |
|
| 9649 |
function ebook_store_template_editor_save_template() { |
| 9650 |
$template_key = ebook_store_get_template_editor_current_key( isset( $_POST['template_key'] ) ? wp_unslash( $_POST['template_key'] ) : '' ); |
| 9651 |
$editable = ebook_store_get_template_editor_files(); |
| 9652 |
|
| 9653 |
if ( $template_key === '' || ! isset( $editable[ $template_key ] ) ) { |
| 9654 |
return array( |
| 9655 |
'errors' => array( __( 'The selected template could not be found.', 'ebook-store' ) ), |
| 9656 |
); |
| 9657 |
} |
| 9658 |
|
| 9659 |
$template_code = isset( $_POST['template_code'] ) ? wp_unslash( $_POST['template_code'] ) : ''; |
| 9660 |
|
| 9661 |
$syntax_check = ebook_store_validate_template_php_syntax( $template_code ); |
| 9662 |
if ( is_wp_error( $syntax_check ) ) { |
| 9663 |
return array( |
| 9664 |
'errors' => array( $syntax_check->get_error_message() ), |
| 9665 |
'template_key' => $template_key, |
| 9666 |
'template_code' => $template_code, |
| 9667 |
); |
| 9668 |
} |
| 9669 |
|
| 9670 |
if ( ! empty( $editable[ $template_key ]['core'] ) ) { |
| 9671 |
$preferred_name = sprintf( __( '%s Custom', 'ebook-store' ), $editable[ $template_key ]['label'] ); |
| 9672 |
$preferred_key = sanitize_title( $template_key . '-custom' ); |
| 9673 |
$created_template = ebook_store_create_custom_template_from_code( $preferred_name, $template_code, $preferred_key ); |
| 9674 |
if ( is_wp_error( $created_template ) ) { |
| 9675 |
return array( |
| 9676 |
'errors' => array( $created_template->get_error_message() ), |
| 9677 |
'template_key' => $template_key, |
| 9678 |
'template_code' => $template_code, |
| 9679 |
); |
| 9680 |
} |
| 9681 |
|
| 9682 |
$reassign = ! empty( $_POST['template_editor_reassign_books'] ); |
| 9683 |
$assignment = array( |
| 9684 |
'ebook_count' => 0, |
| 9685 |
'default_updated' => false, |
| 9686 |
); |
| 9687 |
if ( $reassign ) { |
| 9688 |
$assignment = ebook_store_reassign_template_usage( $template_key, $created_template['key'] ); |
| 9689 |
} |
| 9690 |
|
| 9691 |
wp_safe_redirect( |
| 9692 |
ebook_store_get_template_editor_url( |
| 9693 |
array( |
| 9694 |
'template' => $created_template['key'], |
| 9695 |
'created' => 1, |
| 9696 |
'forked_from_core' => 1, |
| 9697 |
'copied_from' => $template_key, |
| 9698 |
'reassigned' => $reassign ? 1 : 0, |
| 9699 |
'reassigned_ebooks' => isset( $assignment['ebook_count'] ) ? (int) $assignment['ebook_count'] : 0, |
| 9700 |
'reassigned_default' => ! empty( $assignment['default_updated'] ) ? 1 : 0, |
| 9701 |
) |
| 9702 |
) |
| 9703 |
); |
| 9704 |
exit; |
| 9705 |
} |
| 9706 |
|
| 9707 |
$template_file = $editable[ $template_key ]['file']; |
| 9708 |
if ( ! is_writable( $template_file ) ) { |
| 9709 |
return array( |
| 9710 |
'errors' => array( __( 'That template file is not writable on the server.', 'ebook-store' ) ), |
| 9711 |
'template_key' => $template_key, |
| 9712 |
'template_code' => $template_code, |
| 9713 |
); |
| 9714 |
} |
| 9715 |
|
| 9716 |
$result = @file_put_contents( $template_file, $template_code ); |
| 9717 |
|
| 9718 |
if ( $result === false ) { |
| 9719 |
return array( |
| 9720 |
'errors' => array( __( 'WordPress could not save the updated template code.', 'ebook-store' ) ), |
| 9721 |
'template_key' => $template_key, |
| 9722 |
'template_code' => $template_code, |
| 9723 |
); |
| 9724 |
} |
| 9725 |
|
| 9726 |
wp_safe_redirect( |
| 9727 |
ebook_store_get_template_editor_url( |
| 9728 |
array( |
| 9729 |
'template' => $template_key, |
| 9730 |
'saved' => 1, |
| 9731 |
) |
| 9732 |
) |
| 9733 |
); |
| 9734 |
exit; |
| 9735 |
} |
| 9736 |
|
| 9737 |
function ebook_store_template_editor_handle_submission() { |
| 9738 |
if ( empty( $_POST['ebook_store_template_editor_action'] ) ) { |
| 9739 |
return null; |
| 9740 |
} |
| 9741 |
|
| 9742 |
if ( ! ebook_store_can_access_template_editor() ) { |
| 9743 |
return array( |
| 9744 |
'errors' => array( __( 'You do not have permission to edit Ebook Store templates.', 'ebook-store' ) ), |
| 9745 |
); |
| 9746 |
} |
| 9747 |
|
| 9748 |
check_admin_referer( 'ebook_store_template_editor' ); |
| 9749 |
|
| 9750 |
$action_type = isset( $_POST['template_editor_action_type'] ) ? sanitize_key( wp_unslash( $_POST['template_editor_action_type'] ) ) : 'save'; |
| 9751 |
|
| 9752 |
if ( $action_type === 'create' ) { |
| 9753 |
return ebook_store_template_editor_create_template(); |
| 9754 |
} |
| 9755 |
|
| 9756 |
if ( $action_type === 'delete' ) { |
| 9757 |
return ebook_store_template_editor_delete_template(); |
| 9758 |
} |
| 9759 |
|
| 9760 |
return ebook_store_template_editor_save_template(); |
| 9761 |
} |
| 9762 |
|
| 9763 |
function ebook_store_template_editor_page_callback() { |
| 9764 |
if ( ! ebook_store_can_access_template_editor() ) { |
| 9765 |
wp_die( |
| 9766 |
esc_html__( 'You do not have permission to access the Ebook Store template editor.', 'ebook-store' ), |
| 9767 |
esc_html__( 'Access denied', 'ebook-store' ), |
| 9768 |
array( 'response' => 403 ) |
| 9769 |
); |
| 9770 |
} |
| 9771 |
|
| 9772 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 9773 |
$locale = ebook_store_get_locale_strings(); |
| 9774 |
$formats_locale = ebook_store_get_format_labels(); |
| 9775 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 9776 |
|
| 9777 |
$code_editor_settings = wp_enqueue_code_editor( |
| 9778 |
array( |
| 9779 |
'type' => 'application/x-httpd-php', |
| 9780 |
'codemirror' => array( |
| 9781 |
'indentUnit' => 4, |
| 9782 |
'tabSize' => 4, |
| 9783 |
'lineNumbers' => true, |
| 9784 |
'lineWrapping' => false, |
| 9785 |
'mode' => 'application/x-httpd-php', |
| 9786 |
), |
| 9787 |
) |
| 9788 |
); |
| 9789 |
|
| 9790 |
wp_enqueue_script( 'code-editor' ); |
| 9791 |
wp_enqueue_script( 'wp-theme-plugin-editor' ); |
| 9792 |
wp_enqueue_style( 'code-editor' ); |
| 9793 |
|
| 9794 |
$submission = null; |
| 9795 |
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['ebook_store_template_editor_action'] ) ) { |
| 9796 |
$submission = ebook_store_template_editor_handle_submission(); |
| 9797 |
} |
| 9798 |
|
| 9799 |
$editable = ebook_store_get_template_editor_files(); |
| 9800 |
$current_template = ebook_store_get_template_editor_current_key( isset( $_REQUEST['template'] ) ? wp_unslash( $_REQUEST['template'] ) : '' ); |
| 9801 |
if ( $submission && ! empty( $submission['template_key'] ) ) { |
| 9802 |
$current_template = ebook_store_get_template_editor_current_key( $submission['template_key'] ); |
| 9803 |
} |
| 9804 |
|
| 9805 |
$current = $current_template !== '' && isset( $editable[ $current_template ] ) ? $editable[ $current_template ] : null; |
| 9806 |
$current_code = ''; |
| 9807 |
|
| 9808 |
if ( $submission && array_key_exists( 'template_code', (array) $submission ) ) { |
| 9809 |
$current_code = (string) $submission['template_code']; |
| 9810 |
} elseif ( $current && file_exists( $current['file'] ) ) { |
| 9811 |
$current_code = (string) file_get_contents( $current['file'] ); |
| 9812 |
} |
| 9813 |
|
| 9814 |
$saved = isset( $_GET['saved'] ) && (int) $_GET['saved'] === 1; |
| 9815 |
$created = isset( $_GET['created'] ) && (int) $_GET['created'] === 1; |
| 9816 |
$deleted = isset( $_GET['deleted'] ) && (int) $_GET['deleted'] === 1; |
| 9817 |
$forked_from_core = isset( $_GET['forked_from_core'] ) && (int) $_GET['forked_from_core'] === 1; |
| 9818 |
$copied_from = isset( $_GET['copied_from'] ) ? sanitize_key( wp_unslash( $_GET['copied_from'] ) ) : ''; |
| 9819 |
$reassigned = isset( $_GET['reassigned'] ) && (int) $_GET['reassigned'] === 1; |
| 9820 |
$reassigned_ebooks = isset( $_GET['reassigned_ebooks'] ) ? max( 0, (int) $_GET['reassigned_ebooks'] ) : 0; |
| 9821 |
$reassigned_default = isset( $_GET['reassigned_default'] ) && (int) $_GET['reassigned_default'] === 1; |
| 9822 |
$errors = $submission && ! empty( $submission['errors'] ) ? $submission['errors'] : array(); |
| 9823 |
$wordfence_active = ebook_store_is_wordfence_active(); |
| 9824 |
$current_usage = $current ? ebook_store_get_template_usage_stats( $current['key'] ) : array( |
| 9825 |
'ebooks' => 0, |
| 9826 |
'default' => false, |
| 9827 |
); |
| 9828 |
|
| 9829 |
ob_start(); |
| 9830 |
?> |
| 9831 |
<div class="wrap ebook-template-editor"> |
| 9832 |
<div class="ebook-template-editor__hero"> |
| 9833 |
<div class="ebook-template-editor__hero-copy"> |
| 9834 |
<span class="ebook-template-editor__eyebrow"><?php esc_html_e( 'Developer tools', 'ebook-store' ); ?></span> |
| 9835 |
<h1><?php esc_html_e( 'Button templates', 'ebook-store' ); ?></h1> |
| 9836 |
<p><?php esc_html_e( 'Edit the PHP templates that drive Ebook Store order-form layouts. Built-in templates are forked into update-safe custom copies when you save them, while custom templates are saved in their own separate files.', 'ebook-store' ); ?></p> |
| 9837 |
<p><?php esc_html_e( 'If you want a different design, you can copy the template code into ChatGPT and ask for changes that match your site, then paste the updated code back here.', 'ebook-store' ); ?></p> |
| 9838 |
</div> |
| 9839 |
<div class="ebook-template-editor__hero-meta"> |
| 9840 |
<span class="ebook-template-editor__pill"><?php esc_html_e( 'PHP-safe editing', 'ebook-store' ); ?></span> |
| 9841 |
<span class="ebook-template-editor__pill"><?php esc_html_e( 'Update-safe custom copies', 'ebook-store' ); ?></span> |
| 9842 |
<span class="ebook-template-editor__pill"><?php esc_html_e( 'Template-scoped access only', 'ebook-store' ); ?></span> |
| 9843 |
</div> |
| 9844 |
</div> |
| 9845 |
|
| 9846 |
<?php if ( $saved && $current ) : ?> |
| 9847 |
<div class="notice notice-success is-dismissible"> |
| 9848 |
<p> |
| 9849 |
<strong><?php esc_html_e( 'Template saved.', 'ebook-store' ); ?></strong> |
| 9850 |
<?php |
| 9851 |
printf( |
| 9852 |
esc_html__( '%s was updated successfully.', 'ebook-store' ), |
| 9853 |
esc_html( $current['label'] ) |
| 9854 |
); |
| 9855 |
?> |
| 9856 |
</p> |
| 9857 |
</div> |
| 9858 |
<?php endif; ?> |
| 9859 |
|
| 9860 |
<?php if ( $created && $current && ! $forked_from_core ) : ?> |
| 9861 |
<div class="notice notice-success is-dismissible"> |
| 9862 |
<p> |
| 9863 |
<strong><?php esc_html_e( 'Template created.', 'ebook-store' ); ?></strong> |
| 9864 |
<?php |
| 9865 |
printf( |
| 9866 |
esc_html__( '%s is now available in the template selector and stored separately from plugin updates.', 'ebook-store' ), |
| 9867 |
esc_html( $current['label'] ) |
| 9868 |
); |
| 9869 |
?> |
| 9870 |
</p> |
| 9871 |
</div> |
| 9872 |
<?php endif; ?> |
| 9873 |
|
| 9874 |
<?php if ( $created && $current && $forked_from_core ) : ?> |
| 9875 |
<div class="notice notice-success is-dismissible"> |
| 9876 |
<p> |
| 9877 |
<strong><?php esc_html_e( 'Custom template created.', 'ebook-store' ); ?></strong> |
| 9878 |
<?php |
| 9879 |
printf( |
| 9880 |
esc_html__( '%1$s was saved as the new custom template %2$s, so it will survive plugin updates.', 'ebook-store' ), |
| 9881 |
esc_html( $copied_from !== '' && isset( $editable[ $copied_from ] ) ? $editable[ $copied_from ]['label'] : __( 'Built-in template', 'ebook-store' ) ), |
| 9882 |
esc_html( $current['label'] ) |
| 9883 |
); |
| 9884 |
?> |
| 9885 |
</p> |
| 9886 |
<?php if ( $reassigned ) : ?> |
| 9887 |
<p> |
| 9888 |
<?php |
| 9889 |
printf( |
| 9890 |
esc_html__( '%1$d ebook(s) were reassigned to use this custom template.%2$s', 'ebook-store' ), |
| 9891 |
(int) $reassigned_ebooks, |
| 9892 |
$reassigned_default ? ' ' . esc_html__( 'The default template setting was updated too.', 'ebook-store' ) : '' |
| 9893 |
); |
| 9894 |
?> |
| 9895 |
</p> |
| 9896 |
<?php endif; ?> |
| 9897 |
</div> |
| 9898 |
<?php endif; ?> |
| 9899 |
|
| 9900 |
<?php if ( $deleted ) : ?> |
| 9901 |
<div class="notice notice-success is-dismissible"> |
| 9902 |
<p><strong><?php esc_html_e( 'Template deleted.', 'ebook-store' ); ?></strong> <?php esc_html_e( 'The custom template file was removed.', 'ebook-store' ); ?></p> |
| 9903 |
</div> |
| 9904 |
<?php endif; ?> |
| 9905 |
|
| 9906 |
<?php if ( ! empty( $errors ) ) : ?> |
| 9907 |
<div class="notice notice-error"> |
| 9908 |
<p><strong><?php esc_html_e( 'The template could not be saved yet.', 'ebook-store' ); ?></strong></p> |
| 9909 |
<ul class="ebook-template-editor__notice-list"> |
| 9910 |
<?php foreach ( $errors as $error ) : ?> |
| 9911 |
<li><?php echo esc_html( $error ); ?></li> |
| 9912 |
<?php endforeach; ?> |
| 9913 |
</ul> |
| 9914 |
</div> |
| 9915 |
<?php endif; ?> |
| 9916 |
|
| 9917 |
<?php if ( $wordfence_active ) : ?> |
| 9918 |
<div class="notice notice-warning"> |
| 9919 |
<p> |
| 9920 |
<strong><?php esc_html_e( 'Wordfence note:', 'ebook-store' ); ?></strong> |
| 9921 |
<?php esc_html_e( 'Saving PHP template files can trigger a Wordfence false positive because this editor writes PHP template files for Ebook Store custom layouts.', 'ebook-store' ); ?> |
| 9922 |
</p> |
| 9923 |
<p><?php esc_html_e( 'If Wordfence blocks the save, review the prompt and click “I am certain this is a false positive.” to allow the custom template save.', 'ebook-store' ); ?></p> |
| 9924 |
</div> |
| 9925 |
<?php endif; ?> |
| 9926 |
|
| 9927 |
<div class="ebook-template-editor__layout"> |
| 9928 |
<aside class="ebook-template-editor__sidebar"> |
| 9929 |
<div class="ebook-template-editor__nav-card"> |
| 9930 |
<div class="ebook-template-editor__nav-header"> |
| 9931 |
<h2><?php esc_html_e( 'Templates', 'ebook-store' ); ?></h2> |
| 9932 |
<button type="button" class="button button-secondary ebook-template-editor__create-trigger" data-template-modal-open="create"><?php esc_html_e( 'New template', 'ebook-store' ); ?></button> |
| 9933 |
</div> |
| 9934 |
<div class="ebook-template-editor__nav"> |
| 9935 |
<?php foreach ( $editable as $template ) : ?> |
| 9936 |
<a class="ebook-template-editor__nav-item<?php echo $current_template === $template['key'] ? ' is-active' : ''; ?>" href="<?php echo esc_url( ebook_store_get_template_editor_url( array( 'template' => $template['key'] ) ) ); ?>"> |
| 9937 |
<strong><?php echo esc_html( $template['label'] ); ?></strong> |
| 9938 |
<small><?php echo esc_html( $template['filename'] ); ?></small> |
| 9939 |
</a> |
| 9940 |
<?php endforeach; ?> |
| 9941 |
</div> |
| 9942 |
</div> |
| 9943 |
</aside> |
| 9944 |
|
| 9945 |
<div class="ebook-template-editor__main"> |
| 9946 |
<?php if ( $current ) : ?> |
| 9947 |
<form method="post" class="ebook-template-editor__form"> |
| 9948 |
<?php wp_nonce_field( 'ebook_store_template_editor' ); ?> |
| 9949 |
<input type="hidden" name="ebook_store_template_editor_action" value="1" /> |
| 9950 |
<input type="hidden" name="template_key" value="<?php echo esc_attr( $current['key'] ); ?>" /> |
| 9951 |
|
| 9952 |
<div class="ebook-template-editor__panel"> |
| 9953 |
<div class="ebook-template-editor__panel-header"> |
| 9954 |
<div> |
| 9955 |
<h2><?php echo esc_html( $current['label'] ); ?></h2> |
| 9956 |
<p> |
| 9957 |
<?php |
| 9958 |
echo ! empty( $current['core'] ) |
| 9959 |
? esc_html__( 'Built-in templates are update-protected. Saving here creates a new custom template copy instead of overwriting the plugin file.', 'ebook-store' ) |
| 9960 |
: esc_html__( 'Editing the custom PHP template file used by Ebook Store for this layout.', 'ebook-store' ); |
| 9961 |
?> |
| 9962 |
</p> |
| 9963 |
</div> |
| 9964 |
<div class="ebook-template-editor__header-side"> |
| 9965 |
<div class="ebook-template-editor__file-meta"> |
| 9966 |
<span><strong><?php esc_html_e( 'File:', 'ebook-store' ); ?></strong> <?php echo esc_html( $current['filename'] ); ?></span> |
| 9967 |
<span><strong><?php esc_html_e( 'Writable:', 'ebook-store' ); ?></strong> <?php echo esc_html( ! empty( $current['core'] ) ? __( 'Copied to custom template on save', 'ebook-store' ) : ( is_writable( $current['file'] ) ? __( 'Yes', 'ebook-store' ) : __( 'No', 'ebook-store' ) ) ); ?></span> |
| 9968 |
</div> |
| 9969 |
<?php if ( empty( $current['core'] ) ) : ?> |
| 9970 |
<button type="button" class="button button-link-delete ebook-template-editor__delete-trigger" data-template-modal-open="delete"><?php esc_html_e( 'Delete template', 'ebook-store' ); ?></button> |
| 9971 |
<?php endif; ?> |
| 9972 |
</div> |
| 9973 |
</div> |
| 9974 |
|
| 9975 |
<div class="ebook-template-editor__editor-wrap"> |
| 9976 |
<textarea id="ebook_store_template_code" name="template_code" class="large-text code" rows="28" spellcheck="false"><?php echo esc_textarea( $current_code ); ?></textarea> |
| 9977 |
</div> |
| 9978 |
|
| 9979 |
<?php if ( ! empty( $current['core'] ) ) : ?> |
| 9980 |
<div class="ebook-template-editor__field"> |
| 9981 |
<label> |
| 9982 |
<input type="checkbox" name="template_editor_reassign_books" value="1" /> |
| 9983 |
<?php |
| 9984 |
printf( |
| 9985 |
esc_html__( 'Also assign the new custom template automatically to ebooks currently using %s.', 'ebook-store' ), |
| 9986 |
esc_html( $current['label'] ) |
| 9987 |
); |
| 9988 |
?> |
| 9989 |
</label> |
| 9990 |
<p class="description"> |
| 9991 |
<?php |
| 9992 |
printf( |
| 9993 |
esc_html__( 'Right now %1$d ebook(s)%2$s resolve to this template.', 'ebook-store' ), |
| 9994 |
(int) $current_usage['ebooks'], |
| 9995 |
! empty( $current_usage['default'] ) ? ' ' . esc_html__( 'The global default template also points to it.', 'ebook-store' ) : '' |
| 9996 |
); |
| 9997 |
?> |
| 9998 |
</p> |
| 9999 |
</div> |
| 10000 |
<?php endif; ?> |
| 10001 |
|
| 10002 |
<div class="ebook-template-editor__actions"> |
| 10003 |
<input type="hidden" name="template_editor_action_type" value="save" /> |
| 10004 |
<button type="submit" class="button button-primary button-hero"><?php echo esc_html( ! empty( $current['core'] ) ? __( 'Save as custom template', 'ebook-store' ) : __( 'Save template', 'ebook-store' ) ); ?></button> |
| 10005 |
<a class="button button-secondary" href="<?php echo esc_url( ebook_store_get_template_editor_url( array( 'template' => $current['key'] ) ) ); ?>"><?php esc_html_e( 'Reload file', 'ebook-store' ); ?></a> |
| 10006 |
</div> |
| 10007 |
<?php if ( $wordfence_active ) : ?> |
| 10008 |
<p class="description"><?php esc_html_e( 'Wordfence may ask for confirmation when this template is saved. If that happens, choose “I am certain this is a false positive.” and allow the action.', 'ebook-store' ); ?></p> |
| 10009 |
<?php endif; ?> |
| 10010 |
</div> |
| 10011 |
</form> |
| 10012 |
<?php else : ?> |
| 10013 |
<div class="ebook-template-editor__panel"> |
| 10014 |
<p><?php esc_html_e( 'No editable templates were found.', 'ebook-store' ); ?></p> |
| 10015 |
</div> |
| 10016 |
<?php endif; ?> |
| 10017 |
</div> |
| 10018 |
</div> |
| 10019 |
</div> |
| 10020 |
<div class="ebook-template-editor__modal" data-template-modal="create" hidden> |
| 10021 |
<div class="ebook-template-editor__modal-backdrop" data-template-modal-close></div> |
| 10022 |
<div class="ebook-template-editor__modal-dialog" role="dialog" aria-modal="true" aria-labelledby="ebook-template-editor-create-title"> |
| 10023 |
<form method="post" class="ebook-template-editor__modal-form"> |
| 10024 |
<?php wp_nonce_field( 'ebook_store_template_editor' ); ?> |
| 10025 |
<input type="hidden" name="ebook_store_template_editor_action" value="1" /> |
| 10026 |
<input type="hidden" name="template_editor_action_type" value="create" /> |
| 10027 |
<div class="ebook-template-editor__modal-header"> |
| 10028 |
<h2 id="ebook-template-editor-create-title"><?php esc_html_e( 'Create new template', 'ebook-store' ); ?></h2> |
| 10029 |
<button type="button" class="button-link" data-template-modal-close><?php esc_html_e( 'Close', 'ebook-store' ); ?></button> |
| 10030 |
</div> |
| 10031 |
<div class="ebook-template-editor__modal-body"> |
| 10032 |
<div class="ebook-template-editor__field"> |
| 10033 |
<label for="new_template_name"><?php esc_html_e( 'Template name', 'ebook-store' ); ?></label> |
| 10034 |
<input type="text" id="new_template_name" name="new_template_name" required /> |
| 10035 |
</div> |
| 10036 |
<div class="ebook-template-editor__field"> |
| 10037 |
<label for="new_template_source"><?php esc_html_e( 'Copy code from', 'ebook-store' ); ?></label> |
| 10038 |
<select id="new_template_source" name="new_template_source"> |
| 10039 |
<option value="none"><?php esc_html_e( 'None (start with an empty scaffold)', 'ebook-store' ); ?></option> |
| 10040 |
<?php foreach ( $editable as $template ) : ?> |
| 10041 |
<option value="<?php echo esc_attr( $template['key'] ); ?>"><?php echo esc_html( $template['label'] ); ?></option> |
| 10042 |
<?php endforeach; ?> |
| 10043 |
</select> |
| 10044 |
<p class="description"><?php esc_html_e( 'Choose an existing template to duplicate its code, or start from a clean PHP scaffold.', 'ebook-store' ); ?></p> |
| 10045 |
</div> |
| 10046 |
</div> |
| 10047 |
<div class="ebook-template-editor__modal-actions"> |
| 10048 |
<button type="button" class="button button-secondary" data-template-modal-close><?php esc_html_e( 'Cancel', 'ebook-store' ); ?></button> |
| 10049 |
<button type="submit" class="button button-primary"><?php esc_html_e( 'Create template', 'ebook-store' ); ?></button> |
| 10050 |
</div> |
| 10051 |
</form> |
| 10052 |
</div> |
| 10053 |
</div> |
| 10054 |
<?php if ( $current && empty( $current['core'] ) ) : ?> |
| 10055 |
<div class="ebook-template-editor__modal" data-template-modal="delete" hidden> |
| 10056 |
<div class="ebook-template-editor__modal-backdrop" data-template-modal-close></div> |
| 10057 |
<div class="ebook-template-editor__modal-dialog" role="dialog" aria-modal="true" aria-labelledby="ebook-template-editor-delete-title"> |
| 10058 |
<form method="post" class="ebook-template-editor__modal-form"> |
| 10059 |
<?php wp_nonce_field( 'ebook_store_template_editor' ); ?> |
| 10060 |
<input type="hidden" name="ebook_store_template_editor_action" value="1" /> |
| 10061 |
<input type="hidden" name="template_editor_action_type" value="delete" /> |
| 10062 |
<input type="hidden" name="template_key" value="<?php echo esc_attr( $current['key'] ); ?>" /> |
| 10063 |
<div class="ebook-template-editor__modal-header"> |
| 10064 |
<h2 id="ebook-template-editor-delete-title"><?php esc_html_e( 'Delete template', 'ebook-store' ); ?></h2> |
| 10065 |
<button type="button" class="button-link" data-template-modal-close><?php esc_html_e( 'Close', 'ebook-store' ); ?></button> |
| 10066 |
</div> |
| 10067 |
<div class="ebook-template-editor__modal-body"> |
| 10068 |
<p><?php printf( esc_html__( 'Delete the custom template “%s”? This removes the saved custom template file.', 'ebook-store' ), esc_html( $current['label'] ) ); ?></p> |
| 10069 |
<p class="description"><?php esc_html_e( 'Built-in templates are protected and cannot be removed. This action affects only this custom template file.', 'ebook-store' ); ?></p> |
| 10070 |
</div> |
| 10071 |
<div class="ebook-template-editor__modal-actions"> |
| 10072 |
<button type="button" class="button button-secondary" data-template-modal-close><?php esc_html_e( 'Cancel', 'ebook-store' ); ?></button> |
| 10073 |
<button type="submit" class="button button-primary button-link-delete"><?php esc_html_e( 'Delete template', 'ebook-store' ); ?></button> |
| 10074 |
</div> |
| 10075 |
</form> |
| 10076 |
</div> |
| 10077 |
</div> |
| 10078 |
<?php endif; ?> |
| 10079 |
<?php |
| 10080 |
|
| 10081 |
if ( $code_editor_settings && $current ) { |
| 10082 |
wp_add_inline_script( |
| 10083 |
'code-editor', |
| 10084 |
'jQuery(function(){wp.codeEditor.initialize("ebook_store_template_code",' . wp_json_encode( $code_editor_settings ) . ');});' |
| 10085 |
); |
| 10086 |
} |
| 10087 |
|
| 10088 |
wp_add_inline_script( |
| 10089 |
'code-editor', |
| 10090 |
'jQuery(function($){$(document).on("click","[data-template-modal-open]",function(){var target=$(this).data("templateModalOpen");$("[data-template-modal=\'"+target+"\']").prop("hidden",false);});$(document).on("click","[data-template-modal-close]",function(){$(this).closest("[data-template-modal]").prop("hidden",true);});$(document).on("keydown",function(event){if(event.key==="Escape"){$("[data-template-modal]").prop("hidden",true);}});});' |
| 10091 |
); |
| 10092 |
|
| 10093 |
$output = ob_get_clean(); |
| 10094 |
echo function_exists( 'ebook_store_translate_markup' ) ? ebook_store_translate_markup( $output ) : $output; |
| 10095 |
} |
| 10096 |
|
| 10097 |
function ebook_store_setup_wizard_menu_title() { |
| 10098 |
return sprintf( |
| 10099 |
'%s <span class="update-plugins count-1"><span class="plugin-count">%s</span></span>', |
| 10100 |
esc_html__( 'Setup wizard', 'ebook-store' ), |
| 10101 |
esc_html__( 'new', 'ebook-store' ) |
| 10102 |
); |
| 10103 |
} |
| 10104 |
|
| 10105 |
function ebook_store_setup_wizard_is_pdf( $file ) { |
| 10106 |
if ( empty( $file['name'] ) || empty( $file['tmp_name'] ) ) { |
| 10107 |
return false; |
| 10108 |
} |
| 10109 |
|
| 10110 |
$filetype = wp_check_filetype( basename( $file['name'] ) ); |
| 10111 |
$extension = isset( $filetype['ext'] ) ? strtolower( (string) $filetype['ext'] ) : ''; |
| 10112 |
$mime_type = isset( $filetype['type'] ) ? strtolower( (string) $filetype['type'] ) : ''; |
| 10113 |
|
| 10114 |
return $extension === 'pdf' || $mime_type === 'application/pdf'; |
| 10115 |
} |
| 10116 |
|
| 10117 |
function ebook_store_setup_wizard_human_mode_label( $mode ) { |
| 10118 |
if ( $mode === 'woocommerce' ) { |
| 10119 |
return __( 'WooCommerce integration', 'ebook-store' ); |
| 10120 |
} |
| 10121 |
|
| 10122 |
return __( 'Standalone ebook sales', 'ebook-store' ); |
| 10123 |
} |
| 10124 |
|
| 10125 |
function ebook_store_is_paypal_enabled() { |
| 10126 |
return (string) get_option( 'paypal_integration_enabled', '1' ) === '1'; |
| 10127 |
} |
| 10128 |
|
| 10129 |
function ebook_store_get_standalone_payment_method_state() { |
| 10130 |
$paypal_ready = ebook_store_is_paypal_enabled() && trim( (string) get_option( 'paypal_account', '' ) ) !== ''; |
| 10131 |
$stripe_ready = (string) get_option( 'stripe_integration_enabled', '0' ) === '1' |
| 10132 |
&& trim( (string) get_option( 'stripe_publishable_key', '' ) ) !== '' |
| 10133 |
&& trim( (string) get_option( 'stripe_secret_key', '' ) ) !== ''; |
| 10134 |
$adyen_environment = (string) get_option( 'adyen_environment', 'test' ); |
| 10135 |
$adyen_ready = (string) get_option( 'adyen_integration_enabled', '0' ) === '1' |
| 10136 |
&& trim( (string) get_option( 'adyen_api_key', '' ) ) !== '' |
| 10137 |
&& trim( (string) get_option( 'adyen_merchant_account', '' ) ) !== '' |
| 10138 |
&& trim( (string) get_option( 'adyen_hmac_key', '' ) ) !== '' |
| 10139 |
&& ( $adyen_environment !== 'live' || trim( (string) get_option( 'adyen_live_prefix', '' ) ) !== '' ); |
| 10140 |
|
| 10141 |
return array( |
| 10142 |
'paypal' => array( |
| 10143 |
'enabled' => ebook_store_is_paypal_enabled(), |
| 10144 |
'ready' => $paypal_ready, |
| 10145 |
), |
| 10146 |
'stripe' => array( |
| 10147 |
'enabled' => (string) get_option( 'stripe_integration_enabled', '0' ) === '1', |
| 10148 |
'ready' => $stripe_ready, |
| 10149 |
), |
| 10150 |
'adyen' => array( |
| 10151 |
'enabled' => (string) get_option( 'adyen_integration_enabled', '0' ) === '1', |
| 10152 |
'ready' => $adyen_ready, |
| 10153 |
), |
| 10154 |
); |
| 10155 |
} |
| 10156 |
|
| 10157 |
function ebook_store_has_ready_standalone_payment_method() { |
| 10158 |
$methods = ebook_store_get_standalone_payment_method_state(); |
| 10159 |
|
| 10160 |
foreach ( $methods as $method ) { |
| 10161 |
if ( ! empty( $method['ready'] ) ) { |
| 10162 |
return true; |
| 10163 |
} |
| 10164 |
} |
| 10165 |
|
| 10166 |
return false; |
| 10167 |
} |
| 10168 |
|
| 10169 |
function ebook_store_setup_wizard_get_pdf_page_count( $pdf_path ) { |
| 10170 |
if ( ! class_exists( 'Imagick' ) || ! file_exists( $pdf_path ) ) { |
| 10171 |
return 0; |
| 10172 |
} |
| 10173 |
|
| 10174 |
try { |
| 10175 |
$imagick = new Imagick(); |
| 10176 |
$imagick->pingImage( $pdf_path ); |
| 10177 |
$page_count = (int) $imagick->getNumberImages(); |
| 10178 |
$imagick->clear(); |
| 10179 |
$imagick->destroy(); |
| 10180 |
|
| 10181 |
return max( 0, $page_count ); |
| 10182 |
} catch ( Exception $exception ) { |
| 10183 |
return 0; |
| 10184 |
} |
| 10185 |
} |
| 10186 |
|
| 10187 |
function ebook_store_setup_wizard_unescape_pdf_literal( $value ) { |
| 10188 |
$value = preg_replace_callback( |
| 10189 |
'/\\\\([0-7]{1,3})/', |
| 10190 |
function ( $matches ) { |
| 10191 |
return chr( octdec( $matches[1] ) ); |
| 10192 |
}, |
| 10193 |
(string) $value |
| 10194 |
); |
| 10195 |
|
| 10196 |
$replacements = array( |
| 10197 |
'\\n' => "\n", |
| 10198 |
'\\r' => "\r", |
| 10199 |
'\\t' => "\t", |
| 10200 |
'\\b' => "\b", |
| 10201 |
'\\f' => "\f", |
| 10202 |
'\\(' => '(', |
| 10203 |
'\\)' => ')', |
| 10204 |
'\\\\' => '\\', |
| 10205 |
); |
| 10206 |
|
| 10207 |
return strtr( $value, $replacements ); |
| 10208 |
} |
| 10209 |
|
| 10210 |
function ebook_store_setup_wizard_clean_pdf_text( $text ) { |
| 10211 |
$text = html_entity_decode( (string) $text, ENT_QUOTES, 'UTF-8' ); |
| 10212 |
$text = preg_replace( "/\r\n?/", "\n", $text ); |
| 10213 |
$text = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/u', ' ', $text ); |
| 10214 |
$text = preg_replace( '/[ \t]+/', ' ', $text ); |
| 10215 |
$text = preg_replace( "/\n{3,}/", "\n\n", $text ); |
| 10216 |
|
| 10217 |
$lines = preg_split( "/\n+/", trim( $text ) ); |
| 10218 |
$clean_lines = array(); |
| 10219 |
|
| 10220 |
foreach ( $lines as $line ) { |
| 10221 |
$line = trim( wp_strip_all_tags( $line ) ); |
| 10222 |
$line = preg_replace( '/\s+/', ' ', $line ); |
| 10223 |
|
| 10224 |
if ( strlen( $line ) < 3 ) { |
| 10225 |
continue; |
| 10226 |
} |
| 10227 |
|
| 10228 |
if ( preg_match( '/^[^A-Za-z\\p{L}\\p{N}]+$/u', $line ) ) { |
| 10229 |
continue; |
| 10230 |
} |
| 10231 |
|
| 10232 |
if ( in_array( $line, $clean_lines, true ) ) { |
| 10233 |
continue; |
| 10234 |
} |
| 10235 |
|
| 10236 |
$clean_lines[] = $line; |
| 10237 |
} |
| 10238 |
|
| 10239 |
return trim( implode( "\n", $clean_lines ) ); |
| 10240 |
} |
| 10241 |
|
| 10242 |
function ebook_store_setup_wizard_extract_pdf_text_via_ghostscript( $pdf_path ) { |
| 10243 |
if ( ! function_exists( 'shell_exec' ) || ! file_exists( $pdf_path ) ) { |
| 10244 |
return ''; |
| 10245 |
} |
| 10246 |
|
| 10247 |
$ghostscript_path = trim( (string) @shell_exec( 'command -v gs 2>/dev/null' ) ); |
| 10248 |
if ( $ghostscript_path === '' ) { |
| 10249 |
return ''; |
| 10250 |
} |
| 10251 |
|
| 10252 |
$command = escapeshellarg( $ghostscript_path ) . ' -q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=txtwrite -sOutputFile=- ' . escapeshellarg( $pdf_path ) . ' 2>/dev/null'; |
| 10253 |
$output = @shell_exec( $command ); |
| 10254 |
|
| 10255 |
return is_string( $output ) ? ebook_store_setup_wizard_clean_pdf_text( $output ) : ''; |
| 10256 |
} |
| 10257 |
|
| 10258 |
function ebook_store_setup_wizard_extract_pdf_text_via_streams( $pdf_path ) { |
| 10259 |
if ( ! file_exists( $pdf_path ) ) { |
| 10260 |
return ''; |
| 10261 |
} |
| 10262 |
|
| 10263 |
$pdf_contents = @file_get_contents( $pdf_path ); |
| 10264 |
if ( ! is_string( $pdf_contents ) || $pdf_contents === '' ) { |
| 10265 |
return ''; |
| 10266 |
} |
| 10267 |
|
| 10268 |
$matches = array(); |
| 10269 |
preg_match_all( '/stream[\r\n]+(.*?)endstream/s', $pdf_contents, $matches ); |
| 10270 |
if ( empty( $matches[1] ) ) { |
| 10271 |
return ''; |
| 10272 |
} |
| 10273 |
|
| 10274 |
$chunks = array(); |
| 10275 |
|
| 10276 |
foreach ( array_slice( $matches[1], 0, 12 ) as $stream ) { |
| 10277 |
$candidates = array( $stream ); |
| 10278 |
|
| 10279 |
if ( function_exists( 'gzuncompress' ) ) { |
| 10280 |
$decoded = @gzuncompress( $stream ); |
| 10281 |
if ( is_string( $decoded ) && $decoded !== '' ) { |
| 10282 |
$candidates[] = $decoded; |
| 10283 |
} |
| 10284 |
} |
| 10285 |
|
| 10286 |
if ( function_exists( 'gzinflate' ) ) { |
| 10287 |
$decoded = @gzinflate( $stream ); |
| 10288 |
if ( is_string( $decoded ) && $decoded !== '' ) { |
| 10289 |
$candidates[] = $decoded; |
| 10290 |
} |
| 10291 |
|
| 10292 |
$decoded = @gzinflate( substr( $stream, 2 ) ); |
| 10293 |
if ( is_string( $decoded ) && $decoded !== '' ) { |
| 10294 |
$candidates[] = $decoded; |
| 10295 |
} |
| 10296 |
} |
| 10297 |
|
| 10298 |
foreach ( $candidates as $candidate ) { |
| 10299 |
$text_matches = array(); |
| 10300 |
preg_match_all( '/\((?:\\\\.|[^\\\\)])*\)\s*Tj/s', $candidate, $text_matches ); |
| 10301 |
foreach ( $text_matches[0] as $text_match ) { |
| 10302 |
if ( preg_match( '/^\((.*)\)\s*Tj$/s', $text_match, $literal_match ) ) { |
| 10303 |
$chunks[] = ebook_store_setup_wizard_unescape_pdf_literal( $literal_match[1] ); |
| 10304 |
} |
| 10305 |
} |
| 10306 |
|
| 10307 |
$array_matches = array(); |
| 10308 |
preg_match_all( '/\[(.*?)\]\s*TJ/s', $candidate, $array_matches ); |
| 10309 |
foreach ( $array_matches[1] as $array_match ) { |
| 10310 |
$literal_matches = array(); |
| 10311 |
preg_match_all( '/\((?:\\\\.|[^\\\\)])*\)/s', $array_match, $literal_matches ); |
| 10312 |
foreach ( $literal_matches[0] as $literal ) { |
| 10313 |
$chunks[] = ebook_store_setup_wizard_unescape_pdf_literal( trim( $literal, '()' ) ); |
| 10314 |
} |
| 10315 |
} |
| 10316 |
} |
| 10317 |
} |
| 10318 |
|
| 10319 |
return ebook_store_setup_wizard_clean_pdf_text( implode( "\n", $chunks ) ); |
| 10320 |
} |
| 10321 |
|
| 10322 |
function ebook_store_setup_wizard_extract_pdf_details( $pdf_path, $fallback_title = '' ) { |
| 10323 |
$details = array( |
| 10324 |
'title' => '', |
| 10325 |
'description' => '', |
| 10326 |
'author' => '', |
| 10327 |
'pages' => ebook_store_setup_wizard_get_pdf_page_count( $pdf_path ), |
| 10328 |
); |
| 10329 |
|
| 10330 |
if ( ! file_exists( $pdf_path ) ) { |
| 10331 |
return $details; |
| 10332 |
} |
| 10333 |
|
| 10334 |
$pdf_contents = @file_get_contents( $pdf_path ); |
| 10335 |
if ( is_string( $pdf_contents ) && $pdf_contents !== '' ) { |
| 10336 |
if ( preg_match( '/\/Title\s*\((.*?)\)/s', $pdf_contents, $title_match ) ) { |
| 10337 |
$details['title'] = trim( ebook_store_setup_wizard_unescape_pdf_literal( $title_match[1] ) ); |
| 10338 |
} |
| 10339 |
|
| 10340 |
if ( preg_match( '/\/Subject\s*\((.*?)\)/s', $pdf_contents, $subject_match ) ) { |
| 10341 |
$details['description'] = trim( ebook_store_setup_wizard_unescape_pdf_literal( $subject_match[1] ) ); |
| 10342 |
} |
| 10343 |
|
| 10344 |
if ( preg_match( '/\/Author\s*\((.*?)\)/s', $pdf_contents, $author_match ) ) { |
| 10345 |
$details['author'] = trim( ebook_store_setup_wizard_unescape_pdf_literal( $author_match[1] ) ); |
| 10346 |
} |
| 10347 |
} |
| 10348 |
|
| 10349 |
$text = ebook_store_setup_wizard_extract_pdf_text_via_ghostscript( $pdf_path ); |
| 10350 |
if ( $text === '' ) { |
| 10351 |
$text = ebook_store_setup_wizard_extract_pdf_text_via_streams( $pdf_path ); |
| 10352 |
} |
| 10353 |
|
| 10354 |
$lines = $text !== '' ? array_values( array_filter( preg_split( "/\n+/", $text ) ) ) : array(); |
| 10355 |
if ( $details['title'] === '' && ! empty( $lines ) ) { |
| 10356 |
foreach ( $lines as $line ) { |
| 10357 |
$line = trim( $line ); |
| 10358 |
if ( strlen( $line ) >= 4 && strlen( $line ) <= 140 ) { |
| 10359 |
$details['title'] = $line; |
| 10360 |
break; |
| 10361 |
} |
| 10362 |
} |
| 10363 |
} |
| 10364 |
|
| 10365 |
if ( $details['description'] === '' && count( $lines ) > 1 ) { |
| 10366 |
$description_lines = array(); |
| 10367 |
foreach ( $lines as $line ) { |
| 10368 |
$line = trim( $line ); |
| 10369 |
if ( $line === '' ) { |
| 10370 |
continue; |
| 10371 |
} |
| 10372 |
if ( $details['title'] !== '' && strcasecmp( $line, $details['title'] ) === 0 ) { |
| 10373 |
continue; |
| 10374 |
} |
| 10375 |
$description_lines[] = $line; |
| 10376 |
if ( strlen( implode( ' ', $description_lines ) ) >= 280 ) { |
| 10377 |
break; |
| 10378 |
} |
| 10379 |
} |
| 10380 |
$details['description'] = trim( implode( ' ', array_slice( $description_lines, 0, 4 ) ) ); |
| 10381 |
} |
| 10382 |
|
| 10383 |
if ( $details['title'] === '' ) { |
| 10384 |
$details['title'] = $fallback_title; |
| 10385 |
} |
| 10386 |
|
| 10387 |
if ( $details['description'] !== '' ) { |
| 10388 |
$details['description'] = wp_trim_words( $details['description'], 48, '...' ); |
| 10389 |
} |
| 10390 |
|
| 10391 |
return $details; |
| 10392 |
} |
| 10393 |
|
| 10394 |
function ebook_store_setup_wizard_upload_pdf( $ebook_id, $file ) { |
| 10395 |
if ( empty( $file['tmp_name'] ) || ! file_exists( $file['tmp_name'] ) ) { |
| 10396 |
return new WP_Error( 'ebook_store_wizard_upload_missing', __( 'The uploaded PDF file could not be read.', 'ebook-store' ) ); |
| 10397 |
} |
| 10398 |
|
| 10399 |
// The wizard accepted whatever it was handed. Run the same content check the |
| 10400 |
// rest of the plugin uses, so a renamed file cannot be parked here. |
| 10401 |
if ( function_exists( 'ebook_store_validate_uploaded_ebook' ) ) { |
| 10402 |
$validated = ebook_store_validate_uploaded_ebook( $file['tmp_name'], 'pdf' ); |
| 10403 |
|
| 10404 |
if ( is_wp_error( $validated ) ) { |
| 10405 |
return $validated; |
| 10406 |
} |
| 10407 |
} |
| 10408 |
|
| 10409 |
// This used to be wp_upload_bits( ..., file_get_contents( $tmp ) ), which |
| 10410 |
// pulled the whole book into memory — a 60 MB PDF blew memory_limit before it |
| 10411 |
// ever reached disk. Moving the temp file is constant-memory. |
| 10412 |
if ( function_exists( 'ebook_store_store_uploaded_ebook' ) ) { |
| 10413 |
$upload = ebook_store_store_uploaded_ebook( $file['tmp_name'], $file['name'] ); |
| 10414 |
} else { |
| 10415 |
add_filter( 'upload_dir', 'ebook_set_upload_dir' ); |
| 10416 |
$upload = wp_upload_bits( $file['name'], null, file_get_contents( $file['tmp_name'] ) ); |
| 10417 |
remove_filter( 'upload_dir', 'ebook_set_upload_dir' ); |
| 10418 |
} |
| 10419 |
|
| 10420 |
if ( is_wp_error( $upload ) ) { |
| 10421 |
return $upload; |
| 10422 |
} |
| 10423 |
|
| 10424 |
if ( isset( $upload['error'] ) && $upload['error'] ) { |
| 10425 |
return new WP_Error( 'ebook_store_wizard_upload_failed', $upload['error'] ); |
| 10426 |
} |
| 10427 |
|
| 10428 |
update_post_meta( $ebook_id, 'ebook_wp_custom_attachment', wp_slash( $upload ) ); |
| 10429 |
update_post_meta( $ebook_id, 'ebook_wp_custom_attachment_pdf', wp_slash( $upload ) ); |
| 10430 |
ebook_store_generate_cover_from_pdf( $ebook_id ); |
| 10431 |
|
| 10432 |
return $upload; |
| 10433 |
} |
| 10434 |
|
| 10435 |
function ebook_store_setup_wizard_create_ebook( $args ) { |
| 10436 |
$title = isset( $args['title'] ) ? sanitize_text_field( $args['title'] ) : ''; |
| 10437 |
$description = isset( $args['description'] ) ? wp_kses_post( $args['description'] ) : ''; |
| 10438 |
$price = isset( $args['price'] ) ? (float) $args['price'] : 0.0; |
| 10439 |
$currency = isset( $args['currency'] ) && $args['currency'] !== '' ? sanitize_text_field( $args['currency'] ) : ebook_store_get_store_currency(); |
| 10440 |
$pdf_file = isset( $args['pdf_file'] ) ? $args['pdf_file'] : array(); |
| 10441 |
$pdf_pages = isset( $args['pages'] ) ? (int) $args['pages'] : 0; |
| 10442 |
$author = isset( $args['author'] ) ? sanitize_text_field( $args['author'] ) : ''; |
| 10443 |
$template = function_exists( 'ebook_store_resolve_form_template' ) ? ebook_store_resolve_form_template( '' ) : 'modern'; |
| 10444 |
$delivery_mode = $price > 0 ? 'paid' : 'free'; |
| 10445 |
|
| 10446 |
if ( $title === '' ) { |
| 10447 |
$title = __( 'Imported ebook', 'ebook-store' ); |
| 10448 |
} |
| 10449 |
|
| 10450 |
$ebook_post = array( |
| 10451 |
'post_title' => $title, |
| 10452 |
'post_content' => $description, |
| 10453 |
'post_excerpt' => wp_trim_words( wp_strip_all_tags( $description ), 40, '...' ), |
| 10454 |
'post_status' => 'draft', |
| 10455 |
'post_type' => 'ebook', |
| 10456 |
); |
| 10457 |
|
| 10458 |
$ebook_id = wp_insert_post( $ebook_post, true ); |
| 10459 |
if ( is_wp_error( $ebook_id ) ) { |
| 10460 |
return $ebook_id; |
| 10461 |
} |
| 10462 |
|
| 10463 |
$ebook_meta = array( |
| 10464 |
'ebook_price' => number_format( $price, 2, '.', '' ), |
| 10465 |
'paypal_currency' => $currency, |
| 10466 |
'donate_or_download' => $delivery_mode, |
| 10467 |
'form_template' => $template, |
| 10468 |
); |
| 10469 |
|
| 10470 |
if ( $pdf_pages > 0 ) { |
| 10471 |
$ebook_meta['ebook_pages'] = $pdf_pages; |
| 10472 |
} |
| 10473 |
|
| 10474 |
update_post_meta( $ebook_id, 'ebook', $ebook_meta ); |
| 10475 |
update_post_meta( $ebook_id, 'ebook_bonus', array() ); |
| 10476 |
|
| 10477 |
if ( $author !== '' ) { |
| 10478 |
update_post_meta( $ebook_id, 'ebook_store_wizard_author_name', $author ); |
| 10479 |
} |
| 10480 |
|
| 10481 |
$upload = ebook_store_setup_wizard_upload_pdf( $ebook_id, $pdf_file ); |
| 10482 |
if ( is_wp_error( $upload ) ) { |
| 10483 |
wp_delete_post( $ebook_id, true ); |
| 10484 |
return $upload; |
| 10485 |
} |
| 10486 |
|
| 10487 |
return array( |
| 10488 |
'ebook_id' => $ebook_id, |
| 10489 |
'title' => $title, |
| 10490 |
'description' => $description, |
| 10491 |
'pages' => $pdf_pages, |
| 10492 |
); |
| 10493 |
} |
| 10494 |
|
| 10495 |
function ebook_store_setup_wizard_create_woocommerce_bundle( $args ) { |
| 10496 |
if ( ! class_exists( 'WooCommerce' ) ) { |
| 10497 |
return new WP_Error( 'ebook_store_wizard_woocommerce_missing', __( 'WooCommerce is not active. Activate WooCommerce first, then run this setup path again.', 'ebook-store' ) ); |
| 10498 |
} |
| 10499 |
|
| 10500 |
$ebook_result = ebook_store_setup_wizard_create_ebook( $args ); |
| 10501 |
if ( is_wp_error( $ebook_result ) ) { |
| 10502 |
return $ebook_result; |
| 10503 |
} |
| 10504 |
|
| 10505 |
$product_name = isset( $args['product_name'] ) ? sanitize_text_field( $args['product_name'] ) : $ebook_result['title']; |
| 10506 |
$product_description = isset( $args['description'] ) ? wp_kses_post( $args['description'] ) : $ebook_result['description']; |
| 10507 |
$price = isset( $args['price'] ) ? (float) $args['price'] : 0.0; |
| 10508 |
|
| 10509 |
$product_post = array( |
| 10510 |
'post_title' => $product_name, |
| 10511 |
'post_content' => $product_description, |
| 10512 |
'post_excerpt' => wp_trim_words( wp_strip_all_tags( $product_description ), 40, '...' ), |
| 10513 |
'post_status' => 'draft', |
| 10514 |
'post_type' => 'product', |
| 10515 |
); |
| 10516 |
|
| 10517 |
$product_id = wp_insert_post( $product_post, true ); |
| 10518 |
if ( is_wp_error( $product_id ) ) { |
| 10519 |
wp_delete_post( $ebook_result['ebook_id'], true ); |
| 10520 |
return $product_id; |
| 10521 |
} |
| 10522 |
|
| 10523 |
wp_set_object_terms( $product_id, 'ebookstore', 'product_type' ); |
| 10524 |
update_post_meta( $product_id, '_regular_price', number_format( $price, 2, '.', '' ) ); |
| 10525 |
update_post_meta( $product_id, '_price', number_format( $price, 2, '.', '' ) ); |
| 10526 |
update_post_meta( $product_id, '_virtual', 'yes' ); |
| 10527 |
update_post_meta( $product_id, '_downloadable', 'yes' ); |
| 10528 |
update_post_meta( $product_id, '_enable_ebook_store_option', 'yes' ); |
| 10529 |
update_post_meta( $product_id, 'ebook_store_book_id', $ebook_result['ebook_id'] ); |
| 10530 |
update_post_meta( $ebook_result['ebook_id'], 'woocommerce_product_id', $product_id ); |
| 10531 |
|
| 10532 |
$license_key = trim( (string) get_option( 'ebook_store_license_key', '' ) ); |
| 10533 |
$integration_enabled = (string) get_option( 'ebook_store_woocommerce_integration', '' ) === '1'; |
| 10534 |
$integration_auto_enabled = false; |
| 10535 |
|
| 10536 |
if ( ! $integration_enabled && $license_key !== '' ) { |
| 10537 |
update_option( 'ebook_store_woocommerce_integration', '1' ); |
| 10538 |
$integration_enabled = true; |
| 10539 |
$integration_auto_enabled = true; |
| 10540 |
} |
| 10541 |
|
| 10542 |
if ( function_exists( 'wc_delete_product_transients' ) ) { |
| 10543 |
wc_delete_product_transients( $product_id ); |
| 10544 |
} |
| 10545 |
|
| 10546 |
return array( |
| 10547 |
'ebook_id' => $ebook_result['ebook_id'], |
| 10548 |
'product_id' => $product_id, |
| 10549 |
'integration_enabled' => $integration_enabled, |
| 10550 |
'integration_auto_enabled' => $integration_auto_enabled, |
| 10551 |
); |
| 10552 |
} |
| 10553 |
|
| 10554 |
function ebook_store_setup_wizard_handle_submission() { |
| 10555 |
if ( empty( $_POST['ebook_store_setup_wizard_action'] ) ) { |
| 10556 |
return null; |
| 10557 |
} |
| 10558 |
|
| 10559 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 10560 |
return array( |
| 10561 |
'errors' => array( __( 'You do not have permission to run the setup wizard.', 'ebook-store' ) ), |
| 10562 |
); |
| 10563 |
} |
| 10564 |
|
| 10565 |
check_admin_referer( 'ebook_store_setup_wizard' ); |
| 10566 |
|
| 10567 |
$mode = sanitize_key( (string) wp_unslash( $_POST['wizard_mode'] ) ); |
| 10568 |
$price = isset( $_POST['wizard_price'] ) ? (float) wp_unslash( $_POST['wizard_price'] ) : 0.0; |
| 10569 |
$product_name = isset( $_POST['wizard_product_name'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_product_name'] ) ) : ''; |
| 10570 |
$wizard_enable_paypal = isset( $_POST['wizard_enable_paypal'] ) && (string) wp_unslash( $_POST['wizard_enable_paypal'] ) === '1' ? '1' : ''; |
| 10571 |
$wizard_enable_stripe = isset( $_POST['wizard_enable_stripe'] ) && (string) wp_unslash( $_POST['wizard_enable_stripe'] ) === '1' ? '1' : ''; |
| 10572 |
$wizard_enable_adyen = isset( $_POST['wizard_enable_adyen'] ) && (string) wp_unslash( $_POST['wizard_enable_adyen'] ) === '1' ? '1' : ''; |
| 10573 |
$wizard_paypal_account = isset( $_POST['wizard_paypal_account'] ) ? sanitize_email( wp_unslash( $_POST['wizard_paypal_account'] ) ) : ''; |
| 10574 |
$wizard_stripe_publishable_key = isset( $_POST['wizard_stripe_publishable_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_stripe_publishable_key'] ) ) : ''; |
| 10575 |
$wizard_stripe_secret_key = isset( $_POST['wizard_stripe_secret_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_stripe_secret_key'] ) ) : ''; |
| 10576 |
$wizard_adyen_environment = isset( $_POST['wizard_adyen_environment'] ) ? sanitize_key( (string) wp_unslash( $_POST['wizard_adyen_environment'] ) ) : 'test'; |
| 10577 |
$wizard_adyen_api_key = isset( $_POST['wizard_adyen_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_api_key'] ) ) : ''; |
| 10578 |
$wizard_adyen_merchant_account = isset( $_POST['wizard_adyen_merchant_account'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_merchant_account'] ) ) : ''; |
| 10579 |
$wizard_adyen_hmac_key = isset( $_POST['wizard_adyen_hmac_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_hmac_key'] ) ) : ''; |
| 10580 |
$wizard_adyen_live_prefix = isset( $_POST['wizard_adyen_live_prefix'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_live_prefix'] ) ) : ''; |
| 10581 |
$pdf_file = isset( $_FILES['wizard_pdf_file'] ) ? $_FILES['wizard_pdf_file'] : array(); |
| 10582 |
$errors = array(); |
| 10583 |
|
| 10584 |
if ( ! in_array( $mode, array( 'standalone', 'woocommerce' ), true ) ) { |
| 10585 |
$errors[] = __( 'Please choose how you want to set up Ebook Store first.', 'ebook-store' ); |
| 10586 |
} |
| 10587 |
|
| 10588 |
if ( empty( $pdf_file['tmp_name'] ) ) { |
| 10589 |
$errors[] = __( 'Please upload the PDF file you want to sell.', 'ebook-store' ); |
| 10590 |
} elseif ( ! ebook_store_setup_wizard_is_pdf( $pdf_file ) ) { |
| 10591 |
$errors[] = __( 'The uploaded file must be a PDF.', 'ebook-store' ); |
| 10592 |
} |
| 10593 |
|
| 10594 |
if ( $mode === 'woocommerce' && $product_name === '' ) { |
| 10595 |
$errors[] = __( 'Please enter the WooCommerce product name.', 'ebook-store' ); |
| 10596 |
} |
| 10597 |
|
| 10598 |
if ( $mode === 'standalone' ) { |
| 10599 |
$wizard_ready_payment_methods = 0; |
| 10600 |
|
| 10601 |
if ( $wizard_enable_paypal === '1' ) { |
| 10602 |
if ( $wizard_paypal_account === '' ) { |
| 10603 |
$errors[] = __( 'Enter the PayPal account email or turn off PayPal in Payment method setup.', 'ebook-store' ); |
| 10604 |
} else { |
| 10605 |
$wizard_ready_payment_methods++; |
| 10606 |
} |
| 10607 |
} |
| 10608 |
|
| 10609 |
if ( $wizard_enable_stripe === '1' ) { |
| 10610 |
if ( $wizard_stripe_publishable_key === '' || $wizard_stripe_secret_key === '' ) { |
| 10611 |
$errors[] = __( 'Enter both Stripe API keys or turn off Stripe in Payment method setup.', 'ebook-store' ); |
| 10612 |
} else { |
| 10613 |
$wizard_ready_payment_methods++; |
| 10614 |
} |
| 10615 |
} |
| 10616 |
|
| 10617 |
if ( $wizard_enable_adyen === '1' ) { |
| 10618 |
if ( ! in_array( $wizard_adyen_environment, array( 'test', 'live' ), true ) ) { |
| 10619 |
$wizard_adyen_environment = 'test'; |
| 10620 |
} |
| 10621 |
if ( $wizard_adyen_api_key === '' || $wizard_adyen_merchant_account === '' || $wizard_adyen_hmac_key === '' ) { |
| 10622 |
$errors[] = __( 'Enter the Adyen API key, merchant account, and webhook HMAC key or turn off Adyen in Payment method setup.', 'ebook-store' ); |
| 10623 |
} elseif ( $wizard_adyen_environment === 'live' && $wizard_adyen_live_prefix === '' ) { |
| 10624 |
$errors[] = __( 'Enter the Adyen live URL prefix or switch Adyen to Test mode in Payment method setup.', 'ebook-store' ); |
| 10625 |
} else { |
| 10626 |
$wizard_ready_payment_methods++; |
| 10627 |
} |
| 10628 |
} |
| 10629 |
|
| 10630 |
if ( $price > 0 && $wizard_ready_payment_methods < 1 ) { |
| 10631 |
$errors[] = __( 'Standalone paid ebooks need at least one checkout method that is ready to use. Enable a payment method below and complete its required details before creating the draft.', 'ebook-store' ); |
| 10632 |
} |
| 10633 |
} |
| 10634 |
|
| 10635 |
if ( ! empty( $errors ) ) { |
| 10636 |
return array( |
| 10637 |
'errors' => $errors, |
| 10638 |
'mode' => $mode, |
| 10639 |
); |
| 10640 |
} |
| 10641 |
|
| 10642 |
if ( $mode === 'standalone' ) { |
| 10643 |
update_option( 'paypal_integration_enabled', $wizard_enable_paypal ); |
| 10644 |
update_option( 'paypal_account', $wizard_paypal_account ); |
| 10645 |
update_option( 'stripe_integration_enabled', $wizard_enable_stripe ); |
| 10646 |
update_option( 'stripe_publishable_key', $wizard_stripe_publishable_key ); |
| 10647 |
update_option( 'stripe_secret_key', $wizard_stripe_secret_key ); |
| 10648 |
update_option( 'adyen_integration_enabled', $wizard_enable_adyen ); |
| 10649 |
update_option( 'adyen_environment', in_array( $wizard_adyen_environment, array( 'test', 'live' ), true ) ? $wizard_adyen_environment : 'test' ); |
| 10650 |
update_option( 'adyen_api_key', $wizard_adyen_api_key ); |
| 10651 |
update_option( 'adyen_merchant_account', $wizard_adyen_merchant_account ); |
| 10652 |
update_option( 'adyen_hmac_key', $wizard_adyen_hmac_key ); |
| 10653 |
update_option( 'adyen_live_prefix', $wizard_adyen_live_prefix ); |
| 10654 |
} |
| 10655 |
|
| 10656 |
$fallback_title = $mode === 'woocommerce' ? $product_name : preg_replace( '/\.[^.]+$/', '', sanitize_file_name( $pdf_file['name'] ) ); |
| 10657 |
$pdf_details = ebook_store_setup_wizard_extract_pdf_details( $pdf_file['tmp_name'], $fallback_title ); |
| 10658 |
$title = $mode === 'woocommerce' ? $product_name : $pdf_details['title']; |
| 10659 |
$description = $pdf_details['description']; |
| 10660 |
|
| 10661 |
if ( $title === '' ) { |
| 10662 |
$title = $fallback_title !== '' ? $fallback_title : __( 'Imported ebook', 'ebook-store' ); |
| 10663 |
} |
| 10664 |
|
| 10665 |
$shared_args = array( |
| 10666 |
'title' => $title, |
| 10667 |
'product_name'=> $product_name, |
| 10668 |
'description' => $description, |
| 10669 |
'price' => $price, |
| 10670 |
'currency' => ebook_store_get_store_currency(), |
| 10671 |
'pdf_file' => $pdf_file, |
| 10672 |
'pages' => isset( $pdf_details['pages'] ) ? (int) $pdf_details['pages'] : 0, |
| 10673 |
'author' => isset( $pdf_details['author'] ) ? $pdf_details['author'] : '', |
| 10674 |
); |
| 10675 |
|
| 10676 |
$result = $mode === 'woocommerce' |
| 10677 |
? ebook_store_setup_wizard_create_woocommerce_bundle( $shared_args ) |
| 10678 |
: ebook_store_setup_wizard_create_ebook( $shared_args ); |
| 10679 |
|
| 10680 |
if ( is_wp_error( $result ) ) { |
| 10681 |
return array( |
| 10682 |
'errors' => array( $result->get_error_message() ), |
| 10683 |
'mode' => $mode, |
| 10684 |
); |
| 10685 |
} |
| 10686 |
|
| 10687 |
$redirect_args = array( |
| 10688 |
'wizard_mode' => $mode, |
| 10689 |
'wizard_created' => 1, |
| 10690 |
'ebook_id' => (int) $result['ebook_id'], |
| 10691 |
); |
| 10692 |
|
| 10693 |
if ( ! empty( $result['product_id'] ) ) { |
| 10694 |
$redirect_args['product_id'] = (int) $result['product_id']; |
| 10695 |
} |
| 10696 |
|
| 10697 |
if ( isset( $result['integration_enabled'] ) ) { |
| 10698 |
$redirect_args['integration_enabled'] = $result['integration_enabled'] ? 1 : 0; |
| 10699 |
} |
| 10700 |
|
| 10701 |
if ( isset( $result['integration_auto_enabled'] ) ) { |
| 10702 |
$redirect_args['integration_auto_enabled'] = $result['integration_auto_enabled'] ? 1 : 0; |
| 10703 |
} |
| 10704 |
|
| 10705 |
wp_safe_redirect( ebook_store_get_setup_wizard_url( $redirect_args ) ); |
| 10706 |
exit; |
| 10707 |
} |
| 10708 |
|
| 10709 |
function ebook_store_setup_wizard_page_callback() { |
| 10710 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 10711 |
$locale = ebook_store_get_locale_strings(); |
| 10712 |
$formats_locale = ebook_store_get_format_labels(); |
| 10713 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 10714 |
|
| 10715 |
$submission = null; |
| 10716 |
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['ebook_store_setup_wizard_action'] ) ) { |
| 10717 |
$submission = ebook_store_setup_wizard_handle_submission(); |
| 10718 |
} |
| 10719 |
|
| 10720 |
$wizard_mode = isset( $_REQUEST['wizard_mode'] ) ? sanitize_key( (string) wp_unslash( $_REQUEST['wizard_mode'] ) ) : ''; |
| 10721 |
if ( $submission && ! empty( $submission['mode'] ) ) { |
| 10722 |
$wizard_mode = $submission['mode']; |
| 10723 |
} |
| 10724 |
|
| 10725 |
$errors = $submission && ! empty( $submission['errors'] ) ? $submission['errors'] : array(); |
| 10726 |
$wizard_created = isset( $_GET['wizard_created'] ) && (int) $_GET['wizard_created'] === 1; |
| 10727 |
$ebook_id = isset( $_GET['ebook_id'] ) ? (int) $_GET['ebook_id'] : 0; |
| 10728 |
$product_id = isset( $_GET['product_id'] ) ? (int) $_GET['product_id'] : 0; |
| 10729 |
$integration_enabled = isset( $_GET['integration_enabled'] ) ? (int) $_GET['integration_enabled'] === 1 : null; |
| 10730 |
$integration_auto_enabled = isset( $_GET['integration_auto_enabled'] ) ? (int) $_GET['integration_auto_enabled'] === 1 : false; |
| 10731 |
$current_currency = ebook_store_get_store_currency(); |
| 10732 |
$current_price = isset( $_POST['wizard_price'] ) ? (string) wp_unslash( $_POST['wizard_price'] ) : '5.00'; |
| 10733 |
$current_product_name = isset( $_POST['wizard_product_name'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_product_name'] ) ) : ''; |
| 10734 |
$payment_method_state = ebook_store_get_standalone_payment_method_state(); |
| 10735 |
$has_ready_standalone_payment = ebook_store_has_ready_standalone_payment_method(); |
| 10736 |
$current_wizard_paypal_enabled = isset( $_POST['wizard_enable_paypal'] ) ? ( (string) wp_unslash( $_POST['wizard_enable_paypal'] ) === '1' ? '1' : '' ) : ( ! empty( $payment_method_state['paypal']['enabled'] ) ? '1' : '' ); |
| 10737 |
$current_wizard_stripe_enabled = isset( $_POST['wizard_enable_stripe'] ) ? ( (string) wp_unslash( $_POST['wizard_enable_stripe'] ) === '1' ? '1' : '' ) : ( ! empty( $payment_method_state['stripe']['enabled'] ) ? '1' : '' ); |
| 10738 |
$current_wizard_adyen_enabled = isset( $_POST['wizard_enable_adyen'] ) ? ( (string) wp_unslash( $_POST['wizard_enable_adyen'] ) === '1' ? '1' : '' ) : ( ! empty( $payment_method_state['adyen']['enabled'] ) ? '1' : '' ); |
| 10739 |
$current_wizard_paypal_account = isset( $_POST['wizard_paypal_account'] ) ? sanitize_email( wp_unslash( $_POST['wizard_paypal_account'] ) ) : (string) get_option( 'paypal_account', '' ); |
| 10740 |
$current_wizard_stripe_publishable_key = isset( $_POST['wizard_stripe_publishable_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_stripe_publishable_key'] ) ) : (string) get_option( 'stripe_publishable_key', '' ); |
| 10741 |
$current_wizard_stripe_secret_key = isset( $_POST['wizard_stripe_secret_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_stripe_secret_key'] ) ) : (string) get_option( 'stripe_secret_key', '' ); |
| 10742 |
$current_wizard_adyen_environment = isset( $_POST['wizard_adyen_environment'] ) ? sanitize_key( (string) wp_unslash( $_POST['wizard_adyen_environment'] ) ) : (string) get_option( 'adyen_environment', 'test' ); |
| 10743 |
$current_wizard_adyen_api_key = isset( $_POST['wizard_adyen_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_api_key'] ) ) : (string) get_option( 'adyen_api_key', '' ); |
| 10744 |
$current_wizard_adyen_merchant_account = isset( $_POST['wizard_adyen_merchant_account'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_merchant_account'] ) ) : (string) get_option( 'adyen_merchant_account', '' ); |
| 10745 |
$current_wizard_adyen_hmac_key = isset( $_POST['wizard_adyen_hmac_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_hmac_key'] ) ) : (string) get_option( 'adyen_hmac_key', '' ); |
| 10746 |
$current_wizard_adyen_live_prefix = isset( $_POST['wizard_adyen_live_prefix'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_adyen_live_prefix'] ) ) : (string) get_option( 'adyen_live_prefix', '' ); |
| 10747 |
|
| 10748 |
ob_start(); |
| 10749 |
?> |
| 10750 |
<div class="wrap ebook-setup-wizard"> |
| 10751 |
<div class="ebook-setup-wizard__hero"> |
| 10752 |
<div class="ebook-setup-wizard__hero-copy"> |
| 10753 |
<span class="ebook-setup-wizard__eyebrow"><?php esc_html_e( 'New onboarding flow', 'ebook-store' ); ?></span> |
| 10754 |
<h1><?php esc_html_e( 'Setup wizard', 'ebook-store' ); ?></h1> |
| 10755 |
<p><?php esc_html_e( 'Start with the selling model that matches your store. The wizard can create a ready-to-review ebook draft from your PDF, generate the cover from page one, and optionally set up the linked WooCommerce product for you.', 'ebook-store' ); ?></p> |
| 10756 |
</div> |
| 10757 |
<div class="ebook-setup-wizard__hero-meta"> |
| 10758 |
<span class="ebook-setup-wizard__pill"><?php esc_html_e( 'Creates draft items', 'ebook-store' ); ?></span> |
| 10759 |
<span class="ebook-setup-wizard__pill"><?php esc_html_e( 'Extracts title and summary', 'ebook-store' ); ?></span> |
| 10760 |
<span class="ebook-setup-wizard__pill"><?php esc_html_e( 'Builds first-page cover', 'ebook-store' ); ?></span> |
| 10761 |
</div> |
| 10762 |
</div> |
| 10763 |
|
| 10764 |
<?php if ( ! empty( $errors ) ) : ?> |
| 10765 |
<div class="notice notice-error"> |
| 10766 |
<p><strong><?php esc_html_e( 'The wizard could not finish yet.', 'ebook-store' ); ?></strong></p> |
| 10767 |
<ul class="ebook-setup-wizard__notice-list"> |
| 10768 |
<?php foreach ( $errors as $error ) : ?> |
| 10769 |
<li><?php echo esc_html( $error ); ?></li> |
| 10770 |
<?php endforeach; ?> |
| 10771 |
</ul> |
| 10772 |
</div> |
| 10773 |
<?php endif; ?> |
| 10774 |
|
| 10775 |
<?php if ( $wizard_created && $ebook_id > 0 ) : ?> |
| 10776 |
<div class="notice notice-success is-dismissible"> |
| 10777 |
<p> |
| 10778 |
<strong><?php esc_html_e( 'Setup complete.', 'ebook-store' ); ?></strong> |
| 10779 |
<?php |
| 10780 |
printf( |
| 10781 |
esc_html__( '%1$s was created as a draft so you can review it before going live.', 'ebook-store' ), |
| 10782 |
esc_html( ebook_store_setup_wizard_human_mode_label( $wizard_mode ) ) |
| 10783 |
); |
| 10784 |
?> |
| 10785 |
</p> |
| 10786 |
<p class="ebook-setup-wizard__success-links"> |
| 10787 |
<a class="button button-primary" href="<?php echo esc_url( get_edit_post_link( $ebook_id, 'raw' ) ); ?>"><?php esc_html_e( 'Review ebook', 'ebook-store' ); ?></a> |
| 10788 |
<?php if ( $product_id > 0 ) : ?> |
| 10789 |
<a class="button button-secondary" href="<?php echo esc_url( get_edit_post_link( $product_id, 'raw' ) ); ?>"><?php esc_html_e( 'Review WooCommerce product', 'ebook-store' ); ?></a> |
| 10790 |
<?php endif; ?> |
| 10791 |
<a class="button button-secondary" href="<?php echo esc_url( ebook_store_get_setup_wizard_url() ); ?>"><?php esc_html_e( 'Run wizard again', 'ebook-store' ); ?></a> |
| 10792 |
</p> |
| 10793 |
<?php if ( $product_id > 0 && $integration_enabled === false ) : ?> |
| 10794 |
<p class="description"><?php esc_html_e( 'The linked product and ebook were created, but WooCommerce integration is still off. Enable it in Ebook Store > Settings > WooCommerce before sending traffic to this flow.', 'ebook-store' ); ?></p> |
| 10795 |
<?php elseif ( $product_id > 0 && $integration_auto_enabled ) : ?> |
| 10796 |
<p class="description"><?php esc_html_e( 'WooCommerce integration was enabled automatically so the linked product can use Ebook Store delivery.', 'ebook-store' ); ?></p> |
| 10797 |
<?php endif; ?> |
| 10798 |
</div> |
| 10799 |
<?php endif; ?> |
| 10800 |
|
| 10801 |
<?php if ( $wizard_mode === 'standalone' ) : ?> |
| 10802 |
<div class="ebook-setup-wizard__panel"> |
| 10803 |
<div class="ebook-setup-wizard__panel-header"> |
| 10804 |
<div> |
| 10805 |
<h2><?php esc_html_e( 'Standalone ebook sales', 'ebook-store' ); ?></h2> |
| 10806 |
<p><?php esc_html_e( 'Upload the PDF you want to sell. The wizard will extract the title and a summary, build the cover from the first page, and create a draft ebook item with your default modern template.', 'ebook-store' ); ?></p> |
| 10807 |
</div> |
| 10808 |
<a class="button button-secondary" href="<?php echo esc_url( ebook_store_get_setup_wizard_url() ); ?>"><?php esc_html_e( 'Back', 'ebook-store' ); ?></a> |
| 10809 |
</div> |
| 10810 |
<form method="post" enctype="multipart/form-data" class="ebook-setup-wizard__form"> |
| 10811 |
<?php wp_nonce_field( 'ebook_store_setup_wizard' ); ?> |
| 10812 |
<input type="hidden" name="ebook_store_setup_wizard_action" value="1" /> |
| 10813 |
<input type="hidden" name="wizard_mode" value="standalone" /> |
| 10814 |
<div class="ebook-setup-wizard__grid"> |
| 10815 |
<div class="ebook-setup-wizard__field"> |
| 10816 |
<label for="wizard_pdf_file"><?php esc_html_e( 'PDF file', 'ebook-store' ); ?></label> |
| 10817 |
<input type="file" id="wizard_pdf_file" name="wizard_pdf_file" accept="application/pdf,.pdf" required /> |
| 10818 |
<p class="description"><?php esc_html_e( 'The wizard reads the PDF title and summary and turns page one into the cover automatically.', 'ebook-store' ); ?></p> |
| 10819 |
</div> |
| 10820 |
<div class="ebook-setup-wizard__field"> |
| 10821 |
<label for="wizard_price"><?php esc_html_e( 'Selling price', 'ebook-store' ); ?></label> |
| 10822 |
<div class="ebook-setup-wizard__input-group"> |
| 10823 |
<span><?php echo esc_html( $current_currency ); ?></span> |
| 10824 |
<input type="number" id="wizard_price" name="wizard_price" min="0" step="0.01" value="<?php echo esc_attr( $current_price ); ?>" required /> |
| 10825 |
</div> |
| 10826 |
<p class="description"><?php esc_html_e( 'Set this to 0 if you want the wizard to create the ebook as a free download draft.', 'ebook-store' ); ?></p> |
| 10827 |
</div> |
| 10828 |
</div> |
| 10829 |
<div class="ebook-setup-wizard__gateway-panel"> |
| 10830 |
<div class="ebook-setup-wizard__gateway-header"> |
| 10831 |
<h3><?php esc_html_e( 'Payment method setup', 'ebook-store' ); ?></h3> |
| 10832 |
<p><?php esc_html_e( 'Choose the checkout methods you want to offer on standalone ebook pages, then enter the credentials each enabled gateway needs so payments can work immediately after launch.', 'ebook-store' ); ?></p> |
| 10833 |
<?php if ( ! $has_ready_standalone_payment && (float) $current_price > 0 ) : ?> |
| 10834 |
<p class="ebook-setup-wizard__gateway-note"><?php esc_html_e( 'This store does not have a ready standalone payment method yet. Turn on at least one option below and complete its required details before creating a paid ebook draft.', 'ebook-store' ); ?></p> |
| 10835 |
<?php endif; ?> |
| 10836 |
</div> |
| 10837 |
<div class="ebook-settings-toggle-grid ebook-settings-toggle-grid--wizard"> |
| 10838 |
<label class="ebook-settings-toggle-card"> |
| 10839 |
<input type="hidden" name="wizard_enable_paypal" value="" /> |
| 10840 |
<input type="checkbox" name="wizard_enable_paypal" value="1" <?php checked( $current_wizard_paypal_enabled, '1' ); ?> /> |
| 10841 |
<span class="ebook-settings-toggle-card__control" aria-hidden="true"></span> |
| 10842 |
<span class="ebook-settings-toggle-card__content"> |
| 10843 |
<span class="ebook-settings-toggle-card__title"><?php esc_html_e( 'Enable PayPal', 'ebook-store' ); ?></span> |
| 10844 |
<span class="ebook-settings-toggle-card__description"><?php esc_html_e( 'Fastest setup path for standalone Ebook Store checkout. Add the receiving PayPal account email below.', 'ebook-store' ); ?></span> |
| 10845 |
</span> |
| 10846 |
</label> |
| 10847 |
<label class="ebook-settings-toggle-card"> |
| 10848 |
<input type="hidden" name="wizard_enable_stripe" value="" /> |
| 10849 |
<input type="checkbox" name="wizard_enable_stripe" value="1" <?php checked( $current_wizard_stripe_enabled, '1' ); ?> /> |
| 10850 |
<span class="ebook-settings-toggle-card__control" aria-hidden="true"></span> |
| 10851 |
<span class="ebook-settings-toggle-card__content"> |
| 10852 |
<span class="ebook-settings-toggle-card__title"><?php esc_html_e( 'Enable Stripe', 'ebook-store' ); ?></span> |
| 10853 |
<span class="ebook-settings-toggle-card__description"><?php esc_html_e( 'Accept card payments with Stripe. Add both the publishable key and the secret key below.', 'ebook-store' ); ?></span> |
| 10854 |
</span> |
| 10855 |
</label> |
| 10856 |
<label class="ebook-settings-toggle-card"> |
| 10857 |
<input type="hidden" name="wizard_enable_adyen" value="" /> |
| 10858 |
<input type="checkbox" name="wizard_enable_adyen" value="1" <?php checked( $current_wizard_adyen_enabled, '1' ); ?> /> |
| 10859 |
<span class="ebook-settings-toggle-card__control" aria-hidden="true"></span> |
| 10860 |
<span class="ebook-settings-toggle-card__content"> |
| 10861 |
<span class="ebook-settings-toggle-card__title"><?php esc_html_e( 'Enable Adyen', 'ebook-store' ); ?></span> |
| 10862 |
<span class="ebook-settings-toggle-card__description"><?php esc_html_e( 'Use Adyen Pay by Link for standalone sales. Add the API key, merchant account, and webhook HMAC key below.', 'ebook-store' ); ?></span> |
| 10863 |
</span> |
| 10864 |
</label> |
| 10865 |
</div> |
| 10866 |
<div class="ebook-setup-wizard__grid"> |
| 10867 |
<div class="ebook-setup-wizard__field"> |
| 10868 |
<label for="wizard_paypal_account"><?php esc_html_e( 'PayPal account email', 'ebook-store' ); ?></label> |
| 10869 |
<input type="email" id="wizard_paypal_account" name="wizard_paypal_account" value="<?php echo esc_attr( $current_wizard_paypal_account ); ?>" placeholder="<?php echo esc_attr__( 'your-paypal@example.com', 'ebook-store' ); ?>" /> |
| 10870 |
<p class="description"><?php esc_html_e( 'Required when PayPal is enabled. Payments are sent to this PayPal account.', 'ebook-store' ); ?></p> |
| 10871 |
</div> |
| 10872 |
<div class="ebook-setup-wizard__field"> |
| 10873 |
<label for="wizard_stripe_publishable_key"><?php esc_html_e( 'Stripe publishable key', 'ebook-store' ); ?></label> |
| 10874 |
<input type="text" id="wizard_stripe_publishable_key" name="wizard_stripe_publishable_key" value="<?php echo esc_attr( $current_wizard_stripe_publishable_key ); ?>" placeholder="pk_live_... or pk_test_..." class="ebook-setup-wizard__code-input" /> |
| 10875 |
<p class="description"><?php esc_html_e( 'Required when Stripe is enabled.', 'ebook-store' ); ?></p> |
| 10876 |
</div> |
| 10877 |
<div class="ebook-setup-wizard__field"> |
| 10878 |
<label for="wizard_stripe_secret_key"><?php esc_html_e( 'Stripe secret key', 'ebook-store' ); ?></label> |
| 10879 |
<input type="text" id="wizard_stripe_secret_key" name="wizard_stripe_secret_key" value="<?php echo esc_attr( $current_wizard_stripe_secret_key ); ?>" placeholder="sk_live_... or sk_test_..." class="ebook-setup-wizard__code-input" /> |
| 10880 |
<p class="description"><?php esc_html_e( 'Required when Stripe is enabled.', 'ebook-store' ); ?></p> |
| 10881 |
</div> |
| 10882 |
<div class="ebook-setup-wizard__field"> |
| 10883 |
<label for="wizard_adyen_environment"><?php esc_html_e( 'Adyen environment', 'ebook-store' ); ?></label> |
| 10884 |
<select id="wizard_adyen_environment" name="wizard_adyen_environment"> |
| 10885 |
<option value="test" <?php selected( $current_wizard_adyen_environment, 'test' ); ?>><?php esc_html_e( 'Test', 'ebook-store' ); ?></option> |
| 10886 |
<option value="live" <?php selected( $current_wizard_adyen_environment, 'live' ); ?>><?php esc_html_e( 'Live', 'ebook-store' ); ?></option> |
| 10887 |
</select> |
| 10888 |
<p class="description"><?php esc_html_e( 'Required when Adyen is enabled.', 'ebook-store' ); ?></p> |
| 10889 |
</div> |
| 10890 |
<div class="ebook-setup-wizard__field"> |
| 10891 |
<label for="wizard_adyen_merchant_account"><?php esc_html_e( 'Adyen merchant account', 'ebook-store' ); ?></label> |
| 10892 |
<input type="text" id="wizard_adyen_merchant_account" name="wizard_adyen_merchant_account" value="<?php echo esc_attr( $current_wizard_adyen_merchant_account ); ?>" placeholder="YourMerchantAccount" class="ebook-setup-wizard__code-input" /> |
| 10893 |
<p class="description"><?php esc_html_e( 'Required when Adyen is enabled.', 'ebook-store' ); ?></p> |
| 10894 |
</div> |
| 10895 |
<div class="ebook-setup-wizard__field ebook-setup-wizard__field--full"> |
| 10896 |
<label for="wizard_adyen_api_key"><?php esc_html_e( 'Adyen API key', 'ebook-store' ); ?></label> |
| 10897 |
<input type="text" id="wizard_adyen_api_key" name="wizard_adyen_api_key" value="<?php echo esc_attr( $current_wizard_adyen_api_key ); ?>" placeholder="AQEy..." class="ebook-setup-wizard__code-input" /> |
| 10898 |
<p class="description"><?php esc_html_e( 'Required when Adyen is enabled.', 'ebook-store' ); ?></p> |
| 10899 |
</div> |
| 10900 |
<div class="ebook-setup-wizard__field ebook-setup-wizard__field--full"> |
| 10901 |
<label for="wizard_adyen_hmac_key"><?php esc_html_e( 'Adyen webhook HMAC key', 'ebook-store' ); ?></label> |
| 10902 |
<input type="text" id="wizard_adyen_hmac_key" name="wizard_adyen_hmac_key" value="<?php echo esc_attr( $current_wizard_adyen_hmac_key ); ?>" placeholder="base64-hmac-key" class="ebook-setup-wizard__code-input" /> |
| 10903 |
<p class="description"><?php esc_html_e( 'Required when Adyen is enabled. Ebook Store uses this to verify webhook events before creating orders.', 'ebook-store' ); ?></p> |
| 10904 |
</div> |
| 10905 |
<div class="ebook-setup-wizard__field ebook-setup-wizard__field--full"> |
| 10906 |
<label for="wizard_adyen_live_prefix"><?php esc_html_e( 'Adyen live URL prefix', 'ebook-store' ); ?></label> |
| 10907 |
<input type="text" id="wizard_adyen_live_prefix" name="wizard_adyen_live_prefix" value="<?php echo esc_attr( $current_wizard_adyen_live_prefix ); ?>" placeholder="1797a841fbb37ca7-AdyenDemo" class="ebook-setup-wizard__code-input" /> |
| 10908 |
<p class="description"><?php esc_html_e( 'Required only when Adyen is enabled in Live mode.', 'ebook-store' ); ?></p> |
| 10909 |
</div> |
| 10910 |
</div> |
| 10911 |
</div> |
| 10912 |
<div class="ebook-setup-wizard__actions"> |
| 10913 |
<button type="submit" class="button button-primary button-hero"><?php esc_html_e( 'Create ebook draft', 'ebook-store' ); ?></button> |
| 10914 |
</div> |
| 10915 |
</form> |
| 10916 |
</div> |
| 10917 |
<?php elseif ( $wizard_mode === 'woocommerce' ) : ?> |
| 10918 |
<div class="ebook-setup-wizard__panel"> |
| 10919 |
<div class="ebook-setup-wizard__panel-header"> |
| 10920 |
<div> |
| 10921 |
<h2><?php esc_html_e( 'WooCommerce integration', 'ebook-store' ); ?></h2> |
| 10922 |
<p><?php esc_html_e( 'Create the ebook item and the WooCommerce product in one pass. The wizard links them together, marks the product as virtual, and uses the same title on both sides.', 'ebook-store' ); ?></p> |
| 10923 |
</div> |
| 10924 |
<a class="button button-secondary" href="<?php echo esc_url( ebook_store_get_setup_wizard_url() ); ?>"><?php esc_html_e( 'Back', 'ebook-store' ); ?></a> |
| 10925 |
</div> |
| 10926 |
<form method="post" enctype="multipart/form-data" class="ebook-setup-wizard__form"> |
| 10927 |
<?php wp_nonce_field( 'ebook_store_setup_wizard' ); ?> |
| 10928 |
<input type="hidden" name="ebook_store_setup_wizard_action" value="1" /> |
| 10929 |
<input type="hidden" name="wizard_mode" value="woocommerce" /> |
| 10930 |
<div class="ebook-setup-wizard__grid"> |
| 10931 |
<div class="ebook-setup-wizard__field"> |
| 10932 |
<label for="wizard_product_name"><?php esc_html_e( 'Product name', 'ebook-store' ); ?></label> |
| 10933 |
<input type="text" id="wizard_product_name" name="wizard_product_name" value="<?php echo esc_attr( $current_product_name ); ?>" required /> |
| 10934 |
<p class="description"><?php esc_html_e( 'This name is used for both the WooCommerce product and the linked Ebook Store item.', 'ebook-store' ); ?></p> |
| 10935 |
</div> |
| 10936 |
<div class="ebook-setup-wizard__field"> |
| 10937 |
<label for="wizard_price"><?php esc_html_e( 'Product price', 'ebook-store' ); ?></label> |
| 10938 |
<div class="ebook-setup-wizard__input-group"> |
| 10939 |
<span><?php echo esc_html( $current_currency ); ?></span> |
| 10940 |
<input type="number" id="wizard_price" name="wizard_price" min="0" step="0.01" value="<?php echo esc_attr( $current_price ); ?>" required /> |
| 10941 |
</div> |
| 10942 |
<p class="description"><?php esc_html_e( 'The same amount is written to the Ebook Store item and the WooCommerce product.', 'ebook-store' ); ?></p> |
| 10943 |
</div> |
| 10944 |
<div class="ebook-setup-wizard__field ebook-setup-wizard__field--full"> |
| 10945 |
<label for="wizard_pdf_file"><?php esc_html_e( 'PDF file', 'ebook-store' ); ?></label> |
| 10946 |
<input type="file" id="wizard_pdf_file" name="wizard_pdf_file" accept="application/pdf,.pdf" required /> |
| 10947 |
<p class="description"><?php esc_html_e( 'The uploaded PDF becomes the secured file in Ebook Store. Its text is used to build a summary, and the first page becomes the generated cover.', 'ebook-store' ); ?></p> |
| 10948 |
</div> |
| 10949 |
</div> |
| 10950 |
<div class="ebook-setup-wizard__actions"> |
| 10951 |
<button type="submit" class="button button-primary button-hero"><?php esc_html_e( 'Create linked draft items', 'ebook-store' ); ?></button> |
| 10952 |
</div> |
| 10953 |
</form> |
| 10954 |
</div> |
| 10955 |
<?php else : ?> |
| 10956 |
<div class="ebook-setup-wizard__chooser"> |
| 10957 |
<a class="ebook-setup-wizard__choice" href="<?php echo esc_url( ebook_store_get_setup_wizard_url( array( 'wizard_mode' => 'standalone' ) ) ); ?>"> |
| 10958 |
<span class="dashicons dashicons-book-alt"></span> |
| 10959 |
<h2><?php esc_html_e( 'Sell with Ebook Store', 'ebook-store' ); ?></h2> |
| 10960 |
<p><?php esc_html_e( 'Use Ebook Store as the primary checkout and delivery flow. Best when you want the standalone order form, watermarking, and direct secure fulfillment without a separate product catalog.', 'ebook-store' ); ?></p> |
| 10961 |
<strong><?php esc_html_e( 'Upload a PDF and generate the ebook item automatically', 'ebook-store' ); ?></strong> |
| 10962 |
</a> |
| 10963 |
<a class="ebook-setup-wizard__choice" href="<?php echo esc_url( ebook_store_get_setup_wizard_url( array( 'wizard_mode' => 'woocommerce' ) ) ); ?>"> |
| 10964 |
<span class="dashicons dashicons-cart"></span> |
| 10965 |
<h2><?php esc_html_e( 'Use WooCommerce integration', 'ebook-store' ); ?></h2> |
| 10966 |
<p><?php esc_html_e( 'Create the secured ebook item behind the scenes, then pair it with a WooCommerce product so checkout happens in WooCommerce while Ebook Store still handles delivery and protection.', 'ebook-store' ); ?></p> |
| 10967 |
<strong><?php esc_html_e( 'Create and link both items in one guided flow', 'ebook-store' ); ?></strong> |
| 10968 |
</a> |
| 10969 |
</div> |
| 10970 |
<?php endif; ?> |
| 10971 |
</div> |
| 10972 |
<?php |
| 10973 |
$output = ob_get_clean(); |
| 10974 |
echo function_exists( 'ebook_store_translate_markup' ) ? ebook_store_translate_markup( $output ) : $output; |
| 10975 |
} |
| 10976 |
|
| 10977 |
function ebook_store_get_import_books_url() { |
| 10978 |
return admin_url( 'edit.php?post_type=ebook&page=ebook-store-import-books' ); |
| 10979 |
} |
| 10980 |
|
| 10981 |
function ebook_store_import_books_has_pro_access() { |
| 10982 |
if ( function_exists( 'ebook_store_has_pro_license' ) ) { |
| 10983 |
return ebook_store_has_pro_license(); |
| 10984 |
} |
| 10985 |
|
| 10986 |
return trim( (string) get_option( 'ebook_store_license_key', '' ) ) !== ''; |
| 10987 |
} |
| 10988 |
|
| 10989 |
function ebook_store_import_books_admin_assets() { |
| 10990 |
if ( ! is_admin() ) { |
| 10991 |
return; |
| 10992 |
} |
| 10993 |
|
| 10994 |
$page = isset( $_GET['page'] ) ? sanitize_key( (string) wp_unslash( $_GET['page'] ) ) : ''; |
| 10995 |
if ( $page !== 'ebook-store-import-books' ) { |
| 10996 |
return; |
| 10997 |
} |
| 10998 |
|
| 10999 |
wp_enqueue_style( 'ebookstorestylesheet' ); |
| 11000 |
wp_enqueue_script( 'plupload-all' ); |
| 11001 |
wp_enqueue_script( |
| 11002 |
'ebook-store-import-books', |
| 11003 |
plugins_url( 'js/ebook_store_import_books.js', __FILE__ ), |
| 11004 |
array( 'jquery', 'plupload-all' ), |
| 11005 |
filemtime( plugin_dir_path( __FILE__ ) . 'js/ebook_store_import_books.js' ), |
| 11006 |
true |
| 11007 |
); |
| 11008 |
wp_localize_script( |
| 11009 |
'ebook-store-import-books', |
| 11010 |
'ebookStoreImportBooks', |
| 11011 |
array( |
| 11012 |
'ajaxUrl' => admin_url( 'admin-ajax.php' ), |
| 11013 |
'nonce' => wp_create_nonce( 'ebook_store_import_books' ), |
| 11014 |
'sessionId' => wp_generate_uuid4(), |
| 11015 |
'hasProLicense' => ebook_store_import_books_has_pro_access(), |
| 11016 |
'woocommerceAvailable' => class_exists( 'WooCommerce' ), |
| 11017 |
'maxFileSize' => (string) wp_max_upload_size() . 'b', |
| 11018 |
'strings' => array( |
| 11019 |
'dropActive' => __( 'Drop PDFs to add them to the import queue', 'ebook-store' ), |
| 11020 |
'dropIdle' => __( 'Drop PDF files here or use the button below', 'ebook-store' ), |
| 11021 |
'uploading' => __( 'Uploading...', 'ebook-store' ), |
| 11022 |
'uploaded' => __( 'Uploaded', 'ebook-store' ), |
| 11023 |
'queued' => __( 'Queued for import', 'ebook-store' ), |
| 11024 |
'importing' => __( 'Creating draft items...', 'ebook-store' ), |
| 11025 |
'imported' => __( 'Imported', 'ebook-store' ), |
| 11026 |
'failed' => __( 'Failed', 'ebook-store' ), |
| 11027 |
'invalidMode' => __( 'Choose how imported books should be created before starting the import.', 'ebook-store' ), |
| 11028 |
'invalidPrice' => __( 'Enter a valid default price before starting the import.', 'ebook-store' ), |
| 11029 |
'noUploadedFiles' => __( 'Upload at least one PDF before starting the import.', 'ebook-store' ), |
| 11030 |
'proRequired' => __( 'Import books is available in Ebook Store Pro only.', 'ebook-store' ), |
| 11031 |
'woocommerceMissing' => __( 'WooCommerce is not active, so the connected product option is not available.', 'ebook-store' ), |
| 11032 |
'titleFallback' => __( 'Imported ebook', 'ebook-store' ), |
| 11033 |
'descriptionFallback' => __( 'No description could be extracted from the PDF.', 'ebook-store' ), |
| 11034 |
'ebookLink' => __( 'Edit ebook', 'ebook-store' ), |
| 11035 |
'productLink' => __( 'Edit product', 'ebook-store' ), |
| 11036 |
'completedSummary' => __( 'Import finished.', 'ebook-store' ), |
| 11037 |
'processingSummary' => __( 'Importing uploaded books...', 'ebook-store' ), |
| 11038 |
), |
| 11039 |
) |
| 11040 |
); |
| 11041 |
} |
| 11042 |
|
| 11043 |
function ebook_store_import_books_get_temp_root() { |
| 11044 |
return trailingslashit( get_temp_dir() ) . 'ebook-store-imports'; |
| 11045 |
} |
| 11046 |
|
| 11047 |
function ebook_store_import_books_get_session_dir( $user_id, $session_id ) { |
| 11048 |
$user_id = max( 0, (int) $user_id ); |
| 11049 |
$session_id = preg_replace( '/[^a-z0-9_-]/i', '', (string) $session_id ); |
| 11050 |
|
| 11051 |
return trailingslashit( ebook_store_import_books_get_temp_root() ) . $user_id . '/' . $session_id; |
| 11052 |
} |
| 11053 |
|
| 11054 |
function ebook_store_import_books_ensure_directory( $dir ) { |
| 11055 |
if ( $dir === '' ) { |
| 11056 |
return false; |
| 11057 |
} |
| 11058 |
|
| 11059 |
if ( file_exists( $dir ) ) { |
| 11060 |
return is_dir( $dir ); |
| 11061 |
} |
| 11062 |
|
| 11063 |
return wp_mkdir_p( $dir ); |
| 11064 |
} |
| 11065 |
|
| 11066 |
function ebook_store_import_books_cleanup_empty_dirs( $dir, $stop_dir ) { |
| 11067 |
$dir = untrailingslashit( (string) $dir ); |
| 11068 |
$stop_dir = untrailingslashit( (string) $stop_dir ); |
| 11069 |
|
| 11070 |
while ( $dir !== '' && strpos( $dir, $stop_dir ) === 0 && $dir !== $stop_dir ) { |
| 11071 |
$entries = @scandir( $dir ); |
| 11072 |
if ( ! is_array( $entries ) || count( $entries ) > 2 ) { |
| 11073 |
break; |
| 11074 |
} |
| 11075 |
@rmdir( $dir ); |
| 11076 |
$dir = dirname( $dir ); |
| 11077 |
} |
| 11078 |
} |
| 11079 |
|
| 11080 |
function ebook_store_import_books_store_queue_item( $token, $data ) { |
| 11081 |
$user_id = get_current_user_id(); |
| 11082 |
set_transient( 'ebook_store_import_queue_' . $user_id . '_' . $token, $data, DAY_IN_SECONDS ); |
| 11083 |
} |
| 11084 |
|
| 11085 |
function ebook_store_import_books_get_queue_item( $token ) { |
| 11086 |
$user_id = get_current_user_id(); |
| 11087 |
return get_transient( 'ebook_store_import_queue_' . $user_id . '_' . $token ); |
| 11088 |
} |
| 11089 |
|
| 11090 |
function ebook_store_import_books_delete_queue_item( $token ) { |
| 11091 |
$user_id = get_current_user_id(); |
| 11092 |
delete_transient( 'ebook_store_import_queue_' . $user_id . '_' . $token ); |
| 11093 |
} |
| 11094 |
|
| 11095 |
function ebook_store_import_books_ajax_upload() { |
| 11096 |
check_ajax_referer( 'ebook_store_import_books', 'nonce' ); |
| 11097 |
|
| 11098 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 11099 |
wp_send_json_error( array( 'message' => __( 'You do not have permission to import books.', 'ebook-store' ) ), 403 ); |
| 11100 |
} |
| 11101 |
|
| 11102 |
if ( ! ebook_store_import_books_has_pro_access() ) { |
| 11103 |
wp_send_json_error( array( 'message' => __( 'Import books is available in Ebook Store Pro only.', 'ebook-store' ) ), 403 ); |
| 11104 |
} |
| 11105 |
|
| 11106 |
if ( empty( $_FILES['file']['tmp_name'] ) ) { |
| 11107 |
wp_send_json_error( array( 'message' => __( 'No upload chunk was received.', 'ebook-store' ) ), 400 ); |
| 11108 |
} |
| 11109 |
|
| 11110 |
$session_id = isset( $_REQUEST['session'] ) ? preg_replace( '/[^a-z0-9_-]/i', '', (string) wp_unslash( $_REQUEST['session'] ) ) : ''; |
| 11111 |
$file_id = isset( $_REQUEST['file_id'] ) ? preg_replace( '/[^a-z0-9_-]/i', '', (string) wp_unslash( $_REQUEST['file_id'] ) ) : ''; |
| 11112 |
$original_name = isset( $_REQUEST['name'] ) ? sanitize_file_name( (string) wp_unslash( $_REQUEST['name'] ) ) : sanitize_file_name( (string) $_FILES['file']['name'] ); |
| 11113 |
$chunk = isset( $_REQUEST['chunk'] ) ? max( 0, (int) $_REQUEST['chunk'] ) : 0; |
| 11114 |
$chunks = isset( $_REQUEST['chunks'] ) ? max( 0, (int) $_REQUEST['chunks'] ) : 0; |
| 11115 |
|
| 11116 |
if ( $session_id === '' || $file_id === '' || $original_name === '' ) { |
| 11117 |
wp_send_json_error( array( 'message' => __( 'The upload request is missing required file information.', 'ebook-store' ) ), 400 ); |
| 11118 |
} |
| 11119 |
|
| 11120 |
$filetype = wp_check_filetype( $original_name ); |
| 11121 |
$extension = isset( $filetype['ext'] ) ? strtolower( (string) $filetype['ext'] ) : ''; |
| 11122 |
if ( $extension !== 'pdf' ) { |
| 11123 |
wp_send_json_error( array( 'message' => __( 'Only PDF files can be imported here.', 'ebook-store' ) ), 400 ); |
| 11124 |
} |
| 11125 |
|
| 11126 |
$session_dir = ebook_store_import_books_get_session_dir( get_current_user_id(), $session_id ); |
| 11127 |
if ( ! ebook_store_import_books_ensure_directory( $session_dir ) ) { |
| 11128 |
wp_send_json_error( array( 'message' => __( 'The temporary upload directory could not be created.', 'ebook-store' ) ), 500 ); |
| 11129 |
} |
| 11130 |
|
| 11131 |
$stored_name = sanitize_file_name( $file_id . '-' . $original_name ); |
| 11132 |
$partial_path = trailingslashit( $session_dir ) . $stored_name . '.part'; |
| 11133 |
$final_path = trailingslashit( $session_dir ) . $stored_name; |
| 11134 |
$chunk_path = $_FILES['file']['tmp_name']; |
| 11135 |
|
| 11136 |
$input_handle = @fopen( $chunk_path, 'rb' ); |
| 11137 |
if ( ! $input_handle ) { |
| 11138 |
wp_send_json_error( array( 'message' => __( 'The uploaded file chunk could not be read.', 'ebook-store' ) ), 500 ); |
| 11139 |
} |
| 11140 |
|
| 11141 |
$output_handle = @fopen( $partial_path, $chunk === 0 ? 'wb' : 'ab' ); |
| 11142 |
if ( ! $output_handle ) { |
| 11143 |
@fclose( $input_handle ); |
| 11144 |
wp_send_json_error( array( 'message' => __( 'The temporary upload file could not be written.', 'ebook-store' ) ), 500 ); |
| 11145 |
} |
| 11146 |
|
| 11147 |
while ( ! feof( $input_handle ) ) { |
| 11148 |
$buffer = fread( $input_handle, 1048576 ); |
| 11149 |
if ( $buffer === false ) { |
| 11150 |
break; |
| 11151 |
} |
| 11152 |
fwrite( $output_handle, $buffer ); |
| 11153 |
} |
| 11154 |
|
| 11155 |
@fclose( $input_handle ); |
| 11156 |
@fclose( $output_handle ); |
| 11157 |
|
| 11158 |
$is_last_chunk = $chunks < 2 || ( $chunk + 1 ) >= $chunks; |
| 11159 |
if ( ! $is_last_chunk ) { |
| 11160 |
wp_send_json_success( |
| 11161 |
array( |
| 11162 |
'fileId' => $file_id, |
| 11163 |
'status' => 'chunked', |
| 11164 |
) |
| 11165 |
); |
| 11166 |
} |
| 11167 |
|
| 11168 |
@unlink( $final_path ); |
| 11169 |
if ( ! @rename( $partial_path, $final_path ) ) { |
| 11170 |
wp_send_json_error( array( 'message' => __( 'The uploaded PDF could not be finalized.', 'ebook-store' ) ), 500 ); |
| 11171 |
} |
| 11172 |
|
| 11173 |
$pdf_file = array( |
| 11174 |
'name' => $original_name, |
| 11175 |
'tmp_name' => $final_path, |
| 11176 |
); |
| 11177 |
if ( ! ebook_store_setup_wizard_is_pdf( $pdf_file ) ) { |
| 11178 |
@unlink( $final_path ); |
| 11179 |
wp_send_json_error( array( 'message' => __( 'The uploaded file is not a valid PDF.', 'ebook-store' ) ), 400 ); |
| 11180 |
} |
| 11181 |
|
| 11182 |
$fallback_title = preg_replace( '/\.[^.]+$/', '', $original_name ); |
| 11183 |
$pdf_details = ebook_store_setup_wizard_extract_pdf_details( $final_path, $fallback_title ); |
| 11184 |
$token = str_replace( '-', '', wp_generate_uuid4() ); |
| 11185 |
$filesize = file_exists( $final_path ) ? (int) filesize( $final_path ) : 0; |
| 11186 |
|
| 11187 |
ebook_store_import_books_store_queue_item( |
| 11188 |
$token, |
| 11189 |
array( |
| 11190 |
'token' => $token, |
| 11191 |
'file_id' => $file_id, |
| 11192 |
'session_id' => $session_id, |
| 11193 |
'file_path' => $final_path, |
| 11194 |
'file_name' => $original_name, |
| 11195 |
'file_size' => $filesize, |
| 11196 |
'details' => $pdf_details, |
| 11197 |
'uploaded_at' => time(), |
| 11198 |
) |
| 11199 |
); |
| 11200 |
|
| 11201 |
wp_send_json_success( |
| 11202 |
array( |
| 11203 |
'fileId' => $file_id, |
| 11204 |
'token' => $token, |
| 11205 |
'fileName' => $original_name, |
| 11206 |
'fileSize' => $filesize, |
| 11207 |
'title' => isset( $pdf_details['title'] ) ? (string) $pdf_details['title'] : '', |
| 11208 |
'description' => isset( $pdf_details['description'] ) ? (string) $pdf_details['description'] : '', |
| 11209 |
'author' => isset( $pdf_details['author'] ) ? (string) $pdf_details['author'] : '', |
| 11210 |
'pages' => isset( $pdf_details['pages'] ) ? (int) $pdf_details['pages'] : 0, |
| 11211 |
) |
| 11212 |
); |
| 11213 |
} |
| 11214 |
|
| 11215 |
function ebook_store_import_books_ajax_process() { |
| 11216 |
check_ajax_referer( 'ebook_store_import_books', 'nonce' ); |
| 11217 |
|
| 11218 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 11219 |
wp_send_json_error( array( 'message' => __( 'You do not have permission to import books.', 'ebook-store' ) ), 403 ); |
| 11220 |
} |
| 11221 |
|
| 11222 |
if ( ! ebook_store_import_books_has_pro_access() ) { |
| 11223 |
wp_send_json_error( array( 'message' => __( 'Import books is available in Ebook Store Pro only.', 'ebook-store' ) ), 403 ); |
| 11224 |
} |
| 11225 |
|
| 11226 |
$token = isset( $_POST['token'] ) ? sanitize_key( (string) wp_unslash( $_POST['token'] ) ) : ''; |
| 11227 |
$mode = isset( $_POST['import_mode'] ) ? sanitize_key( (string) wp_unslash( $_POST['import_mode'] ) ) : 'ebook'; |
| 11228 |
$import_price = isset( $_POST['import_price'] ) && is_scalar( $_POST['import_price'] ) ? (float) str_replace( ',', '.', (string) wp_unslash( $_POST['import_price'] ) ) : 0.0; |
| 11229 |
$import_price = max( 0, $import_price ); |
| 11230 |
|
| 11231 |
if ( $token === '' ) { |
| 11232 |
wp_send_json_error( array( 'message' => __( 'The uploaded file token is missing.', 'ebook-store' ) ), 400 ); |
| 11233 |
} |
| 11234 |
|
| 11235 |
if ( ! in_array( $mode, array( 'ebook', 'woocommerce' ), true ) ) { |
| 11236 |
wp_send_json_error( array( 'message' => __( 'The selected import mode is invalid.', 'ebook-store' ) ), 400 ); |
| 11237 |
} |
| 11238 |
|
| 11239 |
$queue_item = ebook_store_import_books_get_queue_item( $token ); |
| 11240 |
if ( ! is_array( $queue_item ) || empty( $queue_item['file_path'] ) ) { |
| 11241 |
wp_send_json_error( array( 'message' => __( 'The uploaded file could not be found. Please upload it again.', 'ebook-store' ) ), 404 ); |
| 11242 |
} |
| 11243 |
|
| 11244 |
$file_path = (string) $queue_item['file_path']; |
| 11245 |
if ( ! file_exists( $file_path ) ) { |
| 11246 |
ebook_store_import_books_delete_queue_item( $token ); |
| 11247 |
wp_send_json_error( array( 'message' => __( 'The uploaded PDF is no longer available. Please upload it again.', 'ebook-store' ) ), 404 ); |
| 11248 |
} |
| 11249 |
|
| 11250 |
$details = isset( $queue_item['details'] ) && is_array( $queue_item['details'] ) ? $queue_item['details'] : array(); |
| 11251 |
$file_name = isset( $queue_item['file_name'] ) ? (string) $queue_item['file_name'] : __( 'uploaded PDF', 'ebook-store' ); |
| 11252 |
$fallback_title = preg_replace( '/\.[^.]+$/', '', (string) $queue_item['file_name'] ); |
| 11253 |
$title = isset( $details['title'] ) && $details['title'] !== '' ? (string) $details['title'] : $fallback_title; |
| 11254 |
$description = isset( $details['description'] ) ? (string) $details['description'] : ''; |
| 11255 |
|
| 11256 |
$shared_args = array( |
| 11257 |
'title' => $title !== '' ? $title : __( 'Imported ebook', 'ebook-store' ), |
| 11258 |
'product_name' => $title !== '' ? $title : __( 'Imported ebook', 'ebook-store' ), |
| 11259 |
'description' => $description, |
| 11260 |
'price' => $import_price, |
| 11261 |
'currency' => ebook_store_get_store_currency(), |
| 11262 |
'pdf_file' => array( |
| 11263 |
'name' => $file_name, |
| 11264 |
'tmp_name' => $file_path, |
| 11265 |
), |
| 11266 |
'pages' => isset( $details['pages'] ) ? (int) $details['pages'] : 0, |
| 11267 |
'author' => isset( $details['author'] ) ? (string) $details['author'] : '', |
| 11268 |
); |
| 11269 |
|
| 11270 |
try { |
| 11271 |
$result = $mode === 'woocommerce' |
| 11272 |
? ebook_store_setup_wizard_create_woocommerce_bundle( $shared_args ) |
| 11273 |
: ebook_store_setup_wizard_create_ebook( $shared_args ); |
| 11274 |
} catch ( Throwable $throwable ) { |
| 11275 |
wp_send_json_error( |
| 11276 |
array( |
| 11277 |
'message' => sprintf( |
| 11278 |
__( 'Import failed for "%1$s": %2$s', 'ebook-store' ), |
| 11279 |
$file_name, |
| 11280 |
$throwable->getMessage() |
| 11281 |
), |
| 11282 |
), |
| 11283 |
500 |
| 11284 |
); |
| 11285 |
} |
| 11286 |
|
| 11287 |
if ( is_wp_error( $result ) ) { |
| 11288 |
wp_send_json_error( |
| 11289 |
array( |
| 11290 |
'message' => sprintf( |
| 11291 |
__( 'Import failed for "%1$s": %2$s', 'ebook-store' ), |
| 11292 |
$file_name, |
| 11293 |
$result->get_error_message() |
| 11294 |
), |
| 11295 |
), |
| 11296 |
400 |
| 11297 |
); |
| 11298 |
} |
| 11299 |
|
| 11300 |
ebook_store_import_books_delete_queue_item( $token ); |
| 11301 |
@unlink( $file_path ); |
| 11302 |
ebook_store_import_books_cleanup_empty_dirs( dirname( $file_path ), ebook_store_import_books_get_temp_root() ); |
| 11303 |
|
| 11304 |
wp_send_json_success( |
| 11305 |
array( |
| 11306 |
'title' => $shared_args['title'], |
| 11307 |
'ebookId' => isset( $result['ebook_id'] ) ? (int) $result['ebook_id'] : 0, |
| 11308 |
'ebookEditUrl' => ! empty( $result['ebook_id'] ) ? get_edit_post_link( (int) $result['ebook_id'], 'raw' ) : '', |
| 11309 |
'productId' => ! empty( $result['product_id'] ) ? (int) $result['product_id'] : 0, |
| 11310 |
'productEditUrl' => ! empty( $result['product_id'] ) ? get_edit_post_link( (int) $result['product_id'], 'raw' ) : '', |
| 11311 |
) |
| 11312 |
); |
| 11313 |
} |
| 11314 |
|
| 11315 |
function ebook_store_register_my_custom_submenu_page() { |
| 11316 |
|
| 11317 |
add_submenu_page( |
| 11318 |
'edit.php?post_type=ebook' //or 'options.php' |
| 11319 |
, __('Orders - Add New', 'ebook-store') |
| 11320 |
, __('Orders - Add New', 'ebook-store') |
| 11321 |
, 'manage_options' |
| 11322 |
, 'ebook-store-add-order-page' |
| 11323 |
, 'ebook_store_add_order_page_callback' |
| 11324 |
); |
| 11325 |
|
| 11326 |
add_submenu_page( |
| 11327 |
'edit.php?post_type=ebook' //or 'options.php' |
| 11328 |
, __('Orders - Reports', 'ebook-store') |
| 11329 |
, __('Orders - Reports', 'ebook-store') |
| 11330 |
, 'manage_options' |
| 11331 |
, 'ebook-store-order_reports' |
| 11332 |
, 'ebook_store_order_reports_callback' |
| 11333 |
); |
| 11334 |
|
| 11335 |
add_submenu_page( |
| 11336 |
'edit.php?post_type=ebook' //or 'options.php' |
| 11337 |
, __('Orders - New Issue', 'ebook-store') |
| 11338 |
, __('Orders - New Issue', 'ebook-store') |
| 11339 |
, 'manage_options' |
| 11340 |
, 'ebook-store-add-issue-page' |
| 11341 |
, 'ebook_store_add_issue_page_callback' |
| 11342 |
); |
| 11343 |
|
| 11344 |
add_submenu_page( |
| 11345 |
'edit.php?post_type=ebook', |
| 11346 |
__( 'Setup wizard', 'ebook-store' ), |
| 11347 |
ebook_store_setup_wizard_menu_title(), |
| 11348 |
'manage_options', |
| 11349 |
'ebook-store-setup-wizard', |
| 11350 |
'ebook_store_setup_wizard_page_callback' |
| 11351 |
); |
| 11352 |
|
| 11353 |
add_submenu_page( |
| 11354 |
'edit.php?post_type=ebook', |
| 11355 |
__( 'Import books', 'ebook-store' ), |
| 11356 |
__( 'Import books', 'ebook-store' ), |
| 11357 |
'manage_options', |
| 11358 |
'ebook-store-import-books', |
| 11359 |
'ebook_store_import_books_page_callback' |
| 11360 |
); |
| 11361 |
|
| 11362 |
add_submenu_page( |
| 11363 |
'edit.php?post_type=ebook', |
| 11364 |
__( 'Button templates', 'ebook-store' ), |
| 11365 |
__( 'Button templates', 'ebook-store' ), |
| 11366 |
'manage_options', |
| 11367 |
'ebook-store-template-editor', |
| 11368 |
'ebook_store_template_editor_page_callback' |
| 11369 |
); |
| 11370 |
|
| 11371 |
if ( ! ebook_store_can_access_template_editor() ) { |
| 11372 |
remove_submenu_page( 'edit.php?post_type=ebook', 'ebook-store-template-editor' ); |
| 11373 |
} |
| 11374 |
|
| 11375 |
add_submenu_page( |
| 11376 |
'edit.php?post_type=ebook', |
| 11377 |
'Settings', |
| 11378 |
__('Settings', 'ebook-store'), |
| 11379 |
'manage_options', |
| 11380 |
'settings-page', |
| 11381 |
'ebook_store_settings_callback' ); |
| 11382 |
|
| 11383 |
} |
| 11384 |
function ebook_store_settings_callback() { |
| 11385 |
echo '<script>window.location = "options-general.php?page=ebook_options.php"; </script>'; |
| 11386 |
} |
| 11387 |
function ebook_store_add_query_vars_filter( $vars ){ |
| 11388 |
$vars[] = "new_issue_purchase_period"; |
| 11389 |
$vars[] = "new_issue_ebook_id"; |
| 11390 |
$vars[] = "woocommerce_product_id"; |
| 11391 |
return $vars; |
| 11392 |
} |
| 11393 |
|
| 11394 |
//Add custom query vars |
| 11395 |
|
| 11396 |
function ebook_store_add_issue_page_callback() { |
| 11397 |
require_once 'ebook_store_add_issue_page_callback.php'; |
| 11398 |
|
| 11399 |
} |
| 11400 |
|
| 11401 |
function ebook_store_import_books_page_callback() { |
| 11402 |
require_once 'ebook_store_import_books_page_callback.php'; |
| 11403 |
} |
| 11404 |
|
| 11405 |
function ebook_store_add_order_page_callback() { |
| 11406 |
require_once 'ebook_store_add_order_page_callback.php'; |
| 11407 |
} |
| 11408 |
function ebook_store_add_order($data, $silent = false, $thankYouRedirect = false, $ebook_key = false) { |
| 11409 |
$QSWPOptions = new QSWPOptions(); |
| 11410 |
|
| 11411 |
if (@$ebook_key == '') { |
| 11412 |
$ebook_key = md5(microtime() . rand(1,10000000)); |
| 11413 |
} else { |
| 11414 |
// An explicit key was supplied — this is a payment webhook (Stripe/Adyen) |
| 11415 |
// or a recovery call. If an order already exists for this key, a retried |
| 11416 |
// or duplicated webhook is calling again: return the existing order rather |
| 11417 |
// than creating a duplicate order and sending a second delivery email. |
| 11418 |
$existing_order = ebook_get_order( 'ebook_key', $ebook_key ); |
| 11419 |
if ( ! empty( $existing_order['order_id'][0] ) ) { |
| 11420 |
return $silent ? (int) $existing_order['order_id'][0] : null; |
| 11421 |
} |
| 11422 |
} |
| 11423 |
|
| 11424 |
$data['md5_nonce'] = md5(microtime() . mt_rand(1,99999999) . NONCE_KEY); |
| 11425 |
if ( ! isset( $data['mc_fee'] ) ) { |
| 11426 |
$data['mc_fee'] = 0; |
| 11427 |
} |
| 11428 |
$data['item_name'] = get_the_title($data['ebook']); |
| 11429 |
$data['payment_date'] = date("m/d/Y H:i:s"); |
| 11430 |
@$data['txn_id'] = (@$data['txn_id'] != '' ? @$data['txn_id'] : 'Manual Payment'); |
| 11431 |
$data['mc_currency'] = ebook_store_get_store_currency(); |
| 11432 |
if ( ! isset( $data['tax'] ) || ! is_numeric( $data['tax'] ) ) { |
| 11433 |
$data['tax'] = 0; |
| 11434 |
} |
| 11435 |
$data['residence_country'] = 'n/a'; |
| 11436 |
|
| 11437 |
ebook_store_subscribe_buyer_to_marketing_lists($data['payer_email']); |
| 11438 |
if (@$data['user_id'] == 0) { |
| 11439 |
$data['user_id'] = ebook_store_silent_registration($data); |
| 11440 |
} |
| 11441 |
|
| 11442 |
|
| 11443 |
$my_post = array( |
| 11444 |
'post_title' => $data['first_name'] . ' ' . $data['last_name'], |
| 11445 |
'post_type' => 'ebook_order', |
| 11446 |
'post_status' => 'publish', |
| 11447 |
'post_author' => 1, |
| 11448 |
//'post_category' => array(8,39), |
| 11449 |
); |
| 11450 |
if ($data['mc_gross'] == false) { |
| 11451 |
$data['mc_gross'] = 0; |
| 11452 |
} |
| 11453 |
// Store a machine-readable amount with NO thousands separator. It used |
| 11454 |
// to be number_format()'d twice with a ',' separator: the second pass |
| 11455 |
// re-cast "1,500.00" to a float, which PHP truncates at the comma to |
| 11456 |
// 1.0 — so every order of 1000+ was recorded as 1.00. Thousands |
| 11457 |
// separators belong at display time only. |
| 11458 |
$mc_gross = number_format( (float) $data['mc_gross'], 2, '.', '' ); |
| 11459 |
$ebook = get_post_meta($data['ebook'], 'ebook', true); |
| 11460 |
$ebookObj = new EbookStoreEbook($data['ebook']); |
| 11461 |
|
| 11462 |
// Note: unlike the PayPal IPN path, every caller of this function has |
| 11463 |
// already verified the payment (admin-entered order, or a signed |
| 11464 |
// Stripe/Adyen webhook), so there is no untrusted amount to check here. |
| 11465 |
$post_id = wp_insert_post( $my_post, true ); |
| 11466 |
if ( ! is_wp_error( $post_id ) && $post_id ) { |
| 11467 |
foreach ($data as $k => $v) { |
| 11468 |
update_post_meta($post_id, $k, $v); |
| 11469 |
@$order[$k] = $v; |
| 11470 |
} |
| 11471 |
|
| 11472 |
$order['order_id'] = $post_id; |
| 11473 |
$order['password'] = ebook_store_get_password_for_order($post_id, array( |
| 11474 |
'password' => isset($data['password']) ? $data['password'] : '', |
| 11475 |
'payer_email' => isset($data['payer_email']) ? $data['payer_email'] : '', |
| 11476 |
'md5_nonce' => isset($data['md5_nonce']) ? $data['md5_nonce'] : '', |
| 11477 |
'ebook_key' => $ebook_key, |
| 11478 |
)); |
| 11479 |
$data['password'] = $order['password']; |
| 11480 |
|
| 11481 |
global $attachment, $ebook_email_delivery; |
| 11482 |
$attachment = ebook_attachment($data['ebook'],true); |
| 11483 |
|
| 11484 |
$formData = ebook_store_get_form($data['md5_nonce']); |
| 11485 |
$formData = json_encode($formData); |
| 11486 |
$ebook_order['ebook_key'][0] = $ebook_key; |
| 11487 |
$ebook_order['ebook'][0] = $data['ebook']; |
| 11488 |
$ebook_order['order_id'][0] = $post_id; |
| 11489 |
$ebook_order['md5_nonce'][0] = isset( $data['md5_nonce'] ) ? $data['md5_nonce'] : ''; |
| 11490 |
$order['downloadlink'] = ebook_download_link($ebook_order); |
| 11491 |
|
| 11492 |
$ebookObj->order_id = $post_id; |
| 11493 |
// Plugin order: link() needs the ebook_key credentials or the |
| 11494 |
// non-PDF format links point at the WooCommerce-only endpoint. |
| 11495 |
$ebookObj->ebook_key = $ebook_key; |
| 11496 |
$ebookObj->md5_nonce = isset( $data['md5_nonce'] ) ? (string) $data['md5_nonce'] : ''; |
| 11497 |
$ebookObj->setLink['pdf'] = $order['downloadlink']; |
| 11498 |
|
| 11499 |
$order['downloadlink_html'] = $ebookObj->format_links(); |
| 11500 |
$ebook_order['downloadlink_html'][0] = $ebookObj->format_links(); |
| 11501 |
|
| 11502 |
$order['ebook_key'] = $ebook_key; |
| 11503 |
$order['order_id'] = $post_id; |
| 11504 |
$order['md5_nonce'] = $data['md5_nonce']; |
| 11505 |
update_post_meta($post_id,'ebook_key',$ebook_key); |
| 11506 |
update_post_meta($post_id,'downloads',0); |
| 11507 |
update_post_meta($post_id,'mc_gross',$mc_gross); |
| 11508 |
update_post_meta($post_id,'formData',wp_slash($formData)); |
| 11509 |
update_post_meta($post_id,'downloadlink',$order['downloadlink']); |
| 11510 |
update_post_meta($post_id,'ebook',$data['ebook']); |
| 11511 |
//error_log('ebook_store_add_order - ' . print_r($order,true)); |
| 11512 |
|
| 11513 |
$email_template = ebook_store_get_effective_delivery_email_template( absint( $data['ebook'] ) ); |
| 11514 |
$ebook_email_delivery = array( |
| 11515 |
'to' => $data['payer_email'], |
| 11516 |
'subject' => $email_template['subject'], |
| 11517 |
'text' => $email_template['text'], |
| 11518 |
'attachment' => $attachment, |
| 11519 |
'order' => $order, |
| 11520 |
'source' => 'standalone', |
| 11521 |
'related_order_type' => 'ebook_order', |
| 11522 |
); |
| 11523 |
//mail(get_option( 'admin_email' ), 'Ebook store for WordPress - Verified Order Received', print_r($ebook_email_delivery,true)); |
| 11524 |
@$fileExt = pathinfo($attachment[0]['file'],PATHINFO_EXTENSION); |
| 11525 |
//wp_mail('deian@motov.net','test','test'); |
| 11526 |
if ($fileExt == 'pdf' && get_option('encrypt_pdf')) { |
| 11527 |
ebook_encrypt_pdf($data, null, $data); |
| 11528 |
} |
| 11529 |
if ($silent == false) { |
| 11530 |
?> |
| 11531 |
<div class="updated"> |
| 11532 |
<p><?php _e( 'Order has been generated and email was sent to the buyer!<br /><small>If your email has not arrived, we strongly recommend using Easy WP SMTP to send emails trough a service like Gmail to ensure inbox delivery.</small>', 'ebook-store' ); ?></p> |
| 11533 |
</div><?php |
| 11534 |
} ebook_email_delivery($post_id); |
| 11535 |
|
| 11536 |
add_action( 'init', 'ebook_email_delivery', 100); |
| 11537 |
if ($thankYouRedirect) { |
| 11538 |
$checkoutPage = esc_attr(get_option('ebook_store_checkout_page')); |
| 11539 |
$link = add_query_arg(array('ebook_key' => $ebook_key, 'action' => 'thank_you'),get_permalink($checkoutPage)); |
| 11540 |
header("Location: $link"); |
| 11541 |
} |
| 11542 |
if ($silent) { |
| 11543 |
return $post_id; |
| 11544 |
} |
| 11545 |
//error_log('ebook_email_delivery added to plugins_loaded'); |
| 11546 |
} |
| 11547 |
} |
| 11548 |
|
| 11549 |
|
| 11550 |
|
| 11551 |
|
| 11552 |
|
| 11553 |
function erl($var) { |
| 11554 |
if (is_array($var)) { |
| 11555 |
$varOut = print_r($varOut,true); |
| 11556 |
} else { |
| 11557 |
$varOut = $var; |
| 11558 |
} |
| 11559 |
error_log(debug_backtrace()[1]['function']); |
| 11560 |
error_log($varOut); |
| 11561 |
} |
| 11562 |
|
| 11563 |
|
| 11564 |
function file_upload_max_size() { |
| 11565 |
static $max_size = -1; |
| 11566 |
|
| 11567 |
if ($max_size < 0) { |
| 11568 |
// Start with post_max_size. |
| 11569 |
$max_size = parse_size(ini_get('post_max_size')); |
| 11570 |
|
| 11571 |
// If upload_max_size is less, then reduce. Except if upload_max_size is |
| 11572 |
// zero, which indicates no limit. |
| 11573 |
$upload_max = parse_size(ini_get('upload_max_filesize')); |
| 11574 |
if ($upload_max > 0 && $upload_max < $max_size) { |
| 11575 |
$max_size = $upload_max; |
| 11576 |
} |
| 11577 |
} |
| 11578 |
return $max_size; |
| 11579 |
} |
| 11580 |
|
| 11581 |
function parse_size($size) { |
| 11582 |
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size. |
| 11583 |
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size. |
| 11584 |
if ($unit) { |
| 11585 |
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by. |
| 11586 |
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); |
| 11587 |
} |
| 11588 |
else { |
| 11589 |
return round($size); |
| 11590 |
} |
| 11591 |
} |
| 11592 |
|
| 11593 |
|
| 11594 |
function register_ebook_store_woocommerce_type() { |
| 11595 |
/** |
| 11596 |
* This should be in its own separate file. |
| 11597 |
*/ |
| 11598 |
// if (class_exists('WC_Product_Simple')) { |
| 11599 |
// class WC_Product_Ebook_Store extends WC_Product_Simple { |
| 11600 |
|
| 11601 |
// public function __construct( $product ) { |
| 11602 |
|
| 11603 |
// $this->product_type = 'ebook_store'; |
| 11604 |
|
| 11605 |
// parent::__construct( $product ); |
| 11606 |
|
| 11607 |
// } |
| 11608 |
// public function get_type() { |
| 11609 |
// return 'ebook_store'; |
| 11610 |
// } |
| 11611 |
|
| 11612 |
// } |
| 11613 |
// } |
| 11614 |
if (class_exists('WC_Product')) { |
| 11615 |
class WC_Product_ebookstore extends WC_Product { |
| 11616 |
|
| 11617 |
function __construct($product) { |
| 11618 |
$this->supports[] = 'ajax_add_to_cart'; |
| 11619 |
parent::__construct($product); |
| 11620 |
$this->product_type = 'ebookstore'; |
| 11621 |
} |
| 11622 |
|
| 11623 |
/** |
| 11624 |
* Method to read a product from the database. |
| 11625 |
* @param WC_Product |
| 11626 |
*/ |
| 11627 |
|
| 11628 |
public function get_product_type( $product_id ) { |
| 11629 |
return 'ebookstore'; |
| 11630 |
|
| 11631 |
} |
| 11632 |
public function get_type() { |
| 11633 |
return 'ebookstore'; |
| 11634 |
} |
| 11635 |
} |
| 11636 |
|
| 11637 |
|
| 11638 |
} |
| 11639 |
|
| 11640 |
} |
| 11641 |
function woocommerce_custom_product_tabs_for_ebook_store( $tabs) { |
| 11642 |
$tabs['ebookstore'] = array( |
| 11643 |
'label' => __( 'Ebook Store', 'woocommerce' ), |
| 11644 |
'target' => 'ebook_store_options', |
| 11645 |
'class' => array( 'show_if_simple', 'show_if_variable', 'show_if_ebookstore' ), |
| 11646 |
); |
| 11647 |
return $tabs; |
| 11648 |
} |
| 11649 |
|
| 11650 |
|
| 11651 |
|
| 11652 |
|
| 11653 |
function add_ebook_store_item( $types ){ |
| 11654 |
|
| 11655 |
// Key should be exactly the same as in the class product_type parameter |
| 11656 |
//$types[ 'ebookstore' ] = __( 'Ebook Store', 'ebook-store' ); |
| 11657 |
|
| 11658 |
return $types; |
| 11659 |
|
| 11660 |
} |
| 11661 |
add_filter( 'product_type_selector', 'add_ebook_store_item' ); |
| 11662 |
|
| 11663 |
function woocommerce_ebook_store_price_field() { |
| 11664 |
|
| 11665 |
if ( 'product' != get_post_type() ) : |
| 11666 |
//return; |
| 11667 |
endif; |
| 11668 |
|
| 11669 |
?><script type='text/javascript'> |
| 11670 |
jQuery( document ).ready( function() { |
| 11671 |
jQuery( '.options_group.pricing' ).addClass( 'show_if_ebook_store' ).show(); |
| 11672 |
jQuery( '.general_options' ).show(); |
| 11673 |
jQuery('label[for="_virtual"]').show().addClass('show_if_ebook_store'); |
| 11674 |
jQuery('label[for="_downloadable"]').show().addClass('show_if_ebook_store'); |
| 11675 |
|
| 11676 |
}); |
| 11677 |
jQuery('#product-type').on('change', function () { |
| 11678 |
jQuery('.general_options').show(); |
| 11679 |
setTimeout(function () { |
| 11680 |
jQuery('label[for="_virtual"], label[for="_downloadable"]').show(); |
| 11681 |
}, 500); |
| 11682 |
}); |
| 11683 |
|
| 11684 |
</script><?php |
| 11685 |
} |
| 11686 |
|
| 11687 |
|
| 11688 |
|
| 11689 |
/*-----------------------------------*/ |
| 11690 |
/** |
| 11691 |
* Add a custom product tab. |
| 11692 |
*/ |
| 11693 |
|
| 11694 |
/** |
| 11695 |
* Contents of the rental options product tab. |
| 11696 |
*/ |
| 11697 |
function ebook_store_woocommerce_tab_content() { |
| 11698 |
global $post; |
| 11699 |
$postOrig = $post; |
| 11700 |
?><div id='ebook_store_options' class='panel woocommerce_options_panel' style=""><?php |
| 11701 |
?><div class='options_group'> |
| 11702 |
<?php |
| 11703 |
if (get_option('ebook_store_woocommerce_integration') == 0) { |
| 11704 |
?> |
| 11705 |
<h4>To use this feature please get the full version of the Ebook Store plugin by clicking <a href="https://www.shopfiles.com/index.php/products/wordpress-ebook-store" target="_blank">here</a>, and enable WooCommerce integration in <a href="options-general.php?page=ebook_options.php&tab=WooCommerce">Settings > Ebook Store > WooCommerce</a>.</h4> |
| 11706 |
<?php |
| 11707 |
return true; |
| 11708 |
} |
| 11709 |
|
| 11710 |
$product = function_exists( 'wc_get_product' ) ? wc_get_product( $post->ID ) : null; |
| 11711 |
if ( $product && $product->is_type( 'variable' ) ) { |
| 11712 |
?> |
| 11713 |
<p class="form-field"> |
| 11714 |
<strong><?php esc_html_e( 'Variable product note:', 'ebook-store' ); ?></strong> |
| 11715 |
<?php esc_html_e( 'Assign the ebook on each variation below. Leaving a variation empty means that variation will not trigger Ebook Store delivery.', 'ebook-store' ); ?> |
| 11716 |
</p> |
| 11717 |
<?php |
| 11718 |
} |
| 11719 |
|
| 11720 |
woocommerce_wp_select(array( |
| 11721 |
'id' => 'ebook_store_book_id', |
| 11722 |
'label' => __( 'Select Ebook', 'woocommerce' ), |
| 11723 |
'desc_tip' => 'true', |
| 11724 |
'description' => __( 'The selected ebook will be delivered to your customer. Encryption and watermarking will be handled by ebook store plugin.' ), |
| 11725 |
'type' => 'text', |
| 11726 |
'value' => get_post_meta(get_the_ID(), 'ebook_store_book_id', true), |
| 11727 |
'options' => ebook_store_get_woocommerce_options(), |
| 11728 |
) ); |
| 11729 |
|
| 11730 |
$selected_ebook_id = (int) get_post_meta( get_the_ID(), 'ebook_store_book_id', true ); |
| 11731 |
if ( $selected_ebook_id > 0 ) { |
| 11732 |
$selected_ebook = get_post( $selected_ebook_id ); |
| 11733 |
if ( $selected_ebook && $selected_ebook->post_type === 'ebook' ) { |
| 11734 |
$ebook_meta = get_post_meta( $selected_ebook_id, 'ebook', true ); |
| 11735 |
$ebook_meta = is_array( $ebook_meta ) ? $ebook_meta : array(); |
| 11736 |
$ebook_cover = get_post_meta( $selected_ebook_id, 'ebook_wp_custom_attachment_cover', true ); |
| 11737 |
$ebook_cover = is_array( $ebook_cover ) ? $ebook_cover : array(); |
| 11738 |
$ebook_pdf = get_post_meta( $selected_ebook_id, 'ebook_wp_custom_attachment_pdf', true ); |
| 11739 |
$ebook_pdf = is_array( $ebook_pdf ) ? $ebook_pdf : array(); |
| 11740 |
$ebook_template = function_exists( 'ebook_store_resolve_form_template' ) ? ebook_store_resolve_form_template( isset( $ebook_meta['form_template'] ) ? $ebook_meta['form_template'] : '' ) : 'modern'; |
| 11741 |
$form_templates = function_exists( 'ebook_store_get_form_templates' ) ? ebook_store_get_form_templates() : array(); |
| 11742 |
$template_label = isset( $form_templates[ $ebook_template ]['label'] ) ? $form_templates[ $ebook_template ]['label'] : ucfirst( $ebook_template ); |
| 11743 |
$delivery_mode = isset( $ebook_meta['donate_or_download'] ) ? $ebook_meta['donate_or_download'] : 'paid'; |
| 11744 |
$delivery_label = __( 'Paid download', 'ebook-store' ); |
| 11745 |
if ( $delivery_mode === 'free' ) { |
| 11746 |
$delivery_label = __( 'Free download', 'ebook-store' ); |
| 11747 |
} elseif ( $delivery_mode === 'donate' ) { |
| 11748 |
$delivery_label = __( 'Donate to download', 'ebook-store' ); |
| 11749 |
} |
| 11750 |
$currency = isset( $ebook_meta['paypal_currency'] ) && $ebook_meta['paypal_currency'] !== '' ? $ebook_meta['paypal_currency'] : ebook_store_get_store_currency(); |
| 11751 |
$price = isset( $ebook_meta['ebook_price'] ) ? (float) $ebook_meta['ebook_price'] : 0.0; |
| 11752 |
$pages = isset( $ebook_meta['ebook_pages'] ) ? (int) $ebook_meta['ebook_pages'] : 0; |
| 11753 |
$cover_url = ! empty( $ebook_cover['url'] ) ? $ebook_cover['url'] : ''; |
| 11754 |
$pdf_name = ! empty( $ebook_pdf['file'] ) ? basename( $ebook_pdf['file'] ) : ''; |
| 11755 |
$edit_ebook_link = get_edit_post_link( $selected_ebook_id, 'raw' ); |
| 11756 |
?> |
| 11757 |
<div class="ebook-woocommerce-linked-ebook"> |
| 11758 |
<div class="ebook-woocommerce-linked-ebook__media"> |
| 11759 |
<div class="ebook-woocommerce-linked-ebook__cover<?php echo $cover_url ? ' has-image' : ' is-empty'; ?>"> |
| 11760 |
<?php if ( $cover_url ) : ?> |
| 11761 |
<img src="<?php echo esc_url( $cover_url ); ?>" alt="<?php echo esc_attr( get_the_title( $selected_ebook_id ) ); ?>" /> |
| 11762 |
<?php else : ?> |
| 11763 |
<span><?php esc_html_e( 'No cover', 'ebook-store' ); ?></span> |
| 11764 |
<?php endif; ?> |
| 11765 |
</div> |
| 11766 |
</div> |
| 11767 |
<div class="ebook-woocommerce-linked-ebook__content"> |
| 11768 |
<div class="ebook-woocommerce-linked-ebook__header"> |
| 11769 |
<div> |
| 11770 |
<h4><?php echo esc_html( get_the_title( $selected_ebook_id ) ); ?></h4> |
| 11771 |
<p><?php echo esc_html( wp_trim_words( wp_strip_all_tags( $selected_ebook->post_excerpt ? $selected_ebook->post_excerpt : $selected_ebook->post_content ), 24, '...' ) ); ?></p> |
| 11772 |
</div> |
| 11773 |
<?php if ( $edit_ebook_link ) : ?> |
| 11774 |
<a class="button button-secondary" href="<?php echo esc_url( $edit_ebook_link ); ?>"><?php esc_html_e( 'Edit ebook', 'ebook-store' ); ?></a> |
| 11775 |
<?php endif; ?> |
| 11776 |
</div> |
| 11777 |
<div class="ebook-woocommerce-linked-ebook__meta"> |
| 11778 |
<span><strong><?php esc_html_e( 'Status:', 'ebook-store' ); ?></strong> <?php echo esc_html( ucfirst( get_post_status( $selected_ebook_id ) ) ); ?></span> |
| 11779 |
<span><strong><?php esc_html_e( 'Price:', 'ebook-store' ); ?></strong> <?php echo esc_html( ebook_store_order_money_display( $price, $currency ) ); ?></span> |
| 11780 |
<span><strong><?php esc_html_e( 'Delivery:', 'ebook-store' ); ?></strong> <?php echo esc_html( $delivery_label ); ?></span> |
| 11781 |
<span><strong><?php esc_html_e( 'Template:', 'ebook-store' ); ?></strong> <?php echo esc_html( $template_label ); ?></span> |
| 11782 |
<?php if ( $pages > 0 ) : ?> |
| 11783 |
<span><strong><?php esc_html_e( 'Pages:', 'ebook-store' ); ?></strong> <?php echo esc_html( number_format_i18n( $pages ) ); ?></span> |
| 11784 |
<?php endif; ?> |
| 11785 |
<?php if ( $pdf_name !== '' ) : ?> |
| 11786 |
<span><strong><?php esc_html_e( 'File:', 'ebook-store' ); ?></strong> <?php echo esc_html( $pdf_name ); ?></span> |
| 11787 |
<?php endif; ?> |
| 11788 |
</div> |
| 11789 |
<p class="description"><?php esc_html_e( 'This is the ebook that Ebook Store will protect and deliver when this WooCommerce product is purchased.', 'ebook-store' ); ?></p> |
| 11790 |
</div> |
| 11791 |
</div> |
| 11792 |
<?php |
| 11793 |
} |
| 11794 |
} |
| 11795 |
|
| 11796 |
?></div> |
| 11797 |
|
| 11798 |
</div><?php |
| 11799 |
$post = $postOrig; |
| 11800 |
} |
| 11801 |
/** |
| 11802 |
* Save the custom fields. |
| 11803 |
*/ |
| 11804 |
function save_woocommerce_ebook_store_data( $post_id ) { |
| 11805 |
|
| 11806 |
$ebook_store_ebook_id = isset( $_POST['_enable_ebook_store_option'] ) ? 'yes' : 'no'; |
| 11807 |
update_post_meta( $post_id, '_enable_ebook_store_option', $ebook_store_ebook_id ); |
| 11808 |
|
| 11809 |
if ( isset( $_POST['ebook_store_book_id'] ) ) { |
| 11810 |
//first reset the old ebook store item to 0 in case id of the assigned woocommerce prduct is changed |
| 11811 |
$ebook_store_book_id = get_post_meta($post_id, 'ebook_store_book_id', true); |
| 11812 |
update_post_meta( $ebook_store_book_id, 'woocommerce_product_id', 0 ); |
| 11813 |
//then update to the currently set it. |
| 11814 |
$ebook_store_book_id = sanitize_text_field( $_POST['ebook_store_book_id'] ); |
| 11815 |
update_post_meta( $post_id, 'ebook_store_book_id', $ebook_store_book_id ); |
| 11816 |
update_post_meta( $ebook_store_book_id, 'woocommerce_product_id', $post_id ); |
| 11817 |
//wp_die('woocommerce_product_id - ' . get_post_meta($ebook_store_book_id, 'woocommerce_product_id', true)); |
| 11818 |
} |
| 11819 |
|
| 11820 |
} |
| 11821 |
function ebook_store_get_woocommerce_options() { |
| 11822 |
$new = new WP_Query( |
| 11823 |
array( |
| 11824 |
'posts_per_page' => -1, |
| 11825 |
'post_type' => 'ebook', |
| 11826 |
'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ), |
| 11827 |
'orderby' => 'title', |
| 11828 |
'order' => 'ASC', |
| 11829 |
'no_found_rows' => true, |
| 11830 |
) |
| 11831 |
); |
| 11832 |
$ebooks = array(); |
| 11833 |
$ebooks[0] = '-- Please select --'; |
| 11834 |
while ($new->have_posts()) : $new->the_post(); |
| 11835 |
$ebook_title = get_the_title(get_the_ID()); |
| 11836 |
if ($ebook_title == '') { |
| 11837 |
$ebook_title = '(no title)'; |
| 11838 |
} |
| 11839 |
$ebook_status = get_post_status( get_the_ID() ); |
| 11840 |
if ( $ebook_status && $ebook_status !== 'publish' ) { |
| 11841 |
$ebook_title .= ' [' . ucfirst( $ebook_status ) . ']'; |
| 11842 |
} |
| 11843 |
$ebooks[get_the_ID()] = $ebook_title; |
| 11844 |
endwhile; |
| 11845 |
wp_reset_postdata(); |
| 11846 |
|
| 11847 |
return $ebooks; |
| 11848 |
} |
| 11849 |
|
| 11850 |
function ebook_store_woocommerce_email_delivery_pending($order_id) { |
| 11851 |
if ('pending' != esc_attr(get_option('ebook_store_woocommerce_required_order_status'))) return; |
| 11852 |
ebook_store_woocommerce_email_delivery($order_id); |
| 11853 |
} |
| 11854 |
|
| 11855 |
function ebook_store_woocommerce_email_delivery_processing($order_id) { |
| 11856 |
if ('processing' != esc_attr(get_option('ebook_store_woocommerce_required_order_status'))) return; |
| 11857 |
ebook_store_woocommerce_email_delivery($order_id); |
| 11858 |
} |
| 11859 |
|
| 11860 |
function ebook_store_woocommerce_email_delivery_on_hold($order_id) { |
| 11861 |
if ('on-hold' != esc_attr(get_option('ebook_store_woocommerce_required_order_status'))) return; |
| 11862 |
ebook_store_woocommerce_email_delivery($order_id); |
| 11863 |
} |
| 11864 |
|
| 11865 |
function ebook_store_woocommerce_email_delivery_cancelled($order_id) { |
| 11866 |
if ('cancelled' != esc_attr(get_option('ebook_store_woocommerce_required_order_status'))) return; |
| 11867 |
ebook_store_woocommerce_email_delivery($order_id); |
| 11868 |
} |
| 11869 |
function ebook_store_woocommerce_email_delivery_completed($order_id) { |
| 11870 |
if ('completed' != esc_attr(get_option('ebook_store_woocommerce_required_order_status'))) return; |
| 11871 |
ebook_store_woocommerce_email_delivery($order_id); |
| 11872 |
} |
| 11873 |
|
| 11874 |
function ebook_store_woocommerce_email_delivery($order_id = 0, $event = 'completed' ) { |
| 11875 |
|
| 11876 |
if (is_numeric($order_status)) { //fix for using the order completed status hook |
| 11877 |
$order_id = $order_status; |
| 11878 |
$order_status = 'completed'; |
| 11879 |
} |
| 11880 |
global $ebook_email_delivery; |
| 11881 |
// erl('triggered completed'); |
| 11882 |
//error_reporting(E_ALL); |
| 11883 |
$ebook_email_delivery = array(); |
| 11884 |
$order = new WC_Order( $order_id ); |
| 11885 |
$order_items = $order->get_items(); |
| 11886 |
$order->address = $order->get_address(); |
| 11887 |
// error_log('WC ORDER' . print_r($order, true)); |
| 11888 |
|
| 11889 |
foreach ($order_items as $id => $item) { |
| 11890 |
// error_log(print_r($item,true)); |
| 11891 |
$item_mapping = ebook_store_get_wc_item_ebook_mapping( $item ); |
| 11892 |
$ebook_store_book_id = isset( $item_mapping['ebook_id'] ) ? (int) $item_mapping['ebook_id'] : 0; |
| 11893 |
if ($ebook_store_book_id == 0) { |
| 11894 |
continue; |
| 11895 |
} |
| 11896 |
$email_template = ebook_store_get_effective_delivery_email_template( $ebook_store_book_id ); |
| 11897 |
$ebook_email_delivery = array( |
| 11898 |
'to' => $order->address['email'], |
| 11899 |
'subject' => $email_template['subject'], |
| 11900 |
'text' => $email_template['text'], |
| 11901 |
'source' => 'woocommerce', |
| 11902 |
'related_order_type' => 'shop_order', |
| 11903 |
'woocommerce_event' => sanitize_key( (string) $event ), |
| 11904 |
); |
| 11905 |
//create new order array to make email delivery work |
| 11906 |
$ebook = new EbookStoreEbook($ebook_store_book_id); |
| 11907 |
$ebook->order_id = $order_id; |
| 11908 |
$ebook->wc_order = ebook_store_get_wc_order_key($order_id); // HPOS-safe; raw post meta is empty when orders live in the orders table. |
| 11909 |
$taxes = $order->get_taxes(); |
| 11910 |
|
| 11911 |
// error_log('taxes' . print_r($taxes,true)); |
| 11912 |
// foreach ($taxes as $tax) { |
| 11913 |
// $total_taxes = $tax->get_tax_total(); |
| 11914 |
// error_log('total_taxes' . print_r($total_taxes,true)); |
| 11915 |
// //break; |
| 11916 |
// } |
| 11917 |
$vat = ebook_store_get_vat_percent(); |
| 11918 |
if ($vat > 0) { |
| 11919 |
$tax = $ebook->price * ($vat / 100); |
| 11920 |
} else { |
| 11921 |
$tax = 0; |
| 11922 |
} |
| 11923 |
$md5_nonce = md5(microtime() . mt_rand(1,99999999) . NONCE_KEY); |
| 11924 |
$ebook_key = md5($md5_nonce); |
| 11925 |
update_post_meta( $order_id, 'md5_nonce', $md5_nonce ); |
| 11926 |
update_post_meta( $order_id, 'ebook_key', $ebook_key ); |
| 11927 |
|
| 11928 |
$ebook_email_delivery['order'] = array( |
| 11929 |
'first_name' => $order->address['first_name'], |
| 11930 |
'last_name' => $order->address['last_name'], |
| 11931 |
'mc_gross' => $item['line_subtotal'], |
| 11932 |
'ebook' => $ebook_store_book_id, |
| 11933 |
'payer_email' => $order->address['email'], |
| 11934 |
'md5_nonce' => $md5_nonce, |
| 11935 |
'ebook_key' => $ebook_key, |
| 11936 |
'mc_fee' => '', |
| 11937 |
'item_name' => get_the_title($ebook_store_book_id), |
| 11938 |
'payment_date' => $order->get_date_created()->__toString(), |
| 11939 |
'mc_currency' => $order->get_currency(), |
| 11940 |
'residence_country' => $order->address['country'], |
| 11941 |
'order_id' => $order_id, |
| 11942 |
'password' => $ebook->password(), |
| 11943 |
'txn_id' => $order->get_transaction_id(), |
| 11944 |
'downloadlink' => $ebook->format_links(), |
| 11945 |
'downloadlink_html' => $ebook->format_links(), |
| 11946 |
'downloadlinks' => $ebook->format_links(), |
| 11947 |
'tax' => $tax, |
| 11948 |
'ip' => $_SERVER['REMOTE_ADDR'], |
| 11949 |
); |
| 11950 |
|
| 11951 |
|
| 11952 |
$ebook_email_delivery['attachment'][0]['file'] = $ebook->file; |
| 11953 |
$ebook_email_delivery['attachment'][0]['file'] = $ebook_email_delivery['attachment'][0]['file']['file']; |
| 11954 |
|
| 11955 |
@$fileExt = pathinfo($ebook_email_delivery['attachment'][0]['file'],PATHINFO_EXTENSION); |
| 11956 |
|
| 11957 |
//wp_mail('deian@motov.net','test','test'); |
| 11958 |
if ($fileExt == 'pdf' && get_option('encrypt_pdf')) { |
| 11959 |
$ebook_email_delivery['attachment'][0]['file'] = ebook_encrypt_pdf($ebook_email_delivery['order']); |
| 11960 |
} |
| 11961 |
if ( $fileExt == 'epub' && get_option( 'ebook_store_epub_watermark' ) ) { |
| 11962 |
$ebook_email_delivery['attachment'][0]['file'] = $ebook->encrypted( $order_id, 'epub' ); |
| 11963 |
} |
| 11964 |
|
| 11965 |
$ebook_store_woocommerce_required_order_status = esc_attr(get_option('ebook_store_woocommerce_required_order_status', 0)); |
| 11966 |
// if ($order_status != $ebook_store_woocommerce_required_order_status && $ebook_store_woocommerce_required_order_status != '0') { |
| 11967 |
// error_log('ORDER STATUS ' . $ebook_store_woocommerce_required_order_status . ' VS ' . $order_status); |
| 11968 |
// erl('will return now'); |
| 11969 |
// return $order_status; |
| 11970 |
// } |
| 11971 |
|
| 11972 |
ebook_email_delivery(); |
| 11973 |
// error_log('ebook id ' . $ebook_store_book_id); |
| 11974 |
# code... |
| 11975 |
} |
| 11976 |
// if ( 'processing' == $order_status && ( 'on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status ) ) { |
| 11977 |
// return 'completed'; |
| 11978 |
// } |
| 11979 |
return true; |
| 11980 |
} |
| 11981 |
|
| 11982 |
function ebook_store_woocommerce_order_details($order, $skipHeaders = false){ |
| 11983 |
$order_items = $order->get_items(); |
| 11984 |
$order_status = $order->get_status(); |
| 11985 |
$order_id = 0; |
| 11986 |
if (is_object($order)) { |
| 11987 |
if (method_exists($order, 'get_id')) { |
| 11988 |
$order_id = $order->get_id(); |
| 11989 |
} elseif (isset($order->id)) { |
| 11990 |
$order_id = $order->id; |
| 11991 |
} |
| 11992 |
} |
| 11993 |
$ebook_store_woocommerce_required_order_status = esc_attr(get_option('ebook_store_woocommerce_required_order_status', 0)); |
| 11994 |
if ($order_status != $ebook_store_woocommerce_required_order_status && $ebook_store_woocommerce_required_order_status != '0') { |
| 11995 |
return true; |
| 11996 |
} |
| 11997 |
if ($skipHeaders == false) { |
| 11998 |
@$out .= "<h2>". __('Downloads', 'ebook-store') . "</h2>"; |
| 11999 |
@$out .= '<table class="shop_table order_downloads_ebook_store"> |
| 12000 |
<thead> |
| 12001 |
<tr> |
| 12002 |
<th class="product-name">'. __('Ebook (digital copy)', 'ebook-store') . '</th> |
| 12003 |
<th class="product-total">'. __('Download', 'ebook-store') . '</th> |
| 12004 |
</tr> |
| 12005 |
</thead> |
| 12006 |
<tbody>'; |
| 12007 |
|
| 12008 |
} |
| 12009 |
// echo "<pre>"; |
| 12010 |
// print_r($order_items); |
| 12011 |
// echo "<pre>"; |
| 12012 |
foreach ($order_items as $item_id => $item) { |
| 12013 |
$item_mapping = ebook_store_get_wc_item_ebook_mapping( $item ); |
| 12014 |
$woocommerce_product_id = $item->get_product_id(); |
| 12015 |
$product = $item->get_product(); |
| 12016 |
$title = $product->get_title(); |
| 12017 |
|
| 12018 |
//wp_die("ID is $woocommerce_product_id"); |
| 12019 |
//$woocommerce_product_id = 100; |
| 12020 |
$ebook_store_book_id = isset( $item_mapping['ebook_id'] ) ? (int) $item_mapping['ebook_id'] : 0; |
| 12021 |
|
| 12022 |
if ($ebook_store_book_id != 0) { |
| 12023 |
@$ebooks++; |
| 12024 |
} |
| 12025 |
|
| 12026 |
$ebook = new EbookStoreEbook($ebook_store_book_id); |
| 12027 |
$ebook->order_id = $order_id; |
| 12028 |
$ebook->wc_order = ebook_store_get_wc_order_key($order_id); // HPOS-safe; raw post meta is empty when orders live in the orders table. |
| 12029 |
$cell = '<span class="amount">' . $ebook->format_links() . '</span><br /><small>' . $ebook->password_desc() . '</small>'; |
| 12030 |
if (get_option( 'ebook_store_woocommerce_pdf_reader', false )) { |
| 12031 |
$cell = ebook_pdf_reader(false, $ebook_store_book_id); |
| 12032 |
} |
| 12033 |
|
| 12034 |
if ($ebook_store_book_id > 0) { |
| 12035 |
@$out .= ' |
| 12036 |
<tr class="order_item"> |
| 12037 |
<td class="product-name"> |
| 12038 |
' . $title . '</td> |
| 12039 |
<td class="product-total"> |
| 12040 |
' . $cell . ' |
| 12041 |
</td> |
| 12042 |
</tr>'; |
| 12043 |
|
| 12044 |
} |
| 12045 |
} |
| 12046 |
if ($skipHeaders == false) { |
| 12047 |
@$out .= ' |
| 12048 |
</tbody> |
| 12049 |
</table>'; |
| 12050 |
} |
| 12051 |
|
| 12052 |
if (@$ebooks > 0) { |
| 12053 |
echo $out; |
| 12054 |
} |
| 12055 |
|
| 12056 |
// echo '<pre>' . print_r($order,true) . '</pre>'; |
| 12057 |
// echo '<pre>' . print_r($ord,true) . '</pre>'; |
| 12058 |
|
| 12059 |
// echo '<p><strong>'.__('Pickup Location').':</strong> ' . get_post_meta( $order->id, 'ebook_store_book_id', true ). '</p>'; |
| 12060 |
// echo '<p><strong>'.__('Pickup Date').':</strong> ' . get_post_meta( $order->id, 'ebook_store_book_id', true ). '</p>'; |
| 12061 |
} |
| 12062 |
/** |
| 12063 |
* The order key stored against a WooCommerce order. |
| 12064 |
* |
| 12065 |
* Reads through the WooCommerce CRUD layer so the lookup keeps working under |
| 12066 |
* High-Performance Order Storage, where orders no longer live in wp_posts and |
| 12067 |
* get_post_meta() returns nothing. |
| 12068 |
* |
| 12069 |
* @param int $order_id WooCommerce order ID. |
| 12070 |
* @return string Order key, or '' when the order does not exist. |
| 12071 |
*/ |
| 12072 |
function ebook_store_get_wc_order_key( $order_id ) { |
| 12073 |
$order_id = absint( $order_id ); |
| 12074 |
if ( $order_id < 1 ) { |
| 12075 |
return ''; |
| 12076 |
} |
| 12077 |
|
| 12078 |
if ( function_exists( 'wc_get_order' ) ) { |
| 12079 |
$order = wc_get_order( $order_id ); |
| 12080 |
if ( $order ) { |
| 12081 |
return (string) $order->get_order_key(); |
| 12082 |
} |
| 12083 |
|
| 12084 |
return ''; |
| 12085 |
} |
| 12086 |
|
| 12087 |
return (string) get_post_meta( $order_id, '_order_key', true ); |
| 12088 |
} |
| 12089 |
|
| 12090 |
/** |
| 12091 |
* Does the supplied key authorise access to this WooCommerce order? |
| 12092 |
* |
| 12093 |
* @param int $order_id WooCommerce order ID. |
| 12094 |
* @param string $order_key Key supplied in the request. |
| 12095 |
* @return bool |
| 12096 |
*/ |
| 12097 |
function ebook_store_wc_order_key_matches( $order_id, $order_key ) { |
| 12098 |
$order_key = sanitize_text_field( (string) $order_key ); |
| 12099 |
if ( $order_key === '' ) { |
| 12100 |
return false; |
| 12101 |
} |
| 12102 |
|
| 12103 |
$stored = ebook_store_get_wc_order_key( $order_id ); |
| 12104 |
|
| 12105 |
return $stored !== '' && hash_equals( $stored, $order_key ); |
| 12106 |
} |
| 12107 |
|
| 12108 |
/** |
| 12109 |
* Is this ebook actually one of the items in that WooCommerce order? |
| 12110 |
* |
| 12111 |
* Without this check a valid order key for a $1 book would unlock every other |
| 12112 |
* ebook in the catalogue. |
| 12113 |
* |
| 12114 |
* @param int $order_id WooCommerce order ID. |
| 12115 |
* @param int $ebook_id Ebook post ID. |
| 12116 |
* @return bool |
| 12117 |
*/ |
| 12118 |
function ebook_store_wc_order_contains_ebook( $order_id, $ebook_id ) { |
| 12119 |
$ebook_id = absint( $ebook_id ); |
| 12120 |
if ( $ebook_id < 1 ) { |
| 12121 |
return false; |
| 12122 |
} |
| 12123 |
|
| 12124 |
if ( ! function_exists( 'wc_get_order' ) ) { |
| 12125 |
// Without WooCommerce there is nothing to cross-check against; the order |
| 12126 |
// key match above is the only gate. |
| 12127 |
return true; |
| 12128 |
} |
| 12129 |
|
| 12130 |
$order = wc_get_order( absint( $order_id ) ); |
| 12131 |
if ( ! $order ) { |
| 12132 |
return false; |
| 12133 |
} |
| 12134 |
|
| 12135 |
foreach ( $order->get_items() as $item ) { |
| 12136 |
$product_id = (int) $item->get_product_id(); |
| 12137 |
$variation_id = (int) $item->get_variation_id(); |
| 12138 |
|
| 12139 |
foreach ( array( $variation_id, $product_id ) as $candidate ) { |
| 12140 |
if ( $candidate < 1 ) { |
| 12141 |
continue; |
| 12142 |
} |
| 12143 |
if ( (int) get_post_meta( $candidate, 'ebook_store_book_id', true ) === $ebook_id ) { |
| 12144 |
return true; |
| 12145 |
} |
| 12146 |
} |
| 12147 |
} |
| 12148 |
|
| 12149 |
return (bool) apply_filters( 'ebook_store_wc_order_contains_ebook', false, $order_id, $ebook_id ); |
| 12150 |
} |
| 12151 |
|
| 12152 |
function ebook_process_download_woocomerce() { |
| 12153 |
if ( ! isset( $_GET['action'] ) || $_GET['action'] !== 'wc_order_download' ) { |
| 12154 |
return false; |
| 12155 |
} |
| 12156 |
|
| 12157 |
// ebook_process_download() now performs the order-key and order-contents |
| 12158 |
// checks itself, so this wrapper only forwards the request. |
| 12159 |
ebook_process_download( isset( $_GET['wc_order'] ) ? sanitize_text_field( wp_unslash( $_GET['wc_order'] ) ) : '' ); |
| 12160 |
die(); |
| 12161 |
} |
| 12162 |
|
| 12163 |
function ebook_store_add_to_cart() { |
| 12164 |
include_once( plugin_dir_path( EBOOK_STORE_PLUGIN_FILE ) . 'locale.php' ); |
| 12165 |
$locale = ebook_store_get_locale_strings(); |
| 12166 |
$formats_locale = ebook_store_get_format_labels(); |
| 12167 |
|
| 12168 |
if ($_GET['woocommerce_product_id'] > 0) { |
| 12169 |
global $woocommerce; |
| 12170 |
$woocommerce_product_id = (int)$_GET['woocommerce_product_id']; |
| 12171 |
$cart_config = ebook_store_get_wc_add_to_cart_config( $woocommerce_product_id ); |
| 12172 |
if ( ! $cart_config ) { |
| 12173 |
return; |
| 12174 |
} |
| 12175 |
|
| 12176 |
if ( ! ebook_store_cart_contains_wc_product( $cart_config ) ) { |
| 12177 |
WC()->cart->add_to_cart( |
| 12178 |
$cart_config['product_id'], |
| 12179 |
1, |
| 12180 |
$cart_config['variation_id'], |
| 12181 |
$cart_config['variation'] |
| 12182 |
); |
| 12183 |
} |
| 12184 |
|
| 12185 |
global $ebook_store_messages; |
| 12186 |
|
| 12187 |
if (get_option('ebook_store_woocommerce_integration_no_added_to_cart') < 1) { |
| 12188 |
$ebook_store_messages[$woocommerce_product_id] = '<div class="ebook-store-alert ebook-store-alert-success">"' . esc_html( $cart_config['title'] ) . '" ' . $locale['addedToCart'] . '. <a href="' . wc_get_cart_url() . '" class="ebook_store_button">View Cart</a></div>'; |
| 12189 |
} |
| 12190 |
} |
| 12191 |
} |
| 12192 |
|
| 12193 |
|
| 12194 |
|
| 12195 |
function ebook_store_action_links ( $links ) { |
| 12196 |
$mylinks = array( |
| 12197 |
'<a href="' . admin_url( 'options-general.php?page=ebook_options.php' ) . '">Settings</a>', |
| 12198 |
'<a href="https://www.youtube.com/watch?v=T0mrdvrbmbc&list=PL9hp_4MJ0h1sDTf05H6aguzlYqcg47QJ-" target="_blank">Video Tutorials</a>', |
| 12199 |
'<a href="https://shopfiles.com/index.php/products/wordpress-ebook-store" target="_blank">Upgrade</a>', |
| 12200 |
); |
| 12201 |
return array_merge( $links, $mylinks ); |
| 12202 |
} |
| 12203 |
|
| 12204 |
function ebook_store_payment_completed_wpam($ipn_data, $order_id) |
| 12205 |
{ |
| 12206 |
$custom_data = $ipn_data['custom']; |
| 12207 |
WPAM_Logger::log_debug('Ebook Store for WordPress Integration - payment completed hook fired. Custom field value: '.$custom_data); |
| 12208 |
$custom_values = array(); |
| 12209 |
parse_str($custom_data, $custom_values); |
| 12210 |
if(isset($custom_values['wpam_tracking']) && !empty($custom_values['wpam_tracking'])) |
| 12211 |
{ |
| 12212 |
$tracking_value = $custom_values['wpam_tracking']; |
| 12213 |
WPAM_Logger::log_debug('Ebook Store for WordPress Integration - Tracking data present. Need to track affiliate commission. Tracking value: '.$tracking_value); |
| 12214 |
$purchaseLogId = $ipn_data['txn_id']; |
| 12215 |
$purchaseAmount = $ipn_data['mc_gross']; //TODO - later calculate sub-total only |
| 12216 |
$strRefKey = $tracking_value; |
| 12217 |
$requestTracker = new WPAM_Tracking_RequestTracker(); |
| 12218 |
$requestTracker->handleCheckoutWithRefKey( $purchaseLogId, $purchaseAmount, $strRefKey); |
| 12219 |
WPAM_Logger::log_debug('Ebook Store for WordPress Integration - Commission tracked for transaction ID: '.$purchaseLogId.'. Purchase amt: '.$purchaseAmount); |
| 12220 |
} |
| 12221 |
} |
| 12222 |
|
| 12223 |
function ebook_store_payment_gateway_parameters_wpam($parameters) |
| 12224 |
{ |
| 12225 |
if (class_exists('WPAM_PluginConfig') == false) { |
| 12226 |
return; |
| 12227 |
} |
| 12228 |
if(isset($_COOKIE['wpam_id'])) |
| 12229 |
{ |
| 12230 |
$name = 'wpam_tracking'; |
| 12231 |
$value = $_COOKIE['wpam_id']; |
| 12232 |
$new_val = $name.'='.$value; |
| 12233 |
$current_val = $parameters['custom']; |
| 12234 |
if(empty($current_val)){ |
| 12235 |
$parameters['custom'] = $new_val; |
| 12236 |
} |
| 12237 |
else{ |
| 12238 |
$parameters['custom'] = $current_val.'&'.$new_val; |
| 12239 |
} |
| 12240 |
WPAM_Logger::log_debug('Ebook Store for WordPress Integration - Adding custom field value. New value: '.$parameters['custom']); |
| 12241 |
} |
| 12242 |
|
| 12243 |
else if(isset($_COOKIE[WPAM_PluginConfig::$RefKey])) |
| 12244 |
{ |
| 12245 |
$name = 'wpam_tracking'; |
| 12246 |
$value = $_COOKIE[WPAM_PluginConfig::$RefKey]; |
| 12247 |
$new_val = $name.'='.$value; |
| 12248 |
$current_val = $parameters['custom']; |
| 12249 |
if(empty($current_val)){ |
| 12250 |
$parameters['custom'] = $new_val; |
| 12251 |
} |
| 12252 |
else{ |
| 12253 |
$parameters['custom'] = $current_val.'&'.$new_val; |
| 12254 |
} |
| 12255 |
WPAM_Logger::log_debug('Ebook Store for WordPress Integration - Adding custom field value. New value: '.$parameters['custom']); |
| 12256 |
} |
| 12257 |
|
| 12258 |
return $parameters; |
| 12259 |
} |
| 12260 |
|
| 12261 |
function ebook_store_my_taxonomies_product() { |
| 12262 |
$labels = array( |
| 12263 |
'name' => _x( 'Ebook Categories', 'Ebook Categories', 'ebook-store' ), |
| 12264 |
'singular_name' => _x( 'Ebook Category', 'Ebook Category', 'ebook-store' ), |
| 12265 |
'search_items' => __( 'Search Ebook Categories', 'ebook-store' ), |
| 12266 |
'all_items' => __( 'All Ebook Categories', 'ebook-store' ), |
| 12267 |
'parent_item' => __( 'Parent Ebook Category', 'ebook-store' ), |
| 12268 |
'parent_item_colon' => __( 'Parent Ebook Category:', 'ebook-store' ), |
| 12269 |
'edit_item' => __( 'Edit Ebook Category', 'ebook-store' ), |
| 12270 |
'update_item' => __( 'Update Ebook Category', 'ebook-store' ), |
| 12271 |
'add_new_item' => __( 'Add New Ebook Category', 'ebook-store' ), |
| 12272 |
'new_item_name' => __( 'New Ebook Category', 'ebook-store' ), |
| 12273 |
'menu_name' => __( 'Ebook Categories', 'ebook-store' ), |
| 12274 |
); |
| 12275 |
$args = array( |
| 12276 |
'labels' => $labels, |
| 12277 |
'hierarchical' => true, |
| 12278 |
); |
| 12279 |
register_taxonomy( 'ebook_category', 'ebook', $args ); |
| 12280 |
} |
| 12281 |
|
| 12282 |
function ebook_store_vc_before_init_actions() { |
| 12283 |
class vcInfoBox extends WPBakeryShortCode { |
| 12284 |
|
| 12285 |
// Element Init |
| 12286 |
function __construct() { |
| 12287 |
add_action( 'init', array( $this, 'vc_infobox_mapping' ) ); |
| 12288 |
add_shortcode( 'vc_infobox', array( $this, 'vc_infobox_html' ) ); |
| 12289 |
} |
| 12290 |
|
| 12291 |
// Element Mapping |
| 12292 |
public function vc_infobox_mapping() { |
| 12293 |
|
| 12294 |
// Stop all if VC is not enabled |
| 12295 |
if ( !defined( 'WPB_VC_VERSION' ) ) { |
| 12296 |
return; |
| 12297 |
} |
| 12298 |
|
| 12299 |
// Map the block with vc_map() |
| 12300 |
vc_map( |
| 12301 |
array( |
| 12302 |
'name' => __('Ebook Order Form', 'text-domain'), |
| 12303 |
'base' => 'ebook_store', |
| 12304 |
'description' => __('Inserts shortcode that will call the Ebook Order form.', 'text-domain'), |
| 12305 |
'category' => __('Ebook Store', 'ebook-store'), |
| 12306 |
'icon' => 'dashicons-cart', //get_template_directory_uri().'/assets/img/vc-icon.png', |
| 12307 |
'params' => array( |
| 12308 |
|
| 12309 |
array( |
| 12310 |
'type' => 'dropdown', |
| 12311 |
'holder' => 'h3', |
| 12312 |
'class' => 'title-class', |
| 12313 |
'heading' => __( 'Select Ebook', 'text-domain' ), |
| 12314 |
'param_name' => 'ebook_id', |
| 12315 |
'value' => vc_ebook_store_array(), |
| 12316 |
'description' => __( 'You are embedding a direct order form for the Ebook via Ebook Store plugin.', 'text-domain' ), |
| 12317 |
'admin_label' => false, |
| 12318 |
'weight' => 0, |
| 12319 |
'group' => 'Ebook Store', |
| 12320 |
), |
| 12321 |
|
| 12322 |
|
| 12323 |
), |
| 12324 |
) |
| 12325 |
); |
| 12326 |
|
| 12327 |
} |
| 12328 |
|
| 12329 |
|
| 12330 |
// Element HTML |
| 12331 |
public function vc_infobox_html( $atts ) { |
| 12332 |
|
| 12333 |
// Params extraction |
| 12334 |
extract( |
| 12335 |
shortcode_atts( |
| 12336 |
array( |
| 12337 |
'title' => '', |
| 12338 |
'text' => '', |
| 12339 |
'ebook_id' => '', |
| 12340 |
), |
| 12341 |
$atts |
| 12342 |
) |
| 12343 |
); |
| 12344 |
|
| 12345 |
// Fill $html var with data |
| 12346 |
$html = ' |
| 12347 |
<div class="vc-infobox-wrap"> |
| 12348 |
|
| 12349 |
<h2 class="vc-infobox-title">' . $title . '</h2> |
| 12350 |
|
| 12351 |
<div class="vc-infobox-text"></div> |
| 12352 |
|
| 12353 |
|
| 12354 |
|
| 12355 |
</div>'; |
| 12356 |
|
| 12357 |
return $html; |
| 12358 |
|
| 12359 |
} |
| 12360 |
|
| 12361 |
} // End Element Class |
| 12362 |
|
| 12363 |
|
| 12364 |
// Element Class Init |
| 12365 |
new vcInfoBox(); |
| 12366 |
} |
| 12367 |
function vc_ebook_store_array() { |
| 12368 |
$args = array( |
| 12369 |
'post_type' => 'ebook', |
| 12370 |
); |
| 12371 |
$out = array(); |
| 12372 |
$query = new WP_Query($args); |
| 12373 |
while ($query->have_posts()) { |
| 12374 |
$query->the_post(); |
| 12375 |
$out[get_the_title()] = get_the_ID(); |
| 12376 |
} |
| 12377 |
return $out; |
| 12378 |
} |
| 12379 |
function ebook_store_order_reports_callback() { |
| 12380 |
include_once('ebook_store_order_reports_callback.php'); |
| 12381 |
} |
| 12382 |
/** |
| 12383 |
* Stream the orders list as a CSV download. |
| 12384 |
* |
| 12385 |
* The previous version loaded every order at once, called get_post_meta() per |
| 12386 |
* row, buffered the whole file in a PHP array and then die()'d with an implode. |
| 12387 |
* At 10k orders that peaked several hundred megabytes and never finished. It |
| 12388 |
* also took the column list from the FIRST row only, so every order with a |
| 12389 |
* different meta shape emitted an undefined-index warning per missing field. |
| 12390 |
* |
| 12391 |
* This walks the orders in pages of 200, writes straight to the output stream, |
| 12392 |
* and uses the union of all meta keys as the header row. |
| 12393 |
*/ |
| 12394 |
function ebook_store_export_orders() { |
| 12395 |
if ( ! is_admin() ) { |
| 12396 |
return; |
| 12397 |
} |
| 12398 |
|
| 12399 |
if ( ! isset( $_GET['export'] ) || (int) $_GET['export'] !== 1 ) { |
| 12400 |
return; |
| 12401 |
} |
| 12402 |
|
| 12403 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 12404 |
wp_die( esc_html__( 'You are not allowed to export orders.', 'ebook-store' ) ); |
| 12405 |
} |
| 12406 |
|
| 12407 |
check_admin_referer( 'ebook_store_export_orders' ); |
| 12408 |
|
| 12409 |
$selected_ebook_id = isset( $_GET['ebook_id'] ) ? absint( wp_unslash( $_GET['ebook_id'] ) ) : 0; |
| 12410 |
|
| 12411 |
$base_args = array( |
| 12412 |
'post_type' => 'ebook_order', |
| 12413 |
'post_status' => 'any', |
| 12414 |
'posts_per_page' => 200, |
| 12415 |
'fields' => 'ids', |
| 12416 |
'no_found_rows' => true, |
| 12417 |
'update_post_term_cache' => false, |
| 12418 |
'orderby' => 'ID', |
| 12419 |
'order' => 'ASC', |
| 12420 |
); |
| 12421 |
|
| 12422 |
if ( $selected_ebook_id > 0 ) { |
| 12423 |
$base_args['meta_key'] = 'ebook'; |
| 12424 |
$base_args['meta_value'] = $selected_ebook_id; |
| 12425 |
} |
| 12426 |
|
| 12427 |
// First pass: collect the union of meta keys so no row is missing a column. |
| 12428 |
$keys = array(); |
| 12429 |
$page = 1; |
| 12430 |
do { |
| 12431 |
$args = $base_args; |
| 12432 |
$args['paged'] = $page; |
| 12433 |
$ids = get_posts( $args ); |
| 12434 |
|
| 12435 |
foreach ( $ids as $id ) { |
| 12436 |
foreach ( array_keys( (array) get_post_meta( $id ) ) as $key ) { |
| 12437 |
$keys[ $key ] = true; |
| 12438 |
} |
| 12439 |
} |
| 12440 |
|
| 12441 |
$page++; |
| 12442 |
} while ( count( $ids ) === $base_args['posts_per_page'] ); |
| 12443 |
|
| 12444 |
if ( empty( $keys ) ) { |
| 12445 |
wp_die( esc_html__( 'There are no orders to export yet.', 'ebook-store' ) ); |
| 12446 |
} |
| 12447 |
|
| 12448 |
$keys = array_keys( $keys ); |
| 12449 |
sort( $keys ); |
| 12450 |
|
| 12451 |
nocache_headers(); |
| 12452 |
header( 'Content-Type: text/csv; charset=utf-8' ); |
| 12453 |
header( 'Content-Disposition: attachment; filename="ebook-store-orders-' . gmdate( 'Y-m-d' ) . '.csv"' ); |
| 12454 |
|
| 12455 |
$out = fopen( 'php://output', 'w' ); |
| 12456 |
|
| 12457 |
// A UTF-8 BOM so Excel opens accented buyer names correctly. |
| 12458 |
fwrite( $out, "\xEF\xBB\xBF" ); |
| 12459 |
fputcsv( $out, array_merge( array( 'order_id', 'order_date' ), $keys ) ); |
| 12460 |
|
| 12461 |
$page = 1; |
| 12462 |
do { |
| 12463 |
$args = $base_args; |
| 12464 |
$args['paged'] = $page; |
| 12465 |
$ids = get_posts( $args ); |
| 12466 |
|
| 12467 |
foreach ( $ids as $id ) { |
| 12468 |
$meta = (array) get_post_meta( $id ); |
| 12469 |
$row = array( $id, get_post_field( 'post_date_gmt', $id ) ); |
| 12470 |
|
| 12471 |
foreach ( $keys as $key ) { |
| 12472 |
$row[] = isset( $meta[ $key ][0] ) ? $meta[ $key ][0] : ''; |
| 12473 |
} |
| 12474 |
|
| 12475 |
fputcsv( $out, $row ); |
| 12476 |
} |
| 12477 |
|
| 12478 |
// Release the meta cache for the page we just wrote, otherwise the |
| 12479 |
// pagination saves nothing. |
| 12480 |
foreach ( $ids as $id ) { |
| 12481 |
wp_cache_delete( $id, 'post_meta' ); |
| 12482 |
} |
| 12483 |
|
| 12484 |
flush(); |
| 12485 |
$page++; |
| 12486 |
} while ( count( $ids ) === $base_args['posts_per_page'] ); |
| 12487 |
|
| 12488 |
fclose( $out ); |
| 12489 |
exit; |
| 12490 |
} |
| 12491 |
|
| 12492 |
function ebook_store_session_end() { |
| 12493 |
session_destroy (); |
| 12494 |
} |
| 12495 |
function ebook_store_encryptable($file) { |
| 12496 |
require_once __DIR__ . '/fpdi/bootstrap.php'; |
| 12497 |
try { |
| 12498 |
$pdf = new \setasign\Fpdi\Fpdi(); |
| 12499 |
$pdf->setSourceFile($file); |
| 12500 |
return true; |
| 12501 |
} catch (\Throwable $e) { |
| 12502 |
return false; |
| 12503 |
} |
| 12504 |
} |
| 12505 |
|
| 12506 |
function ebook_store_offer_tutorial() { |
| 12507 |
if (ebook_store_endsWith(esc_url_raw($_SERVER['REQUEST_URI']), 'edit.php?post_type=ebook')) { |
| 12508 |
$args = array( |
| 12509 |
'post_type' => 'ebook', |
| 12510 |
); |
| 12511 |
$myquery = new WP_Query($args); |
| 12512 |
if ($myquery->found_posts == 0) { |
| 12513 |
?> |
| 12514 |
<div class="updated"> |
| 12515 |
<p><?php _e( 'You have not created any ebooks yet, do you want to see the sample videos how to help you get started with it?<br /> |
| 12516 |
<a href="https://www.youtube.com/watch?v=HxYzlaEHPU4&list=PL9hp_4MJ0h1sDTf05H6aguzlYqcg47QJ-&index=1" target="_blank">Watch Tutorials on YouTube</a>', 'ebooks-store' ); ?></p> |
| 12517 |
</div> |
| 12518 |
<?php |
| 12519 |
} |
| 12520 |
} |
| 12521 |
} |
| 12522 |
function ebook_store_not_encryptable() { |
| 12523 |
$class = 'notice notice-error'; |
| 12524 |
$message = __( 'This file is can not be encrypted. Please save it in Adobe Acrobat 5.0 PDF 1.5 compatibility format. See video tutorial here: <a href="https://youtu.be/-yDr-76W4gk" target="_blank">https://youtu.be/-yDr-76W4gk</a>', 'ebook-store' ); |
| 12525 |
|
| 12526 |
printf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), $message ); |
| 12527 |
} |
| 12528 |
|
| 12529 |
function ebook_store_get_password_for_order($order_id = 0, $context = array()) { |
| 12530 |
static $runtime_password_cache = array(); |
| 12531 |
|
| 12532 |
$random_enabled = intval(get_option('ebook_store_random_password')) === 1; |
| 12533 |
$blank_enabled = intval(get_option('ebook_store_blank_password')) === 1; |
| 12534 |
|
| 12535 |
$cache_key = ''; |
| 12536 |
if ($order_id > 0) { |
| 12537 |
$cache_key = 'order_' . $order_id; |
| 12538 |
} elseif (!empty($context['md5_nonce'])) { |
| 12539 |
$cache_key = 'nonce_' . $context['md5_nonce']; |
| 12540 |
} elseif (!empty($context['ebook_key'])) { |
| 12541 |
$cache_key = 'ebook_key_' . $context['ebook_key']; |
| 12542 |
} |
| 12543 |
|
| 12544 |
if ($cache_key && isset($runtime_password_cache[$cache_key])) { |
| 12545 |
return $runtime_password_cache[$cache_key]; |
| 12546 |
} |
| 12547 |
|
| 12548 |
$stored_exists = ($order_id > 0 && function_exists('metadata_exists')) ? metadata_exists('post', $order_id, 'password') : false; |
| 12549 |
if ($stored_exists) { |
| 12550 |
$stored_password = get_post_meta($order_id,'password',true); |
| 12551 |
if ($cache_key) { |
| 12552 |
$runtime_password_cache[$cache_key] = $stored_password; |
| 12553 |
} |
| 12554 |
return $stored_password; |
| 12555 |
} |
| 12556 |
|
| 12557 |
$payer_email = isset($context['payer_email']) ? trim($context['payer_email']) : ''; |
| 12558 |
if (!$payer_email && $order_id > 0) { |
| 12559 |
$payer_email = get_post_meta($order_id,'payer_email',true); |
| 12560 |
} |
| 12561 |
$provided_password = isset($context['password']) ? (string)$context['password'] : ''; |
| 12562 |
$billing_email = isset($context['billing_email']) ? trim($context['billing_email']) : ''; |
| 12563 |
if ($billing_email === '' && $order_id > 0) { |
| 12564 |
$billing_email = get_post_meta($order_id,'_billing_email',true); |
| 12565 |
$billing_email = is_string($billing_email) ? trim($billing_email) : ''; |
| 12566 |
} |
| 12567 |
|
| 12568 |
if ($blank_enabled) { |
| 12569 |
$password = ''; |
| 12570 |
} elseif ($random_enabled) { |
| 12571 |
$password = $provided_password; |
| 12572 |
if ($password === '') { |
| 12573 |
$password = substr(md5(uniqid('ebook_pw_', true)), 0, 8); |
| 12574 |
} |
| 12575 |
} else { |
| 12576 |
$password = $provided_password ?: $payer_email; |
| 12577 |
if ($password === '') { |
| 12578 |
$password = $billing_email; |
| 12579 |
} |
| 12580 |
if ($password === '' && !empty($context['fallback_email'])) { |
| 12581 |
$password = $context['fallback_email']; |
| 12582 |
} |
| 12583 |
} |
| 12584 |
|
| 12585 |
if ($order_id > 0) { |
| 12586 |
update_post_meta($order_id,'password',$password); |
| 12587 |
} |
| 12588 |
|
| 12589 |
if ($cache_key) { |
| 12590 |
$runtime_password_cache[$cache_key] = $password; |
| 12591 |
} |
| 12592 |
|
| 12593 |
return $password; |
| 12594 |
} |
| 12595 |
|
| 12596 |
/** |
| 12597 |
* The order-detail block shared by the confirmation page and the delivery email. |
| 12598 |
* |
| 12599 |
* @param bool $with_vat Append the VAT figure to the total line. |
| 12600 |
* @return string |
| 12601 |
*/ |
| 12602 |
function ebook_store_default_order_details_block( $with_vat = false ) { |
| 12603 |
$total = $with_vat |
| 12604 |
/* translators: %%mc_currency%%, %%total%% and %%tax%% are merge codes — keep them exactly as they are. */ |
| 12605 |
? __( 'Total: %%mc_currency%% %%total%% (VAT %%tax%%)', 'ebook-store' ) |
| 12606 |
/* translators: %%mc_currency%% and %%total%% are merge codes — keep them exactly as they are. */ |
| 12607 |
: __( 'Total: %%mc_currency%% %%total%%', 'ebook-store' ); |
| 12608 |
|
| 12609 |
return '<h3>' . __( 'Details for your order:', 'ebook-store' ) . '</h3>' . "\n" |
| 12610 |
/* translators: %%order_id%% is a merge code — keep it exactly as it is. */ |
| 12611 |
. __( 'Order #: %%order_id%%', 'ebook-store' ) . "\n" |
| 12612 |
/* translators: %%txn_id%% is a merge code — keep it exactly as it is. */ |
| 12613 |
. __( 'Transaction: %%txn_id%%', 'ebook-store' ) . "\n" |
| 12614 |
/* translators: %%mc_currency%% and %%mc_gross%% are merge codes — keep them exactly as they are. */ |
| 12615 |
. __( 'Amount: %%mc_currency%% %%mc_gross%%', 'ebook-store' ) . "\n" |
| 12616 |
. $total . "\n\n" |
| 12617 |
/* translators: %%password%% is a merge code — keep it exactly as it is. */ |
| 12618 |
. '<strong>' . __( 'Your password is: %%password%%', 'ebook-store' ) . '</strong>' . "\n\n" |
| 12619 |
. __( 'Thank you!', 'ebook-store' ); |
| 12620 |
} |
| 12621 |
|
| 12622 |
/** |
| 12623 |
* Default content of the order confirmation ("thank you") page. |
| 12624 |
* |
| 12625 |
* This used to be a hardcoded English string on QSWPOptions, so a shop selling |
| 12626 |
* in German or Arabic showed its buyers an English confirmation page until |
| 12627 |
* somebody rewrote it by hand. Built from translated pieces instead, so a store |
| 12628 |
* gets the page in its own language out of the box. |
| 12629 |
* |
| 12630 |
* The %%...%% merge codes are substituted later and must survive translation. |
| 12631 |
* |
| 12632 |
* @return string |
| 12633 |
*/ |
| 12634 |
function ebook_store_default_thankyou_page() { |
| 12635 |
/* translators: %%first_name%% and %%last_name%% are merge codes — keep them exactly as they are. */ |
| 12636 |
return '<h1>' . __( 'Thank you for your order %%first_name%% %%last_name%%!', 'ebook-store' ) . '</h1>' . "\n" |
| 12637 |
. __( 'You have successfully completed the order process! Please use the link(s) below to download your copy:', 'ebook-store' ) . "\n\n" |
| 12638 |
/* translators: %%downloadlink_html%% is a merge code — keep it exactly as it is. */ |
| 12639 |
. '<strong>' . __( 'Use the link(s) to start the download:', 'ebook-store' ) . '</strong> %%downloadlink_html%%' . "\n\n" |
| 12640 |
/* translators: %%ebook_bonus%% is a merge code — keep it exactly as it is. */ |
| 12641 |
. __( 'Bonus ebook download links:', 'ebook-store' ) . ' %%ebook_bonus%%' . "\n" |
| 12642 |
. ebook_store_default_order_details_block( false ); |
| 12643 |
} |
| 12644 |
|
| 12645 |
/** |
| 12646 |
* Default body of the delivery email. |
| 12647 |
* |
| 12648 |
* Same reasoning as the confirmation page: it shipped in English only. |
| 12649 |
* |
| 12650 |
* @return string |
| 12651 |
*/ |
| 12652 |
function ebook_store_default_email_delivery_text() { |
| 12653 |
/* translators: %%first_name%% and %%last_name%% are merge codes — keep them exactly as they are. */ |
| 12654 |
return '<h1>' . __( 'Thank you for your order %%first_name%% %%last_name%%!', 'ebook-store' ) . '</h1>' . "\n" |
| 12655 |
. __( 'You have successfully completed the order process!', 'ebook-store' ) . "\n\n" |
| 12656 |
/* translators: %%downloadlink_html%% is a merge code — keep it exactly as it is. */ |
| 12657 |
. '<strong>' . __( 'Please use the link(s) below to download your copy:', 'ebook-store' ) . '</strong> %%downloadlink_html%%' . "\n\n" |
| 12658 |
/* translators: %%ebook_bonus%% is a merge code — keep it exactly as it is. */ |
| 12659 |
. __( 'Bonus ebooks:', 'ebook-store' ) . ' %%ebook_bonus%%' . "\n\n" |
| 12660 |
. ebook_store_default_order_details_block( true ); |
| 12661 |
} |
| 12662 |
|
| 12663 |
/** |
| 12664 |
* Default subject line of the delivery email. |
| 12665 |
* |
| 12666 |
* @return string |
| 12667 |
*/ |
| 12668 |
function ebook_store_default_email_delivery_subject() { |
| 12669 |
return __( 'Email Delivery - Order complete!', 'ebook-store' ); |
| 12670 |
} |
| 12671 |
|
| 12672 |
/** |
| 12673 |
* Mark the admin body when this site holds a valid Pro licence. |
| 12674 |
* |
| 12675 |
* The "Pro" badge is drawn by CSS on any element carrying .goPro2, and several |
| 12676 |
* of those classes are printed unconditionally — the delivery options, the bonus |
| 12677 |
* books picker, the importer. A paying customer was still shown a locked-looking |
| 12678 |
* screen covered in upsell badges. One body class lets the stylesheet stand every |
| 12679 |
* one of them down, including any added later. |
| 12680 |
* |
| 12681 |
* @param string $classes Existing body classes. |
| 12682 |
* @return string |
| 12683 |
*/ |
| 12684 |
function ebook_store_admin_body_class( $classes ) { |
| 12685 |
if ( function_exists( 'ebook_store_has_pro_license' ) && ebook_store_has_pro_license() ) { |
| 12686 |
$classes .= ' ebook-store-pro-active'; |
| 12687 |
} |
| 12688 |
|
| 12689 |
return $classes; |
| 12690 |
} |
| 12691 |
add_filter( 'admin_body_class', 'ebook_store_admin_body_class' ); |
| 12692 |
|
| 12693 |
function ebook_store_file_formats_form($ebook_id) { |
| 12694 |
$formats = array('pdf','zip','mobi','txt','mp3','epub','mp4'); |
| 12695 |
$ebook = new EbookStoreEbook($ebook_id); |
| 12696 |
$ebook->formats = is_array($ebook->formats) ? $ebook->formats : array(); |
| 12697 |
|
| 12698 |
// Everything but PDF is a Pro format, but the "Pro" badge should only appear |
| 12699 |
// to someone who does not have Pro. It used to be keyed on the format index |
| 12700 |
// alone, so a paying licence holder still saw every format tab stamped and |
| 12701 |
// styled as locked. |
| 12702 |
$pro_unlocked = function_exists('ebook_store_has_pro_license') ? ebook_store_has_pro_license() : false; |
| 12703 |
|
| 12704 |
$entries = array(); |
| 12705 |
|
| 12706 |
foreach ($formats as $index => $format) { |
| 12707 |
if ($format == 'pdf' && get_option('encrypt_pdf')) { |
| 12708 |
if (!ebook_store_encryptable(@$ebook->files[$format]) && @$ebook->files[$format] != '') { |
| 12709 |
ebook_store_not_encryptable(); |
| 12710 |
} |
| 12711 |
} |
| 12712 |
|
| 12713 |
$has_file = @in_array($format, $ebook->formats); |
| 12714 |
$filename = ($has_file && !empty($ebook->files[$format])) ? basename($ebook->files[$format]) : ''; |
| 12715 |
$size_label = @$ebook->human_filesize(@$ebook->files[$format . '_size']); |
| 12716 |
$entries[] = array( |
| 12717 |
'slug' => $format, |
| 12718 |
'label' => strtoupper($format), |
| 12719 |
'has_file' => $has_file, |
| 12720 |
'filename' => $filename, |
| 12721 |
'size_label' => $size_label, |
| 12722 |
'exists_class'=> $has_file ? '' : 'hiddenEbookStore', |
| 12723 |
'is_pro' => ($index > 0) && ! $pro_unlocked, |
| 12724 |
); |
| 12725 |
} |
| 12726 |
?> |
| 12727 |
<div class="ebook_store_file_formats_form"> |
| 12728 |
<h3><?php _e('Upload eBook Files', 'ebook-store'); ?></h3> |
| 12729 |
<p class="description"><?php _e('Upload your eBook in different formats. Supported formats are grouped by format tabs below.', 'ebook-store'); ?></p> |
| 12730 |
|
| 12731 |
<div class="ebook-format-tabs" id="ebook-format-tabs-<?php echo esc_attr($ebook_id); ?>"> |
| 12732 |
<div class="ebook-format-tabs__nav" role="tablist"> |
| 12733 |
<?php foreach ($entries as $idx => $entry) : |
| 12734 |
$panel_id = 'ebook-format-panel-' . $entry['slug']; |
| 12735 |
$status_icon = $entry['has_file'] ? 'dashicons-yes-alt' : 'dashicons-minus'; |
| 12736 |
$nav_classes = array('ebook-format-tabs__nav-item'); |
| 12737 |
if ($entry['is_pro']) { |
| 12738 |
$nav_classes[] = 'goPro2'; |
| 12739 |
} |
| 12740 |
if ($entry['has_file']) { |
| 12741 |
$nav_classes[] = 'has-file'; |
| 12742 |
} |
| 12743 |
if ($idx === 0) { |
| 12744 |
$nav_classes[] = 'is-active'; |
| 12745 |
} |
| 12746 |
?> |
| 12747 |
<button type="button" |
| 12748 |
class="<?php echo esc_attr(implode(' ', $nav_classes)); ?>" |
| 12749 |
data-format="<?php echo esc_attr($entry['slug']); ?>" |
| 12750 |
data-target="#<?php echo esc_attr($panel_id); ?>" |
| 12751 |
role="tab" |
| 12752 |
aria-controls="<?php echo esc_attr($panel_id); ?>" |
| 12753 |
aria-selected="<?php echo $idx === 0 ? 'true' : 'false'; ?>"> |
| 12754 |
<span class="ebook-format-tab__icon"> |
| 12755 |
<img src="<?php echo esc_url(plugins_url('img/'.$entry['slug'].'.png', __FILE__)); ?>" alt="<?php echo esc_attr($entry['label']); ?>" /> |
| 12756 |
</span> |
| 12757 |
<span class="ebook-format-tab__label"><?php echo esc_html($entry['label']); ?></span> |
| 12758 |
<span class="ebook-format-tab__status dashicons <?php echo esc_attr($status_icon); ?>"></span> |
| 12759 |
</button> |
| 12760 |
<?php endforeach; ?> |
| 12761 |
</div> |
| 12762 |
<div class="ebook-format-tabs__panels"> |
| 12763 |
<?php foreach ($entries as $idx => $entry) : |
| 12764 |
$panel_id = 'ebook-format-panel-' . $entry['slug']; |
| 12765 |
$panel_classes = array('ebook-format-tab-panel'); |
| 12766 |
if ($entry['is_pro']) { |
| 12767 |
$panel_classes[] = 'goPro2'; |
| 12768 |
} |
| 12769 |
if ($entry['has_file']) { |
| 12770 |
$panel_classes[] = 'has-file'; |
| 12771 |
} |
| 12772 |
if ($idx === 0) { |
| 12773 |
$panel_classes[] = 'is-active'; |
| 12774 |
} |
| 12775 |
?> |
| 12776 |
<div |
| 12777 |
id="<?php echo esc_attr($panel_id); ?>" |
| 12778 |
class="<?php echo esc_attr(implode(' ', $panel_classes)); ?>" |
| 12779 |
data-tab-panel="<?php echo esc_attr($entry['slug']); ?>" |
| 12780 |
<?php echo $idx === 0 ? '' : 'hidden'; ?>> |
| 12781 |
<div class="ebook_store_file_format_item<?php echo $entry['is_pro'] ? ' goPro2' : ''; ?>"> |
| 12782 |
<div class="ebook_store_file_format_item_left"> |
| 12783 |
<span class="ebook_store_icon"> |
| 12784 |
<img src="<?php echo esc_url(plugins_url('img/'.$entry['slug'].'.png', __FILE__)); ?>" alt="<?php echo esc_attr($entry['label']); ?> format" width="64" /> |
| 12785 |
</span> |
| 12786 |
<span class="ebook_store_size <?php echo esc_attr($entry['exists_class']); ?>"><?php echo esc_html($entry['size_label']); ?></span> |
| 12787 |
</div> |
| 12788 |
<div class="ebook_store_file_format_item_right"> |
| 12789 |
<div class="ebook_store_filename"> |
| 12790 |
<?php if ($entry['has_file']) : ?> |
| 12791 |
<a href="#" title="<?php _e('Current file', 'ebook-store'); ?>"><?php echo esc_html($entry['filename']); ?></a> |
| 12792 |
<?php else : ?> |
| 12793 |
<span><?php _e('No file uploaded yet', 'ebook-store'); ?></span> |
| 12794 |
<?php endif; ?> |
| 12795 |
</div> |
| 12796 |
<div class="ebook_store_upload"> |
| 12797 |
<input type="file" |
| 12798 |
accept=".<?php echo esc_attr($entry['slug']); ?>" |
| 12799 |
name="ebook_wp_custom_attachment_<?php echo esc_attr($entry['slug']); ?>" |
| 12800 |
id="ebook_wp_custom_attachment_<?php echo esc_attr($entry['slug']); ?>" |
| 12801 |
data-format="<?php echo esc_attr($entry['slug']); ?>" |
| 12802 |
title="<?php echo esc_attr( sprintf(__('Choose %s file', 'ebook-store'), $entry['label']) ); ?>" |
| 12803 |
/> |
| 12804 |
</div> |
| 12805 |
<div class="ebook_store_control <?php echo esc_attr($entry['exists_class']); ?>"> |
| 12806 |
<label title="<?php _e('Delete file', 'ebook-store'); ?>"> |
| 12807 |
<input type="checkbox" name="ebook_store_delete_<?php echo esc_attr($entry['slug']); ?>" /> |
| 12808 |
<i class="dashicons dashicons-trash"></i> |
| 12809 |
<span class="screen-reader-text"><?php _e('Delete file', 'ebook-store'); ?></span> |
| 12810 |
</label> |
| 12811 |
</div> |
| 12812 |
</div> |
| 12813 |
</div> |
| 12814 |
</div> |
| 12815 |
<?php endforeach; ?> |
| 12816 |
</div> |
| 12817 |
</div> |
| 12818 |
<?php |
| 12819 |
// Files are sliced in the browser and streamed past the server's upload |
| 12820 |
// limits, so quoting upload_max_filesize here only scared authors off |
| 12821 |
// books that upload perfectly well. |
| 12822 |
if ( function_exists( 'ebook_store_max_upload_bytes' ) ) : |
| 12823 |
?> |
| 12824 |
<p class="description"> |
| 12825 |
<?php |
| 12826 |
printf( |
| 12827 |
/* translators: %s: maximum file size, already formatted. */ |
| 12828 |
esc_html__( 'Files are uploaded in small pieces, so the server upload limit does not apply. Maximum size per file: %s.', 'ebook-store' ), |
| 12829 |
esc_html( size_format( ebook_store_max_upload_bytes() ) ) |
| 12830 |
); |
| 12831 |
?> |
| 12832 |
</p> |
| 12833 |
<?php else : ?> |
| 12834 |
<p class="description"><?php _e('Maximum upload file size:', 'ebook-store'); ?> <?php echo ini_get('upload_max_filesize'); ?>B</p> |
| 12835 |
<?php endif; ?> |
| 12836 |
</div> |
| 12837 |
<?php |
| 12838 |
} |
| 12839 |
|
| 12840 |
function ebook_store_price_plus_vat($price) { |
| 12841 |
$vat = ebook_store_get_vat_percent(); |
| 12842 |
$price = (float) $price; |
| 12843 |
|
| 12844 |
if ( $vat <= 0 ) { |
| 12845 |
return $price; |
| 12846 |
} |
| 12847 |
|
| 12848 |
return $price + ( $price * ( $vat / 100 ) ); |
| 12849 |
} |
| 12850 |
|
| 12851 |
function ebook_store_get_store_currency() { |
| 12852 |
$currency = strtoupper( trim( (string) get_option( 'paypal_currency', 'USD' ) ) ); |
| 12853 |
|
| 12854 |
return preg_match( '/^[A-Z]{3}$/', $currency ) ? $currency : 'USD'; |
| 12855 |
} |
| 12856 |
|
| 12857 |
function ebook_store_get_vat_percent() { |
| 12858 |
$vat = trim( (string) get_option( 'vat_percent', '0' ) ); |
| 12859 |
|
| 12860 |
if ( $vat === '' || ! is_numeric( $vat ) ) { |
| 12861 |
return 0.0; |
| 12862 |
} |
| 12863 |
|
| 12864 |
return max( 0, (float) $vat ); |
| 12865 |
} |
| 12866 |
|
| 12867 |
function ebook_store_get_download_limit() { |
| 12868 |
$limit = trim( (string) get_option( 'downloads_limit', '5' ) ); |
| 12869 |
|
| 12870 |
if ( $limit === '' || ! is_numeric( $limit ) ) { |
| 12871 |
return 5; |
| 12872 |
} |
| 12873 |
|
| 12874 |
return max( 1, (int) $limit ); |
| 12875 |
} |
| 12876 |
|
| 12877 |
|
| 12878 |
|
| 12879 |
function ebook_store_row( $atts, $content = "") { |
| 12880 |
return "<div class=\"ebook_store_row ebook_store_row_".intval($atts['col'])."\">" . do_shortcode($content) . "</div>"; |
| 12881 |
} |
| 12882 |
|
| 12883 |
function ebook_store_silent_registration($data) { |
| 12884 |
if (get_option('ebook_store_silent_registration') != 1) { |
| 12885 |
return get_current_user_id(); |
| 12886 |
} |
| 12887 |
|
| 12888 |
$user_email = isset( $data['payer_email'] ) ? sanitize_email( $data['payer_email'] ) : ''; |
| 12889 |
if ( $user_email === '' || ! is_email( $user_email ) ) { |
| 12890 |
return get_current_user_id(); |
| 12891 |
} |
| 12892 |
|
| 12893 |
// The email address is the buyer's identity. If an account already uses it, |
| 12894 |
// reuse that account. The username must NOT be the key: the old code keyed on |
| 12895 |
// "first_last", so a second buyer who happened to share a name with an |
| 12896 |
// existing customer was silently attached to that customer's account. |
| 12897 |
$existing = email_exists( $user_email ); |
| 12898 |
if ( $existing ) { |
| 12899 |
return $existing; |
| 12900 |
} |
| 12901 |
|
| 12902 |
// Build a username that does not collide with any existing account. |
| 12903 |
$base = sanitize_user( trim( (string) $data['first_name'] . '_' . (string) $data['last_name'] ), true ); |
| 12904 |
if ( $base === '' || $base === '_' ) { |
| 12905 |
$parts = explode( '@', $user_email ); |
| 12906 |
$base = sanitize_user( $parts[0], true ); |
| 12907 |
} |
| 12908 |
if ( $base === '' ) { |
| 12909 |
$base = 'customer'; |
| 12910 |
} |
| 12911 |
|
| 12912 |
$user_name = $base; |
| 12913 |
$suffix = 1; |
| 12914 |
while ( username_exists( $user_name ) ) { |
| 12915 |
$suffix++; |
| 12916 |
$user_name = $base . '_' . $suffix; |
| 12917 |
} |
| 12918 |
|
| 12919 |
$user_id = wp_create_user( $user_name, wp_generate_password( 16, true ), $user_email ); |
| 12920 |
|
| 12921 |
// A WP_Error must never be stored as the order's user_id. |
| 12922 |
if ( is_wp_error( $user_id ) ) { |
| 12923 |
return get_current_user_id(); |
| 12924 |
} |
| 12925 |
|
| 12926 |
wp_new_user_notification( $user_id, null, 'both' ); |
| 12927 |
|
| 12928 |
return $user_id; |
| 12929 |
} |
| 12930 |
|
| 12931 |
|
| 12932 |
|
| 12933 |
function ebook_store_downloads($atts) { |
| 12934 |
$args = array( |
| 12935 |
'post_type' => 'ebook_order', |
| 12936 |
'meta_query' => array( |
| 12937 |
array( |
| 12938 |
'key' => 'user_id', |
| 12939 |
'value' => get_current_user_id(), |
| 12940 |
) |
| 12941 |
)); |
| 12942 |
// The Query |
| 12943 |
$the_query = new WP_Query( $args ); |
| 12944 |
$out = 0; |
| 12945 |
// The Loop |
| 12946 |
$checkoutPage = esc_attr(get_option('ebook_store_checkout_page')); |
| 12947 |
if ( $the_query->have_posts() ) { |
| 12948 |
|
| 12949 |
$the_query->the_post(); |
| 12950 |
$link = add_query_arg(array('ebook_key' => get_post_meta(get_the_ID(),'ebook_key',true), 'action' => 'thank_you'),get_permalink($checkoutPage)); |
| 12951 |
echo '<h4><a href="' . $link . '">'.__('Order','ebook-store') . ' #' . get_the_ID().'</a></h4>'; |
| 12952 |
} else { |
| 12953 |
echo __('No orders found.'); |
| 12954 |
} |
| 12955 |
wp_reset_postdata(); |
| 12956 |
} |
| 12957 |
function ebook_pdf_reader($ebook_order = false, $ebook_post_id = false) { |
| 12958 |
$src = plugins_url('',__FILE__) . '/includes/ViewerJS/index.html'; |
| 12959 |
// /return $src; |
| 12960 |
$src = add_query_arg('title',__('Online Viewer','ebook-store'),$src); |
| 12961 |
if ($ebook_order) { |
| 12962 |
return '<iframe style="float:left;" src="'.$src.'#' . $ebook_order['downloadlink'][0] . '" width=\'100%\' height=\'' . get_option('pdf_viewer_height',600) . '\' allowfullscreen webkitallowfullscreen></iframe>'; |
| 12963 |
} else { |
| 12964 |
$meta = get_post_meta($ebook_post_id, 'ebook_wp_custom_attachment', true); |
| 12965 |
|
| 12966 |
if (is_array($meta)) { |
| 12967 |
$pdf_url = $meta['url']; |
| 12968 |
} else { |
| 12969 |
$pdf_url = ''; |
| 12970 |
} |
| 12971 |
|
| 12972 |
//wp_die('URL ' . $pdf_url); |
| 12973 |
if (is_ssl()) { |
| 12974 |
$pdf_url = str_replace('http://', 'https://', $pdf_url); |
| 12975 |
} |
| 12976 |
return '<iframe style="float:left;" src="'.$src.'#' . $pdf_url . '" width=\'100%\' height=\'' . get_option('pdf_viewer_height',600) . '\' allowfullscreen webkitallowfullscreen></iframe>'; |
| 12977 |
} |
| 12978 |
|
| 12979 |
} |
| 12980 |
function pr_die($array) { |
| 12981 |
wp_die('<pre>'.print_r($array,true).'</pre>'); |
| 12982 |
} |
| 12983 |
function ebook_store_redirect_add_order() { |
| 12984 |
if (endswith(esc_url_raw($_SERVER['REQUEST_URI']), 'post-new.php?post_type=ebook_order')) { |
| 12985 |
header("Location: edit.php?post_type=ebook&page=ebook-store-add-order-page"); |
| 12986 |
} |
| 12987 |
} |
| 12988 |
|
| 12989 |
if (!function_exists('endswith')) { |
| 12990 |
function endswith($string, $test) { |
| 12991 |
$strlen = strlen($string); |
| 12992 |
$testlen = strlen($test); |
| 12993 |
if ($testlen > $strlen) return false; |
| 12994 |
return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; |
| 12995 |
} |
| 12996 |
} |
| 12997 |
add_action( 'woocommerce_after_account_downloads', 'ebook_store_woocommerce_after_account_downloads', 10, 1 ); |
| 12998 |
function ebook_store_woocommerce_after_account_downloads() { |
| 12999 |
|
| 13000 |
echo '<h3>Ebook Downloads</h3>'; |
| 13001 |
//echo '<p>You can access your ebook downloads by going to the Orders section and clicking on an order number that will open the order confirmation page containing the download links.</p>'; |
| 13002 |
echo '<table class="woocommerce-MyAccount-downloads shop_table shop_table_responsive"> |
| 13003 |
<thead> |
| 13004 |
<tr> |
| 13005 |
<th class="download-product"><span class="nobr">'.__('Product','ebook-store').'</span></th> |
| 13006 |
<th class="download-remaining"><span class="nobr">'.__('Download Links','ebook-store').'</span></th> |
| 13007 |
<!-- <th class="download-expires"><span class="nobr">'.__('Expires','ebook-store').'</span></th> |
| 13008 |
<th class="download-file"><span class="nobr">'.__('File','ebook-store').'</span></th>--> |
| 13009 |
</tr> |
| 13010 |
</thead> |
| 13011 |
<tbody>'; |
| 13012 |
$customer_user_id = get_current_user_id(); |
| 13013 |
|
| 13014 |
$customer_orders = wc_get_orders( array( |
| 13015 |
'meta_key' => '_customer_user', |
| 13016 |
'meta_value' => $customer_user_id, |
| 13017 |
'limit' => -1, |
| 13018 |
)); |
| 13019 |
foreach ( $customer_orders as $order ) { |
| 13020 |
ebook_store_woocommerce_order_details($order, true); |
| 13021 |
|
| 13022 |
continue; |
| 13023 |
} |
| 13024 |
echo ' |
| 13025 |
</tbody></table>'; |
| 13026 |
} |
| 13027 |
|
| 13028 |
function ebook_store_wp_super_cache_warning() { |
| 13029 |
if (!file_exists(WP_PLUGIN_DIR . '/wp-super-cache/wp-cache.php')) { |
| 13030 |
return true; |
| 13031 |
} |
| 13032 |
|
| 13033 |
if ( isset( $_GET['dismiss'] ) && $_GET['dismiss'] === 'wpsc' ) { |
| 13034 |
update_option( 'ebookstorewpsc', 1 ); |
| 13035 |
return true; |
| 13036 |
} |
| 13037 |
if (get_option( 'ebookstorewpsc') < 1) { |
| 13038 |
?> |
| 13039 |
<div id="wpsc-index-warning" class="error notice" style="padding: 10px 10px 50px 10px"><h1>Warning!</h1><p>You are using WP Super Cache, in order to have <b>ebook store</b> working properly with it, you must add these lines to the "Ignore strings" section <a href="options-general.php?page=wpsupercache&tab=settings">here</a>. Exactly where it says "Add here strings (not a filename) that forces a page not to be cached.". The lines you must add are:<br /><textarea>ipn |
| 13040 |
ebook |
| 13041 |
</textarea></p><a id="wpsc-dismiss" href="<?php echo esc_url($_SERVER['REQUEST_URI']); ?>&dismiss=wpsc">Dismiss</a></div> |
| 13042 |
<?php |
| 13043 |
|
| 13044 |
} |
| 13045 |
} |
| 13046 |
|
| 13047 |
function ebook_store_woocommerce_variation_fields( $loop, $variation_data, $variation ) { |
| 13048 |
if ( get_option( 'ebook_store_woocommerce_integration' ) == 0 ) { |
| 13049 |
return; |
| 13050 |
} |
| 13051 |
|
| 13052 |
$variation_id = isset( $variation->ID ) ? (int) $variation->ID : 0; |
| 13053 |
$selected_ebook_id = $variation_id > 0 ? (int) get_post_meta( $variation_id, 'ebook_store_book_id', true ) : 0; |
| 13054 |
$options = ebook_store_get_woocommerce_options(); |
| 13055 |
?> |
| 13056 |
<div class="form-row form-row-full"> |
| 13057 |
<label for="ebook_store_book_id_variation_<?php echo esc_attr( $variation_id ); ?>"><?php esc_html_e( 'Ebook Store ebook for this variation', 'ebook-store' ); ?></label> |
| 13058 |
<select id="ebook_store_book_id_variation_<?php echo esc_attr( $variation_id ); ?>" name="ebook_store_book_id_variation[<?php echo esc_attr( $variation_id ); ?>]" class="select short"> |
| 13059 |
<?php foreach ( $options as $ebook_id => $ebook_title ) : ?> |
| 13060 |
<option value="<?php echo esc_attr( $ebook_id ); ?>" <?php selected( $selected_ebook_id, (int) $ebook_id ); ?>> |
| 13061 |
<?php echo esc_html( $ebook_title ); ?> |
| 13062 |
</option> |
| 13063 |
<?php endforeach; ?> |
| 13064 |
</select> |
| 13065 |
<p class="description"><?php esc_html_e( 'Choose which ebook this variation should deliver. Leave it empty for physical-only or non-ebook variations.', 'ebook-store' ); ?></p> |
| 13066 |
</div> |
| 13067 |
<?php |
| 13068 |
} |
| 13069 |
|
| 13070 |
function ebook_store_save_woocommerce_variation_data( $variation_id, $loop ) { |
| 13071 |
$variation_id = (int) $variation_id; |
| 13072 |
$old_ebook_id = (int) get_post_meta( $variation_id, 'ebook_store_book_id', true ); |
| 13073 |
if ( $old_ebook_id > 0 ) { |
| 13074 |
update_post_meta( $old_ebook_id, 'woocommerce_product_id', 0 ); |
| 13075 |
} |
| 13076 |
|
| 13077 |
$new_ebook_id = 0; |
| 13078 |
if ( isset( $_POST['ebook_store_book_id_variation'][ $variation_id ] ) ) { |
| 13079 |
$new_ebook_id = absint( wp_unslash( $_POST['ebook_store_book_id_variation'][ $variation_id ] ) ); |
| 13080 |
} |
| 13081 |
|
| 13082 |
if ( $new_ebook_id > 0 ) { |
| 13083 |
update_post_meta( $variation_id, 'ebook_store_book_id', $new_ebook_id ); |
| 13084 |
update_post_meta( $variation_id, '_enable_ebook_store_option', 'yes' ); |
| 13085 |
update_post_meta( $new_ebook_id, 'woocommerce_product_id', $variation_id ); |
| 13086 |
} else { |
| 13087 |
delete_post_meta( $variation_id, 'ebook_store_book_id' ); |
| 13088 |
update_post_meta( $variation_id, '_enable_ebook_store_option', 'no' ); |
| 13089 |
} |
| 13090 |
} |
| 13091 |
|
| 13092 |
function ebook_store_get_wc_item_product_ids( $item ) { |
| 13093 |
$product_id = 0; |
| 13094 |
$variation_id = 0; |
| 13095 |
|
| 13096 |
if ( is_object( $item ) ) { |
| 13097 |
if ( method_exists( $item, 'get_product_id' ) ) { |
| 13098 |
$product_id = (int) $item->get_product_id(); |
| 13099 |
} |
| 13100 |
if ( method_exists( $item, 'get_variation_id' ) ) { |
| 13101 |
$variation_id = (int) $item->get_variation_id(); |
| 13102 |
} |
| 13103 |
} elseif ( is_array( $item ) ) { |
| 13104 |
$product_id = isset( $item['product_id'] ) ? (int) $item['product_id'] : 0; |
| 13105 |
$variation_id = isset( $item['variation_id'] ) ? (int) $item['variation_id'] : 0; |
| 13106 |
} |
| 13107 |
|
| 13108 |
return array( |
| 13109 |
'product_id' => $product_id, |
| 13110 |
'variation_id' => $variation_id, |
| 13111 |
); |
| 13112 |
} |
| 13113 |
|
| 13114 |
function ebook_store_product_has_variation_ebook_mappings( $product_id ) { |
| 13115 |
$product_id = (int) $product_id; |
| 13116 |
if ( $product_id < 1 ) { |
| 13117 |
return false; |
| 13118 |
} |
| 13119 |
|
| 13120 |
$variation_ids = get_posts( |
| 13121 |
array( |
| 13122 |
'post_parent' => $product_id, |
| 13123 |
'post_type' => 'product_variation', |
| 13124 |
'post_status' => array( 'publish', 'private' ), |
| 13125 |
'posts_per_page' => -1, |
| 13126 |
'fields' => 'ids', |
| 13127 |
'no_found_rows' => true, |
| 13128 |
) |
| 13129 |
); |
| 13130 |
|
| 13131 |
if ( ! is_array( $variation_ids ) || empty( $variation_ids ) ) { |
| 13132 |
return false; |
| 13133 |
} |
| 13134 |
|
| 13135 |
foreach ( $variation_ids as $variation_id ) { |
| 13136 |
if ( (int) get_post_meta( $variation_id, 'ebook_store_book_id', true ) > 0 ) { |
| 13137 |
return true; |
| 13138 |
} |
| 13139 |
} |
| 13140 |
|
| 13141 |
return false; |
| 13142 |
} |
| 13143 |
|
| 13144 |
function ebook_store_get_wc_product_ebook_mapping( $product_id, $variation_id = 0 ) { |
| 13145 |
$product_id = (int) $product_id; |
| 13146 |
$variation_id = (int) $variation_id; |
| 13147 |
|
| 13148 |
if ( $variation_id > 0 ) { |
| 13149 |
$variation_ebook_id = (int) get_post_meta( $variation_id, 'ebook_store_book_id', true ); |
| 13150 |
if ( $variation_ebook_id > 0 ) { |
| 13151 |
return array( |
| 13152 |
'ebook_id' => $variation_ebook_id, |
| 13153 |
'product_id' => $product_id, |
| 13154 |
'variation_id' => $variation_id, |
| 13155 |
'mapped_post_id' => $variation_id, |
| 13156 |
); |
| 13157 |
} |
| 13158 |
|
| 13159 |
if ( ebook_store_product_has_variation_ebook_mappings( $product_id ) ) { |
| 13160 |
return array( |
| 13161 |
'ebook_id' => 0, |
| 13162 |
'product_id' => $product_id, |
| 13163 |
'variation_id' => $variation_id, |
| 13164 |
'mapped_post_id' => $variation_id, |
| 13165 |
); |
| 13166 |
} |
| 13167 |
|
| 13168 |
return array( |
| 13169 |
'ebook_id' => (int) get_post_meta( $product_id, 'ebook_store_book_id', true ), |
| 13170 |
'product_id' => $product_id, |
| 13171 |
'variation_id' => $variation_id, |
| 13172 |
'mapped_post_id' => $product_id, |
| 13173 |
); |
| 13174 |
} |
| 13175 |
|
| 13176 |
return array( |
| 13177 |
'ebook_id' => (int) get_post_meta( $product_id, 'ebook_store_book_id', true ), |
| 13178 |
'product_id' => $product_id, |
| 13179 |
'variation_id' => 0, |
| 13180 |
'mapped_post_id' => $product_id, |
| 13181 |
); |
| 13182 |
} |
| 13183 |
|
| 13184 |
function ebook_store_get_wc_item_ebook_mapping( $item ) { |
| 13185 |
$ids = ebook_store_get_wc_item_product_ids( $item ); |
| 13186 |
return ebook_store_get_wc_product_ebook_mapping( $ids['product_id'], $ids['variation_id'] ); |
| 13187 |
} |
| 13188 |
|
| 13189 |
function ebook_store_get_wc_product_reference_for_ebook( $ebook_id ) { |
| 13190 |
global $wpdb; |
| 13191 |
|
| 13192 |
$ebook_id = (int) $ebook_id; |
| 13193 |
if ( $ebook_id < 1 || ! function_exists( 'wc_get_product' ) ) { |
| 13194 |
return 0; |
| 13195 |
} |
| 13196 |
|
| 13197 |
$stored_reference_id = (int) get_post_meta( $ebook_id, 'woocommerce_product_id', true ); |
| 13198 |
$allowed_statuses = array( 'publish', 'private' ); |
| 13199 |
if ( $stored_reference_id > 0 && in_array( get_post_status( $stored_reference_id ), $allowed_statuses, true ) ) { |
| 13200 |
$stored_mapping = (int) get_post_meta( $stored_reference_id, 'ebook_store_book_id', true ); |
| 13201 |
if ( $stored_mapping === $ebook_id ) { |
| 13202 |
return $stored_reference_id; |
| 13203 |
} |
| 13204 |
} |
| 13205 |
|
| 13206 |
$product_reference_id = 0; |
| 13207 |
$status_placeholders = implode( ',', array_fill( 0, count( $allowed_statuses ), '%s' ) ); |
| 13208 |
$query_params = array_merge( |
| 13209 |
array( 'ebook_store_book_id', (string) $ebook_id, 'product', 'product_variation' ), |
| 13210 |
$allowed_statuses |
| 13211 |
); |
| 13212 |
|
| 13213 |
$product_reference_id = (int) $wpdb->get_var( |
| 13214 |
$wpdb->prepare( |
| 13215 |
" |
| 13216 |
SELECT pm.post_id |
| 13217 |
FROM {$wpdb->postmeta} pm |
| 13218 |
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id |
| 13219 |
WHERE pm.meta_key = %s |
| 13220 |
AND pm.meta_value = %s |
| 13221 |
AND p.post_type IN (%s, %s) |
| 13222 |
AND p.post_status IN ($status_placeholders) |
| 13223 |
ORDER BY pm.post_id DESC |
| 13224 |
LIMIT 1 |
| 13225 |
", |
| 13226 |
$query_params |
| 13227 |
) |
| 13228 |
); |
| 13229 |
|
| 13230 |
if ( $product_reference_id > 0 && $product_reference_id !== $stored_reference_id && ! wp_doing_ajax() && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { |
| 13231 |
update_post_meta( $ebook_id, 'woocommerce_product_id', $product_reference_id ); |
| 13232 |
} |
| 13233 |
|
| 13234 |
return $product_reference_id; |
| 13235 |
} |
| 13236 |
|
| 13237 |
function ebook_store_get_wc_add_to_cart_config( $product_reference_id ) { |
| 13238 |
$product_reference_id = (int) $product_reference_id; |
| 13239 |
if ( $product_reference_id < 1 || ! function_exists( 'wc_get_product' ) ) { |
| 13240 |
return false; |
| 13241 |
} |
| 13242 |
|
| 13243 |
$product = wc_get_product( $product_reference_id ); |
| 13244 |
if ( ! $product ) { |
| 13245 |
return false; |
| 13246 |
} |
| 13247 |
|
| 13248 |
if ( $product->is_type( 'variation' ) ) { |
| 13249 |
return array( |
| 13250 |
'product_id' => (int) $product->get_parent_id(), |
| 13251 |
'variation_id' => (int) $product->get_id(), |
| 13252 |
'variation' => $product->get_variation_attributes(), |
| 13253 |
'title' => $product->get_name(), |
| 13254 |
); |
| 13255 |
} |
| 13256 |
|
| 13257 |
return array( |
| 13258 |
'product_id' => (int) $product->get_id(), |
| 13259 |
'variation_id' => 0, |
| 13260 |
'variation' => array(), |
| 13261 |
'title' => $product->get_name(), |
| 13262 |
); |
| 13263 |
} |
| 13264 |
|
| 13265 |
function ebook_store_cart_contains_wc_product( $cart_config ) { |
| 13266 |
if ( ! function_exists( 'WC' ) || ! WC()->cart ) { |
| 13267 |
return false; |
| 13268 |
} |
| 13269 |
|
| 13270 |
foreach ( WC()->cart->get_cart() as $cart_item ) { |
| 13271 |
$cart_product_id = isset( $cart_item['product_id'] ) ? (int) $cart_item['product_id'] : 0; |
| 13272 |
$cart_variation_id = isset( $cart_item['variation_id'] ) ? (int) $cart_item['variation_id'] : 0; |
| 13273 |
if ( $cart_product_id === (int) $cart_config['product_id'] && $cart_variation_id === (int) $cart_config['variation_id'] ) { |
| 13274 |
return true; |
| 13275 |
} |
| 13276 |
} |
| 13277 |
|
| 13278 |
return false; |
| 13279 |
} |
| 13280 |
|
| 13281 |
add_action('admin_post_ebook_store_test_encryption', 'ebook_store_test_encryption'); |
| 13282 |
add_action('admin_post_nopriv_ebook_store_test_encryption', 'ebook_store_test_encryption'); |
| 13283 |
function ebook_store_test_encryption() { |
| 13284 |
if (!isset($_GET['ebook_id'])) { |
| 13285 |
wp_die(esc_html__('Missing ebook ID.', 'ebook-store')); |
| 13286 |
} |
| 13287 |
error_log('Ebook Store Test Encryption: request=' . print_r($_REQUEST, true)); |
| 13288 |
$ebook_id = absint($_GET['ebook_id']); |
| 13289 |
if (!$ebook_id || get_post_type($ebook_id) !== 'ebook') { |
| 13290 |
wp_die(esc_html__('Invalid ebook.', 'ebook-store')); |
| 13291 |
} |
| 13292 |
if (!current_user_can('edit_post', $ebook_id)) { |
| 13293 |
wp_die(esc_html__('You are not allowed to perform this test.', 'ebook-store')); |
| 13294 |
} |
| 13295 |
check_admin_referer('ebook_store_test_encryption_' . $ebook_id); |
| 13296 |
|
| 13297 |
$test_format = isset($_GET['format']) ? sanitize_key( wp_unslash( $_GET['format'] ) ) : 'pdf'; |
| 13298 |
if ( $test_format === 'epub' ) { |
| 13299 |
ebook_store_test_epub_watermark( $ebook_id ); |
| 13300 |
exit; |
| 13301 |
} |
| 13302 |
|
| 13303 |
$pdf_meta = get_post_meta($ebook_id, 'ebook_wp_custom_attachment', true); |
| 13304 |
if (!is_array($pdf_meta) || empty($pdf_meta['file']) || !file_exists($pdf_meta['file'])) { |
| 13305 |
error_log('Ebook Store Test Encryption: PDF missing for ebook ' . $ebook_id); |
| 13306 |
wp_die(esc_html__('No PDF found for this ebook.', 'ebook-store')); |
| 13307 |
} |
| 13308 |
if (!ebook_store_encryptable($pdf_meta['file'])) { |
| 13309 |
error_log('Ebook Store Test Encryption: PDF not encryptable'); |
| 13310 |
wp_die(esc_html__('The PDF is not compatible with encryption. Please re-upload it in Acrobat 5.0 PDF 1.5 format.', 'ebook-store')); |
| 13311 |
} |
| 13312 |
|
| 13313 |
global $attachment, $ebook_email_delivery; |
| 13314 |
$original_attachment = isset($attachment) ? $attachment : null; |
| 13315 |
$original_email_delivery = isset($ebook_email_delivery) ? $ebook_email_delivery : null; |
| 13316 |
|
| 13317 |
$attachment = array( |
| 13318 |
array('file' => $pdf_meta['file']) |
| 13319 |
); |
| 13320 |
$ebook_email_delivery = array( |
| 13321 |
'order' => array( |
| 13322 |
'order_id' => 0, |
| 13323 |
'ebook' => $ebook_id, |
| 13324 |
'payer_email' => 'test@example.com', |
| 13325 |
) |
| 13326 |
); |
| 13327 |
|
| 13328 |
$data = array( |
| 13329 |
'txn_id' => 'encryption-test', |
| 13330 |
'payment_date' => current_time('mysql'), |
| 13331 |
'payer_email' => 'test@example.com', |
| 13332 |
'password' => 'test', |
| 13333 |
'md5_nonce' => 'encryption_test_' . $ebook_id, |
| 13334 |
'ebook_key' => ebook_store_test_verification_key( $ebook_id ), |
| 13335 |
); |
| 13336 |
|
| 13337 |
try { |
| 13338 |
$encrypted_file = ebook_encrypt_pdf($data, null); |
| 13339 |
} catch (Throwable $e) { |
| 13340 |
error_log('Ebook Store Test Encryption: exception ' . $e->getMessage() . ' Trace: ' . $e->getTraceAsString()); |
| 13341 |
$attachment = $original_attachment; |
| 13342 |
$ebook_email_delivery = $original_email_delivery; |
| 13343 |
wp_die(sprintf( |
| 13344 |
'%s %s', |
| 13345 |
esc_html__('Encryption test failed with an exception.', 'ebook-store'), |
| 13346 |
esc_html($e->getMessage()) |
| 13347 |
)); |
| 13348 |
} |
| 13349 |
if (!$encrypted_file || !file_exists($encrypted_file)) { |
| 13350 |
error_log('Ebook Store Test Encryption: encryption failed, file missing.'); |
| 13351 |
$attachment = $original_attachment; |
| 13352 |
$ebook_email_delivery = $original_email_delivery; |
| 13353 |
wp_die(esc_html__('Encryption test failed. Please check your PHP error log for details.', 'ebook-store')); |
| 13354 |
} |
| 13355 |
|
| 13356 |
error_log('Ebook Store Test Encryption: success, streaming file ' . $encrypted_file); |
| 13357 |
header('Content-Type: application/pdf'); |
| 13358 |
header('Content-Disposition: inline; filename="' . basename($pdf_meta['file']) . '"'); |
| 13359 |
header('Content-Length: ' . filesize($encrypted_file)); |
| 13360 |
ebook_readfile_chunked($encrypted_file); |
| 13361 |
@unlink($encrypted_file); |
| 13362 |
|
| 13363 |
$attachment = $original_attachment; |
| 13364 |
$ebook_email_delivery = $original_email_delivery; |
| 13365 |
exit; |
| 13366 |
} |
| 13367 |
|
| 13368 |
/** |
| 13369 |
* Stream a watermarked copy of an ebook's ePub so the owner can preview exactly |
| 13370 |
* what a buyer receives. Uses the configured watermark settings with sample buyer |
| 13371 |
* values, and never touches the stored upload. |
| 13372 |
* |
| 13373 |
* @param int $ebook_id Ebook post ID (already permission- and nonce-checked). |
| 13374 |
*/ |
| 13375 |
function ebook_store_test_epub_watermark( $ebook_id ) { |
| 13376 |
$epub_meta = get_post_meta( $ebook_id, 'ebook_wp_custom_attachment_epub', true ); |
| 13377 |
if ( ! is_array( $epub_meta ) || empty( $epub_meta['file'] ) || ! file_exists( $epub_meta['file'] ) ) { |
| 13378 |
wp_die( esc_html__( 'No ePub found for this ebook.', 'ebook-store' ) ); |
| 13379 |
} |
| 13380 |
if ( ! function_exists( 'ebook_store_watermark_epub' ) ) { |
| 13381 |
wp_die( esc_html__( 'The ePub watermarking engine is not available on this server.', 'ebook-store' ) ); |
| 13382 |
} |
| 13383 |
|
| 13384 |
$src = $epub_meta['file']; |
| 13385 |
|
| 13386 |
// Build a representative watermark from the configured template, standing in |
| 13387 |
// sample buyer values for a real order. |
| 13388 |
$template = (string) get_option( 'ebook_store_epub_watermark_text', 'Licensed to %%first_name%% %%last_name%% - %%payer_email%%. Please do not share.' ); |
| 13389 |
if ( trim( $template ) === '' ) { |
| 13390 |
$template = 'Licensed to %%first_name%% %%last_name%% - %%payer_email%%'; |
| 13391 |
} |
| 13392 |
$sample = str_replace( |
| 13393 |
array( '%%first_name%%', '%%last_name%%', '%%payer_email%%', '%%residence_country%%', '%%payment_date%%', '%%txn_id%%' ), |
| 13394 |
array( 'Test', 'Buyer', 'test@example.com', 'US', date_i18n( 'Y-m-d' ), 'watermark-test' ), |
| 13395 |
$template |
| 13396 |
); |
| 13397 |
|
| 13398 |
$dir = untrailingslashit( get_temp_dir() ); |
| 13399 |
$dest = $dir . '/ebook-store-epub-test-' . $ebook_id . '-' . wp_generate_password( 8, false ) . '.epub'; |
| 13400 |
|
| 13401 |
$qr_text = ( get_option( 'ebook_store_epub_qr' ) && function_exists( 'ebook_store_get_order_verification_url' ) ) |
| 13402 |
? ebook_store_get_order_verification_url( ebook_store_test_verification_key( $ebook_id ) ) |
| 13403 |
: ''; |
| 13404 |
|
| 13405 |
$out = ebook_store_watermark_epub( |
| 13406 |
$src, |
| 13407 |
$dest, |
| 13408 |
$sample, |
| 13409 |
array( |
| 13410 |
'scope' => get_option( 'ebook_store_epub_watermark_scope', 'first' ), |
| 13411 |
'position' => get_option( 'ebook_store_epub_watermark_position', 'bottom' ), |
| 13412 |
'style' => get_option( 'ebook_store_epub_watermark_style', 'subtle' ), |
| 13413 |
'stamp_metadata' => get_option( 'ebook_store_epub_stamp_metadata' ), |
| 13414 |
'qr' => get_option( 'ebook_store_epub_qr' ), |
| 13415 |
'qr_scope' => get_option( 'ebook_store_epub_qr_scope', 'first' ), |
| 13416 |
'qr_position' => get_option( 'ebook_store_epub_qr_position', 'bottom' ), |
| 13417 |
'qr_text' => $qr_text, |
| 13418 |
'order_id' => 0, |
| 13419 |
) |
| 13420 |
); |
| 13421 |
|
| 13422 |
if ( $out !== $dest || ! file_exists( $dest ) ) { |
| 13423 |
wp_die( esc_html__( 'This ePub could not be watermarked for testing. It may be DRM-protected, digitally signed, corrupt, or too large — in which case buyers receive the original file unchanged.', 'ebook-store' ) ); |
| 13424 |
} |
| 13425 |
|
| 13426 |
nocache_headers(); |
| 13427 |
header( 'Content-Type: application/epub+zip' ); |
| 13428 |
header( 'Content-Disposition: attachment; filename="watermark-test-' . basename( $src ) . '"' ); |
| 13429 |
header( 'Content-Length: ' . filesize( $dest ) ); |
| 13430 |
ebook_readfile_chunked( $dest ); |
| 13431 |
@unlink( $dest ); |
| 13432 |
exit; |
| 13433 |
} |
| 13434 |
|
| 13435 |
function ebook_store_deregister_embeds(){ |
| 13436 |
wp_dequeue_script( 'wp-embed' ); |
| 13437 |
} |
| 13438 |
|
| 13439 |
/** |
| 13440 |
* Log a adyen webhook event. |
| 13441 |
* |
| 13442 |
* Writes through ebook_store_log(), which keeps the file outside the web root, |
| 13443 |
* caps its size and stays silent unless logging is switched on. The old version |
| 13444 |
* appended to an uncapped .txt inside the plugin folder that any visitor could |
| 13445 |
* fetch; one of those files reached 118 MB and contained live order tokens. |
| 13446 |
* |
| 13447 |
* @param string $message Message. |
| 13448 |
* @param array $context Context. |
| 13449 |
*/ |
| 13450 |
function ebook_store_log_adyen_event( $message, $context = array() ) { |
| 13451 |
ebook_store_log( 'adyen', $message, $context ); |
| 13452 |
} |
| 13453 |
|
| 13454 |
function ebook_store_get_adyen_api_base_url() { |
| 13455 |
$environment = (string) get_option( 'adyen_environment', 'test' ); |
| 13456 |
$version = 'v71'; |
| 13457 |
|
| 13458 |
if ( $environment === 'live' ) { |
| 13459 |
$prefix = trim( (string) get_option( 'adyen_live_prefix', '' ) ); |
| 13460 |
if ( $prefix === '' ) { |
| 13461 |
return new WP_Error( 'adyen_missing_live_prefix', 'Adyen live URL prefix not configured' ); |
| 13462 |
} |
| 13463 |
|
| 13464 |
return 'https://' . rawurlencode( $prefix ) . '-checkout-live.adyenpayments.com/checkout/' . $version; |
| 13465 |
} |
| 13466 |
|
| 13467 |
return 'https://checkout-test.adyen.com/' . $version; |
| 13468 |
} |
| 13469 |
|
| 13470 |
function ebook_store_get_adyen_webhook_url() { |
| 13471 |
return site_url( 'wp-json/ebook-store/v1/adyen-webhook' ); |
| 13472 |
} |
| 13473 |
|
| 13474 |
function ebook_store_adyen_request( $method, $path, $body = null, $headers = array() ) { |
| 13475 |
$api_key = trim( (string) get_option( 'adyen_api_key', '' ) ); |
| 13476 |
if ( $api_key === '' ) { |
| 13477 |
return new WP_Error( 'adyen_missing_api_key', 'Adyen API key not configured' ); |
| 13478 |
} |
| 13479 |
|
| 13480 |
$base_url = ebook_store_get_adyen_api_base_url(); |
| 13481 |
if ( is_wp_error( $base_url ) ) { |
| 13482 |
return $base_url; |
| 13483 |
} |
| 13484 |
|
| 13485 |
$request_headers = array_merge( |
| 13486 |
array( |
| 13487 |
'Content-Type' => 'application/json', |
| 13488 |
'X-API-Key' => $api_key, |
| 13489 |
), |
| 13490 |
(array) $headers |
| 13491 |
); |
| 13492 |
|
| 13493 |
$args = array( |
| 13494 |
'method' => strtoupper( (string) $method ), |
| 13495 |
'timeout' => 20, |
| 13496 |
'headers' => $request_headers, |
| 13497 |
'user-agent' => 'Ebook-Store/' . ( defined( 'EBOOK_STORE_VERSION' ) ? EBOOK_STORE_VERSION : '1.0' ), |
| 13498 |
); |
| 13499 |
|
| 13500 |
if ( null !== $body ) { |
| 13501 |
$args['body'] = wp_json_encode( $body ); |
| 13502 |
} |
| 13503 |
|
| 13504 |
$response = wp_remote_request( rtrim( $base_url, '/' ) . '/' . ltrim( (string) $path, '/' ), $args ); |
| 13505 |
if ( is_wp_error( $response ) ) { |
| 13506 |
return $response; |
| 13507 |
} |
| 13508 |
|
| 13509 |
$status_code = (int) wp_remote_retrieve_response_code( $response ); |
| 13510 |
$response_body = wp_remote_retrieve_body( $response ); |
| 13511 |
$data = $response_body !== '' ? json_decode( $response_body, true ) : array(); |
| 13512 |
|
| 13513 |
if ( $status_code < 200 || $status_code >= 300 ) { |
| 13514 |
$message = is_array( $data ) && ! empty( $data['message'] ) ? (string) $data['message'] : 'Adyen request failed.'; |
| 13515 |
return new WP_Error( |
| 13516 |
'adyen_request_failed', |
| 13517 |
$message, |
| 13518 |
array( |
| 13519 |
'status_code' => $status_code, |
| 13520 |
'body' => $data, |
| 13521 |
) |
| 13522 |
); |
| 13523 |
} |
| 13524 |
|
| 13525 |
return is_array( $data ) ? $data : array(); |
| 13526 |
} |
| 13527 |
|
| 13528 |
function ebook_store_get_adyen_shopper_locale() { |
| 13529 |
$locale = get_locale(); |
| 13530 |
if ( function_exists( 'ebook_store_get_selected_language_code' ) ) { |
| 13531 |
$language_code = (string) ebook_store_get_selected_language_code(); |
| 13532 |
if ( $language_code !== '' && $language_code !== '0' ) { |
| 13533 |
$locale = $language_code . '_' . strtoupper( $language_code ); |
| 13534 |
} |
| 13535 |
} |
| 13536 |
|
| 13537 |
$locale = str_replace( '-', '_', (string) $locale ); |
| 13538 |
if ( preg_match( '/^[a-z]{2}_[A-Z]{2}$/', $locale ) ) { |
| 13539 |
return str_replace( '_', '-', $locale ); |
| 13540 |
} |
| 13541 |
|
| 13542 |
$language = strtolower( substr( $locale, 0, 2 ) ); |
| 13543 |
if ( preg_match( '/^[a-z]{2}$/', $language ) ) { |
| 13544 |
return $language . '-' . strtoupper( $language ); |
| 13545 |
} |
| 13546 |
|
| 13547 |
return 'en-US'; |
| 13548 |
} |
| 13549 |
|
| 13550 |
function ebook_store_get_adyen_country_code() { |
| 13551 |
$locale = str_replace( '-', '_', (string) get_locale() ); |
| 13552 |
if ( preg_match( '/^[a-z]{2}_([A-Z]{2})$/', $locale, $matches ) ) { |
| 13553 |
return strtoupper( $matches[1] ); |
| 13554 |
} |
| 13555 |
|
| 13556 |
return ''; |
| 13557 |
} |
| 13558 |
|
| 13559 |
function ebook_store_store_adyen_checkout_state( $state ) { |
| 13560 |
if ( ! is_array( $state ) ) { |
| 13561 |
return; |
| 13562 |
} |
| 13563 |
|
| 13564 |
$state['created_at'] = isset( $state['created_at'] ) ? (int) $state['created_at'] : time(); |
| 13565 |
$ttl = 7 * DAY_IN_SECONDS; |
| 13566 |
|
| 13567 |
if ( ! empty( $state['payment_link_id'] ) ) { |
| 13568 |
set_transient( 'ebook_store_adyen_link_' . sanitize_key( (string) $state['payment_link_id'] ), $state, $ttl ); |
| 13569 |
} |
| 13570 |
|
| 13571 |
if ( ! empty( $state['ebook_key'] ) ) { |
| 13572 |
set_transient( 'ebook_store_adyen_ebook_' . sanitize_key( (string) $state['ebook_key'] ), $state, $ttl ); |
| 13573 |
} |
| 13574 |
} |
| 13575 |
|
| 13576 |
function ebook_store_get_adyen_checkout_state( $context = array() ) { |
| 13577 |
$context = is_array( $context ) ? $context : array(); |
| 13578 |
$keys = array(); |
| 13579 |
|
| 13580 |
if ( ! empty( $context['payment_link_id'] ) ) { |
| 13581 |
$keys[] = 'ebook_store_adyen_link_' . sanitize_key( (string) $context['payment_link_id'] ); |
| 13582 |
} |
| 13583 |
|
| 13584 |
if ( ! empty( $context['ebook_key'] ) ) { |
| 13585 |
$keys[] = 'ebook_store_adyen_ebook_' . sanitize_key( (string) $context['ebook_key'] ); |
| 13586 |
} |
| 13587 |
|
| 13588 |
foreach ( $keys as $key ) { |
| 13589 |
$state = get_transient( $key ); |
| 13590 |
if ( is_array( $state ) ) { |
| 13591 |
return $state; |
| 13592 |
} |
| 13593 |
} |
| 13594 |
|
| 13595 |
return false; |
| 13596 |
} |
| 13597 |
|
| 13598 |
function ebook_store_clear_adyen_checkout_state( $state ) { |
| 13599 |
if ( ! is_array( $state ) ) { |
| 13600 |
return; |
| 13601 |
} |
| 13602 |
|
| 13603 |
if ( ! empty( $state['payment_link_id'] ) ) { |
| 13604 |
delete_transient( 'ebook_store_adyen_link_' . sanitize_key( (string) $state['payment_link_id'] ) ); |
| 13605 |
} |
| 13606 |
|
| 13607 |
if ( ! empty( $state['ebook_key'] ) ) { |
| 13608 |
delete_transient( 'ebook_store_adyen_ebook_' . sanitize_key( (string) $state['ebook_key'] ) ); |
| 13609 |
} |
| 13610 |
} |
| 13611 |
|
| 13612 |
function ebook_store_fetch_adyen_payment_link( $payment_link_id ) { |
| 13613 |
$payment_link_id = trim( (string) $payment_link_id ); |
| 13614 |
if ( $payment_link_id === '' ) { |
| 13615 |
return new WP_Error( 'adyen_missing_payment_link_id', 'Adyen payment link ID is missing' ); |
| 13616 |
} |
| 13617 |
|
| 13618 |
return ebook_store_adyen_request( 'GET', 'paymentLinks/' . rawurlencode( $payment_link_id ) ); |
| 13619 |
} |
| 13620 |
|
| 13621 |
function ebook_store_get_adyen_order_creation_lock_key( $psp_reference ) { |
| 13622 |
return 'ebook_store_adyen_lock_' . sanitize_key( (string) $psp_reference ); |
| 13623 |
} |
| 13624 |
|
| 13625 |
function ebook_store_acquire_adyen_order_creation_lock( $psp_reference ) { |
| 13626 |
$psp_reference = (string) $psp_reference; |
| 13627 |
if ( $psp_reference === '' ) { |
| 13628 |
return false; |
| 13629 |
} |
| 13630 |
|
| 13631 |
return add_option( ebook_store_get_adyen_order_creation_lock_key( $psp_reference ), time(), '', 'no' ); |
| 13632 |
} |
| 13633 |
|
| 13634 |
function ebook_store_release_adyen_order_creation_lock( $psp_reference ) { |
| 13635 |
$psp_reference = (string) $psp_reference; |
| 13636 |
if ( $psp_reference === '' ) { |
| 13637 |
return; |
| 13638 |
} |
| 13639 |
|
| 13640 |
delete_option( ebook_store_get_adyen_order_creation_lock_key( $psp_reference ) ); |
| 13641 |
} |
| 13642 |
|
| 13643 |
function ebook_store_wait_for_adyen_order( $psp_reference, $ebook_key = '' ) { |
| 13644 |
$psp_reference = (string) $psp_reference; |
| 13645 |
$ebook_key = (string) $ebook_key; |
| 13646 |
|
| 13647 |
for ( $attempt = 0; $attempt < 10; $attempt++ ) { |
| 13648 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'adyen_psp_reference', $psp_reference ); |
| 13649 |
if ( $existing_order_id ) { |
| 13650 |
return $existing_order_id; |
| 13651 |
} |
| 13652 |
|
| 13653 |
if ( $ebook_key !== '' ) { |
| 13654 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'ebook_key', $ebook_key ); |
| 13655 |
if ( $existing_order_id ) { |
| 13656 |
return $existing_order_id; |
| 13657 |
} |
| 13658 |
} |
| 13659 |
|
| 13660 |
usleep( 200000 ); |
| 13661 |
} |
| 13662 |
|
| 13663 |
return 0; |
| 13664 |
} |
| 13665 |
|
| 13666 |
function ebook_store_create_order_from_adyen_notification( $notification, $source = 'webhook' ) { |
| 13667 |
$notification = is_array( $notification ) ? $notification : array(); |
| 13668 |
$psp_reference = isset( $notification['pspReference'] ) ? (string) $notification['pspReference'] : ''; |
| 13669 |
$merchant_reference = isset( $notification['merchantReference'] ) ? (string) $notification['merchantReference'] : ''; |
| 13670 |
$additional_data = isset( $notification['additionalData'] ) && is_array( $notification['additionalData'] ) ? $notification['additionalData'] : array(); |
| 13671 |
$payment_link_id = isset( $additional_data['paymentLinkId'] ) ? (string) $additional_data['paymentLinkId'] : ''; |
| 13672 |
$success = isset( $notification['success'] ) ? strtolower( (string) $notification['success'] ) === 'true' : false; |
| 13673 |
$event_code = isset( $notification['eventCode'] ) ? strtoupper( (string) $notification['eventCode'] ) : ''; |
| 13674 |
|
| 13675 |
if ( $psp_reference === '' ) { |
| 13676 |
return new WP_Error( 'adyen_missing_psp_reference', 'Adyen webhook is missing the PSP reference' ); |
| 13677 |
} |
| 13678 |
|
| 13679 |
if ( $event_code !== 'AUTHORISATION' ) { |
| 13680 |
return new WP_Error( 'adyen_invalid_event_code', 'Adyen notification is not an AUTHORISATION event' ); |
| 13681 |
} |
| 13682 |
|
| 13683 |
if ( ! $success ) { |
| 13684 |
return new WP_Error( 'adyen_payment_not_authorised', 'Adyen payment was not authorised successfully' ); |
| 13685 |
} |
| 13686 |
|
| 13687 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'adyen_psp_reference', $psp_reference ); |
| 13688 |
if ( $existing_order_id ) { |
| 13689 |
return $existing_order_id; |
| 13690 |
} |
| 13691 |
|
| 13692 |
if ( $merchant_reference !== '' ) { |
| 13693 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'ebook_key', $merchant_reference ); |
| 13694 |
if ( $existing_order_id ) { |
| 13695 |
return $existing_order_id; |
| 13696 |
} |
| 13697 |
} |
| 13698 |
|
| 13699 |
$has_lock = ebook_store_acquire_adyen_order_creation_lock( $psp_reference ); |
| 13700 |
if ( ! $has_lock ) { |
| 13701 |
$existing_order_id = ebook_store_wait_for_adyen_order( $psp_reference, $merchant_reference ); |
| 13702 |
if ( $existing_order_id ) { |
| 13703 |
return $existing_order_id; |
| 13704 |
} |
| 13705 |
|
| 13706 |
return new WP_Error( 'adyen_order_creation_in_progress', 'Adyen order creation is already in progress' ); |
| 13707 |
} |
| 13708 |
|
| 13709 |
try { |
| 13710 |
$state = ebook_store_get_adyen_checkout_state( |
| 13711 |
array( |
| 13712 |
'payment_link_id' => $payment_link_id, |
| 13713 |
'ebook_key' => $merchant_reference, |
| 13714 |
) |
| 13715 |
); |
| 13716 |
$state = is_array( $state ) ? $state : array(); |
| 13717 |
|
| 13718 |
$payment_link = $payment_link_id !== '' ? ebook_store_fetch_adyen_payment_link( $payment_link_id ) : new WP_Error( 'adyen_missing_payment_link_id', 'Adyen payment link ID is missing' ); |
| 13719 |
if ( is_wp_error( $payment_link ) ) { |
| 13720 |
$payment_link = array(); |
| 13721 |
} |
| 13722 |
|
| 13723 |
$ebook_id = ! empty( $state['ebook_id'] ) ? absint( $state['ebook_id'] ) : 0; |
| 13724 |
if ( ! $ebook_id && ! empty( $payment_link['metadata']['ebook_id'] ) ) { |
| 13725 |
$ebook_id = absint( $payment_link['metadata']['ebook_id'] ); |
| 13726 |
} |
| 13727 |
|
| 13728 |
if ( ! $ebook_id ) { |
| 13729 |
return new WP_Error( 'adyen_missing_ebook', 'Adyen payment could not be matched to an ebook' ); |
| 13730 |
} |
| 13731 |
|
| 13732 |
$ebook_post = get_post( $ebook_id ); |
| 13733 |
if ( ! $ebook_post || 'ebook' !== $ebook_post->post_type ) { |
| 13734 |
return new WP_Error( 'adyen_invalid_ebook', 'Adyen payment points to an invalid ebook' ); |
| 13735 |
} |
| 13736 |
|
| 13737 |
$shopper_email = ''; |
| 13738 |
if ( ! empty( $payment_link['shopperEmail'] ) ) { |
| 13739 |
$shopper_email = sanitize_email( (string) $payment_link['shopperEmail'] ); |
| 13740 |
} |
| 13741 |
if ( $shopper_email === '' && ! empty( $additional_data['shopperEmail'] ) ) { |
| 13742 |
$shopper_email = sanitize_email( (string) $additional_data['shopperEmail'] ); |
| 13743 |
} |
| 13744 |
if ( $shopper_email === '' && ! empty( $state['buyer_email'] ) ) { |
| 13745 |
$shopper_email = sanitize_email( (string) $state['buyer_email'] ); |
| 13746 |
} |
| 13747 |
if ( $shopper_email === '' ) { |
| 13748 |
$shopper_email = 'adyen-buyer-' . strtolower( substr( preg_replace( '/[^a-zA-Z0-9]+/', '', $psp_reference ), 0, 12 ) ) . '@' . preg_replace( '/^www\./', '', (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); |
| 13749 |
} |
| 13750 |
|
| 13751 |
$shopper_name = ''; |
| 13752 |
if ( ! empty( $payment_link['shopperName'] ) && is_array( $payment_link['shopperName'] ) ) { |
| 13753 |
$shopper_name = trim( |
| 13754 |
( isset( $payment_link['shopperName']['firstName'] ) ? (string) $payment_link['shopperName']['firstName'] : '' ) . ' ' . |
| 13755 |
( isset( $payment_link['shopperName']['lastName'] ) ? (string) $payment_link['shopperName']['lastName'] : '' ) |
| 13756 |
); |
| 13757 |
} |
| 13758 |
$name_parts = ebook_store_parse_customer_name( $shopper_name ); |
| 13759 |
if ( $name_parts[0] === '' ) { |
| 13760 |
$name_parts[0] = __( 'Adyen', 'ebook-store' ); |
| 13761 |
$name_parts[1] = __( 'Buyer', 'ebook-store' ); |
| 13762 |
} |
| 13763 |
|
| 13764 |
$amount_value = isset( $notification['amount']['value'] ) ? ((float) $notification['amount']['value'] / 100) : 0.0; |
| 13765 |
$currency = isset( $notification['amount']['currency'] ) ? strtoupper( (string) $notification['amount']['currency'] ) : ebook_store_get_store_currency(); |
| 13766 |
$country_code = ! empty( $payment_link['countryCode'] ) ? strtoupper( (string) $payment_link['countryCode'] ) : ''; |
| 13767 |
|
| 13768 |
$order_data = array( |
| 13769 |
'first_name' => $name_parts[0], |
| 13770 |
'last_name' => $name_parts[1], |
| 13771 |
'payer_email' => $shopper_email, |
| 13772 |
'payment_status' => 'Completed', |
| 13773 |
'txn_id' => $psp_reference, |
| 13774 |
'payment_type' => 'adyen', |
| 13775 |
'mc_gross' => $amount_value, |
| 13776 |
'mc_fee' => 0, |
| 13777 |
'ebook_key' => $merchant_reference, |
| 13778 |
'ebook' => $ebook_id, |
| 13779 |
'post_id' => $ebook_id, |
| 13780 |
'mc_currency' => $currency, |
| 13781 |
'residence_country' => $country_code, |
| 13782 |
'downloads' => 0, |
| 13783 |
'adyen_psp_reference' => $psp_reference, |
| 13784 |
'adyen_payment_link_id' => $payment_link_id, |
| 13785 |
'adyen_event_code' => $event_code, |
| 13786 |
'adyen_order_source' => $source, |
| 13787 |
); |
| 13788 |
|
| 13789 |
$order_id = ebook_store_add_order( $order_data, true, false, $merchant_reference ); |
| 13790 |
if ( ! $order_id ) { |
| 13791 |
return new WP_Error( 'adyen_order_create_failed', 'Failed to create Ebook Store order from Adyen payment' ); |
| 13792 |
} |
| 13793 |
|
| 13794 |
ebook_store_clear_adyen_checkout_state( $state ); |
| 13795 |
ebook_store_ga4_track_purchase_for_order( $order_id ); |
| 13796 |
|
| 13797 |
return (int) $order_id; |
| 13798 |
} finally { |
| 13799 |
ebook_store_release_adyen_order_creation_lock( $psp_reference ); |
| 13800 |
} |
| 13801 |
} |
| 13802 |
|
| 13803 |
function ebook_store_recover_adyen_order_by_ebook_key( $ebook_key ) { |
| 13804 |
$ebook_key = (string) $ebook_key; |
| 13805 |
if ( $ebook_key === '' ) { |
| 13806 |
return new WP_Error( 'adyen_missing_ebook_key', 'Ebook key is missing for Adyen recovery' ); |
| 13807 |
} |
| 13808 |
|
| 13809 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'ebook_key', $ebook_key ); |
| 13810 |
if ( $existing_order_id ) { |
| 13811 |
return $existing_order_id; |
| 13812 |
} |
| 13813 |
|
| 13814 |
$state = ebook_store_get_adyen_checkout_state( |
| 13815 |
array( |
| 13816 |
'ebook_key' => $ebook_key, |
| 13817 |
) |
| 13818 |
); |
| 13819 |
if ( ! is_array( $state ) || empty( $state['payment_link_id'] ) ) { |
| 13820 |
return new WP_Error( 'adyen_checkout_state_missing', 'No Adyen checkout state found for this ebook key' ); |
| 13821 |
} |
| 13822 |
|
| 13823 |
$payment_link = ebook_store_fetch_adyen_payment_link( (string) $state['payment_link_id'] ); |
| 13824 |
if ( is_wp_error( $payment_link ) ) { |
| 13825 |
return $payment_link; |
| 13826 |
} |
| 13827 |
|
| 13828 |
if ( ! empty( $payment_link['status'] ) && strtolower( (string) $payment_link['status'] ) === 'completed' ) { |
| 13829 |
return new WP_Error( 'adyen_waiting_for_webhook', 'Adyen payment is completed but Ebook Store is still waiting for the webhook confirmation.' ); |
| 13830 |
} |
| 13831 |
|
| 13832 |
return 0; |
| 13833 |
} |
| 13834 |
|
| 13835 |
function ebook_store_create_adyen_payment_link( $ebook_id, $price, $title, $ebook_key ) { |
| 13836 |
$ebook_id = absint( $ebook_id ); |
| 13837 |
if ( ! $ebook_id ) { |
| 13838 |
$ebook_id = get_the_ID(); |
| 13839 |
} |
| 13840 |
|
| 13841 |
$checkout_page = get_option( 'ebook_store_checkout_page' ); |
| 13842 |
if ( ! $checkout_page ) { |
| 13843 |
$checkout_page = $ebook_id; |
| 13844 |
} |
| 13845 |
|
| 13846 |
$return_url = add_query_arg( |
| 13847 |
array( |
| 13848 |
'ebook_key' => $ebook_key, |
| 13849 |
'action' => 'thank_you', |
| 13850 |
'gateway' => 'adyen', |
| 13851 |
), |
| 13852 |
get_permalink( $checkout_page ) |
| 13853 |
); |
| 13854 |
|
| 13855 |
$country_code = ebook_store_get_adyen_country_code(); |
| 13856 |
$shopper_locale = ebook_store_get_adyen_shopper_locale(); |
| 13857 |
$merchant_account = trim( (string) get_option( 'adyen_merchant_account', '' ) ); |
| 13858 |
|
| 13859 |
if ( $merchant_account === '' ) { |
| 13860 |
ebook_store_log_adyen_event( |
| 13861 |
'Adyen payment link creation failed', |
| 13862 |
array( |
| 13863 |
'ebook_id' => $ebook_id, |
| 13864 |
'error' => 'Adyen merchant account not configured', |
| 13865 |
) |
| 13866 |
); |
| 13867 |
return false; |
| 13868 |
} |
| 13869 |
|
| 13870 |
$payload = array( |
| 13871 |
'merchantAccount' => $merchant_account, |
| 13872 |
'reference' => (string) $ebook_key, |
| 13873 |
'amount' => array( |
| 13874 |
'currency' => ebook_store_get_store_currency(), |
| 13875 |
'value' => (int) round( (float) $price * 100 ), |
| 13876 |
), |
| 13877 |
'description' => sprintf( 'Ebook Store order for %s', $title ), |
| 13878 |
'shopperLocale' => $shopper_locale, |
| 13879 |
'returnUrl' => $return_url, |
| 13880 |
'requiredShopperFields'=> array( 'shopperEmail', 'shopperName' ), |
| 13881 |
'expiresAt' => gmdate( 'Y-m-d\TH:i:s+00:00', time() + ( 30 * DAY_IN_SECONDS ) ), |
| 13882 |
'metadata' => array( |
| 13883 |
'ebook_id' => (string) $ebook_id, |
| 13884 |
'ebook_key' => (string) $ebook_key, |
| 13885 |
), |
| 13886 |
); |
| 13887 |
|
| 13888 |
if ( $country_code !== '' ) { |
| 13889 |
$payload['countryCode'] = $country_code; |
| 13890 |
} |
| 13891 |
|
| 13892 |
$response = ebook_store_adyen_request( |
| 13893 |
'POST', |
| 13894 |
'paymentLinks', |
| 13895 |
$payload, |
| 13896 |
array( |
| 13897 |
'Idempotency-Key' => wp_generate_uuid4(), |
| 13898 |
) |
| 13899 |
); |
| 13900 |
|
| 13901 |
if ( is_wp_error( $response ) ) { |
| 13902 |
ebook_store_log_adyen_event( |
| 13903 |
'Adyen payment link creation failed', |
| 13904 |
array( |
| 13905 |
'ebook_id' => $ebook_id, |
| 13906 |
'error' => $response->get_error_message(), |
| 13907 |
'data' => $response->get_error_data(), |
| 13908 |
) |
| 13909 |
); |
| 13910 |
return false; |
| 13911 |
} |
| 13912 |
|
| 13913 |
if ( empty( $response['url'] ) || empty( $response['id'] ) ) { |
| 13914 |
ebook_store_log_adyen_event( |
| 13915 |
'Adyen payment link response missing data', |
| 13916 |
array( |
| 13917 |
'ebook_id' => $ebook_id, |
| 13918 |
'response' => $response, |
| 13919 |
) |
| 13920 |
); |
| 13921 |
return false; |
| 13922 |
} |
| 13923 |
|
| 13924 |
$state = array( |
| 13925 |
'ebook_id' => $ebook_id, |
| 13926 |
'ebook_key' => (string) $ebook_key, |
| 13927 |
'title' => (string) $title, |
| 13928 |
'payment_link_id' => (string) $response['id'], |
| 13929 |
'store_amount' => (float) $price, |
| 13930 |
'store_currency' => ebook_store_get_store_currency(), |
| 13931 |
'country_code' => $country_code, |
| 13932 |
); |
| 13933 |
ebook_store_store_adyen_checkout_state( $state ); |
| 13934 |
|
| 13935 |
return (string) $response['url']; |
| 13936 |
} |
| 13937 |
|
| 13938 |
function ebook_store_verify_adyen_hmac( $notification ) { |
| 13939 |
$notification = is_array( $notification ) ? $notification : array(); |
| 13940 |
$additional_data = isset( $notification['additionalData'] ) && is_array( $notification['additionalData'] ) ? $notification['additionalData'] : array(); |
| 13941 |
$provided_signature = isset( $additional_data['hmacSignature'] ) ? trim( (string) $additional_data['hmacSignature'] ) : ''; |
| 13942 |
$hmac_key = trim( (string) get_option( 'adyen_hmac_key', '' ) ); |
| 13943 |
|
| 13944 |
if ( $provided_signature === '' || $hmac_key === '' ) { |
| 13945 |
return false; |
| 13946 |
} |
| 13947 |
|
| 13948 |
$signing_string = implode( |
| 13949 |
':', |
| 13950 |
array( |
| 13951 |
isset( $notification['pspReference'] ) ? (string) $notification['pspReference'] : '', |
| 13952 |
isset( $notification['originalReference'] ) ? (string) $notification['originalReference'] : '', |
| 13953 |
isset( $notification['merchantAccountCode'] ) ? (string) $notification['merchantAccountCode'] : '', |
| 13954 |
isset( $notification['merchantReference'] ) ? (string) $notification['merchantReference'] : '', |
| 13955 |
isset( $notification['amount']['value'] ) ? (string) $notification['amount']['value'] : '', |
| 13956 |
isset( $notification['amount']['currency'] ) ? (string) $notification['amount']['currency'] : '', |
| 13957 |
isset( $notification['eventCode'] ) ? (string) $notification['eventCode'] : '', |
| 13958 |
isset( $notification['success'] ) ? (string) $notification['success'] : '', |
| 13959 |
) |
| 13960 |
); |
| 13961 |
|
| 13962 |
$decoded_key = base64_decode( $hmac_key, true ); |
| 13963 |
if ( $decoded_key === false ) { |
| 13964 |
return false; |
| 13965 |
} |
| 13966 |
|
| 13967 |
$calculated_signature = base64_encode( hash_hmac( 'sha256', $signing_string, $decoded_key, true ) ); |
| 13968 |
|
| 13969 |
return hash_equals( $calculated_signature, $provided_signature ); |
| 13970 |
} |
| 13971 |
|
| 13972 |
function ebook_store_register_adyen_webhook() { |
| 13973 |
register_rest_route( |
| 13974 |
'ebook-store/v1', |
| 13975 |
'/adyen-webhook', |
| 13976 |
array( |
| 13977 |
'methods' => 'POST', |
| 13978 |
'callback' => 'ebook_store_handle_adyen_webhook', |
| 13979 |
'permission_callback' => '__return_true', |
| 13980 |
) |
| 13981 |
); |
| 13982 |
} |
| 13983 |
add_action( 'rest_api_init', 'ebook_store_register_adyen_webhook' ); |
| 13984 |
|
| 13985 |
function ebook_store_handle_adyen_webhook( WP_REST_Request $request ) { |
| 13986 |
$payload = $request->get_body(); |
| 13987 |
$data = json_decode( $payload, true ); |
| 13988 |
$items = isset( $data['notificationItems'] ) && is_array( $data['notificationItems'] ) ? $data['notificationItems'] : array(); |
| 13989 |
|
| 13990 |
if ( empty( $items ) ) { |
| 13991 |
ebook_store_log_adyen_event( |
| 13992 |
'Adyen webhook ignored empty payload', |
| 13993 |
array( |
| 13994 |
'payload' => $payload, |
| 13995 |
) |
| 13996 |
); |
| 13997 |
return new WP_REST_Response( '[accepted]', 200 ); |
| 13998 |
} |
| 13999 |
|
| 14000 |
foreach ( $items as $entry ) { |
| 14001 |
$notification = isset( $entry['NotificationRequestItem'] ) && is_array( $entry['NotificationRequestItem'] ) |
| 14002 |
? $entry['NotificationRequestItem'] |
| 14003 |
: array(); |
| 14004 |
if ( empty( $notification ) ) { |
| 14005 |
continue; |
| 14006 |
} |
| 14007 |
|
| 14008 |
if ( ! ebook_store_verify_adyen_hmac( $notification ) ) { |
| 14009 |
ebook_store_log_adyen_event( |
| 14010 |
'Adyen webhook HMAC verification failed', |
| 14011 |
array( |
| 14012 |
'notification' => $notification, |
| 14013 |
) |
| 14014 |
); |
| 14015 |
return new WP_REST_Response( 'invalid hmac', 400 ); |
| 14016 |
} |
| 14017 |
|
| 14018 |
$event_code = isset( $notification['eventCode'] ) ? strtoupper( (string) $notification['eventCode'] ) : ''; |
| 14019 |
$success = isset( $notification['success'] ) ? strtolower( (string) $notification['success'] ) === 'true' : false; |
| 14020 |
|
| 14021 |
switch ( $event_code ) { |
| 14022 |
case 'AUTHORISATION': |
| 14023 |
$result = ebook_store_create_order_from_adyen_notification( $notification, 'webhook' ); |
| 14024 |
if ( is_wp_error( $result ) ) { |
| 14025 |
ebook_store_log_adyen_event( |
| 14026 |
'Adyen AUTHORISATION processing skipped', |
| 14027 |
array( |
| 14028 |
'error' => $result->get_error_message(), |
| 14029 |
'notification' => $notification, |
| 14030 |
) |
| 14031 |
); |
| 14032 |
} else { |
| 14033 |
ebook_store_log_adyen_event( |
| 14034 |
'Adyen order created from AUTHORISATION', |
| 14035 |
array( |
| 14036 |
'order_id' => $result, |
| 14037 |
'pspReference' => isset( $notification['pspReference'] ) ? $notification['pspReference'] : '', |
| 14038 |
) |
| 14039 |
); |
| 14040 |
} |
| 14041 |
break; |
| 14042 |
|
| 14043 |
case 'REFUND': |
| 14044 |
if ( $success && ! empty( $notification['originalReference'] ) ) { |
| 14045 |
$order_id = ebook_store_get_order_id_by_meta( 'adyen_psp_reference', (string) $notification['originalReference'] ); |
| 14046 |
if ( $order_id ) { |
| 14047 |
update_post_meta( $order_id, 'payment_status', 'Refunded' ); |
| 14048 |
update_post_meta( $order_id, 'refund_gateway', 'adyen' ); |
| 14049 |
update_post_meta( $order_id, 'refund_gateway_id', isset( $notification['pspReference'] ) ? (string) $notification['pspReference'] : '' ); |
| 14050 |
update_post_meta( $order_id, 'refund_gateway_status', 'received' ); |
| 14051 |
update_post_meta( $order_id, 'refund_date', current_time( 'mysql' ) ); |
| 14052 |
} |
| 14053 |
} |
| 14054 |
break; |
| 14055 |
|
| 14056 |
case 'REFUND_FAILED': |
| 14057 |
case 'REFUNDED_REVERSED': |
| 14058 |
if ( ! empty( $notification['originalReference'] ) ) { |
| 14059 |
$order_id = ebook_store_get_order_id_by_meta( 'adyen_psp_reference', (string) $notification['originalReference'] ); |
| 14060 |
if ( $order_id ) { |
| 14061 |
update_post_meta( $order_id, 'payment_status', 'Completed' ); |
| 14062 |
update_post_meta( $order_id, 'refund_gateway_status', strtolower( $event_code ) ); |
| 14063 |
} |
| 14064 |
} |
| 14065 |
break; |
| 14066 |
} |
| 14067 |
} |
| 14068 |
|
| 14069 |
return new WP_REST_Response( '[accepted]', 200 ); |
| 14070 |
} |
| 14071 |
|
| 14072 |
/** |
| 14073 |
* Log a stripe webhook event. |
| 14074 |
* |
| 14075 |
* Writes through ebook_store_log(), which keeps the file outside the web root, |
| 14076 |
* caps its size and stays silent unless logging is switched on. The old version |
| 14077 |
* appended to an uncapped .txt inside the plugin folder that any visitor could |
| 14078 |
* fetch; one of those files reached 118 MB and contained live order tokens. |
| 14079 |
* |
| 14080 |
* @param string $message Message. |
| 14081 |
* @param array $context Context. |
| 14082 |
*/ |
| 14083 |
function ebook_store_log_stripe_event( $message, $context = array() ) { |
| 14084 |
ebook_store_log( 'stripe', $message, $context ); |
| 14085 |
} |
| 14086 |
|
| 14087 |
function ebook_store_bootstrap_stripe_sdk() { |
| 14088 |
require_once plugin_dir_path( __FILE__ ) . 'vendor/autoload.php'; |
| 14089 |
|
| 14090 |
$stripe_secret_key = (string) get_option( 'stripe_secret_key' ); |
| 14091 |
if ( $stripe_secret_key === '' ) { |
| 14092 |
return new WP_Error( 'stripe_missing_secret_key', 'Stripe secret key not configured' ); |
| 14093 |
} |
| 14094 |
|
| 14095 |
\Stripe\Stripe::setApiKey( $stripe_secret_key ); |
| 14096 |
|
| 14097 |
return true; |
| 14098 |
} |
| 14099 |
|
| 14100 |
function ebook_store_get_order_id_by_meta( $meta_key, $meta_value ) { |
| 14101 |
if ( $meta_value === '' || $meta_value === null ) { |
| 14102 |
return 0; |
| 14103 |
} |
| 14104 |
|
| 14105 |
$orders = get_posts( |
| 14106 |
array( |
| 14107 |
'post_type' => 'ebook_order', |
| 14108 |
'post_status' => 'any', |
| 14109 |
'posts_per_page' => 1, |
| 14110 |
'fields' => 'ids', |
| 14111 |
'meta_key' => $meta_key, |
| 14112 |
'meta_value' => (string) $meta_value, |
| 14113 |
) |
| 14114 |
); |
| 14115 |
|
| 14116 |
return empty( $orders ) ? 0 : (int) $orders[0]; |
| 14117 |
} |
| 14118 |
|
| 14119 |
function ebook_store_parse_customer_name( $name ) { |
| 14120 |
$name = trim( (string) $name ); |
| 14121 |
if ( $name === '' ) { |
| 14122 |
return array( '', '' ); |
| 14123 |
} |
| 14124 |
|
| 14125 |
$name_parts = preg_split( '/\s+/', $name, 2 ); |
| 14126 |
|
| 14127 |
return array( |
| 14128 |
isset( $name_parts[0] ) ? $name_parts[0] : '', |
| 14129 |
isset( $name_parts[1] ) ? $name_parts[1] : '', |
| 14130 |
); |
| 14131 |
} |
| 14132 |
|
| 14133 |
function ebook_store_extract_stripe_billing_details( $session ) { |
| 14134 |
$details = array( |
| 14135 |
'name' => '', |
| 14136 |
'email' => '', |
| 14137 |
'address' => array(), |
| 14138 |
'charge' => null, |
| 14139 |
); |
| 14140 |
|
| 14141 |
if ( ! empty( $session->customer_details ) ) { |
| 14142 |
$details['name'] = isset( $session->customer_details->name ) ? (string) $session->customer_details->name : ''; |
| 14143 |
$details['email'] = isset( $session->customer_details->email ) ? (string) $session->customer_details->email : ''; |
| 14144 |
$details['address'] = ! empty( $session->customer_details->address ) ? (array) $session->customer_details->address : array(); |
| 14145 |
} |
| 14146 |
|
| 14147 |
$payment_intent_id = ''; |
| 14148 |
if ( ! empty( $session->payment_intent ) ) { |
| 14149 |
$payment_intent_id = is_object( $session->payment_intent ) && isset( $session->payment_intent->id ) |
| 14150 |
? (string) $session->payment_intent->id |
| 14151 |
: (string) $session->payment_intent; |
| 14152 |
} |
| 14153 |
|
| 14154 |
if ( $payment_intent_id !== '' ) { |
| 14155 |
try { |
| 14156 |
$payment_intent = \Stripe\PaymentIntent::retrieve( $payment_intent_id ); |
| 14157 |
$charge = null; |
| 14158 |
|
| 14159 |
if ( ! empty( $payment_intent->latest_charge ) ) { |
| 14160 |
$charge = is_object( $payment_intent->latest_charge ) && isset( $payment_intent->latest_charge->id ) |
| 14161 |
? $payment_intent->latest_charge |
| 14162 |
: \Stripe\Charge::retrieve( (string) $payment_intent->latest_charge ); |
| 14163 |
} elseif ( ! empty( $payment_intent->charges ) && ! empty( $payment_intent->charges->data[0] ) ) { |
| 14164 |
$charge = $payment_intent->charges->data[0]; |
| 14165 |
} |
| 14166 |
|
| 14167 |
if ( $charge && ! empty( $charge->billing_details ) ) { |
| 14168 |
$details['charge'] = $charge; |
| 14169 |
|
| 14170 |
if ( $details['name'] === '' && ! empty( $charge->billing_details->name ) ) { |
| 14171 |
$details['name'] = (string) $charge->billing_details->name; |
| 14172 |
} |
| 14173 |
|
| 14174 |
if ( $details['email'] === '' && ! empty( $charge->billing_details->email ) ) { |
| 14175 |
$details['email'] = (string) $charge->billing_details->email; |
| 14176 |
} |
| 14177 |
|
| 14178 |
if ( empty( $details['address'] ) && ! empty( $charge->billing_details->address ) ) { |
| 14179 |
$details['address'] = (array) $charge->billing_details->address; |
| 14180 |
} |
| 14181 |
} |
| 14182 |
} catch ( Exception $e ) { |
| 14183 |
ebook_store_log_stripe_event( |
| 14184 |
'Stripe billing details lookup failed', |
| 14185 |
array( |
| 14186 |
'payment_intent_id' => $payment_intent_id, |
| 14187 |
'error' => $e->getMessage(), |
| 14188 |
) |
| 14189 |
); |
| 14190 |
} |
| 14191 |
} |
| 14192 |
|
| 14193 |
return $details; |
| 14194 |
} |
| 14195 |
|
| 14196 |
function ebook_store_find_stripe_session_by_payment_intent( $payment_intent_id ) { |
| 14197 |
if ( $payment_intent_id === '' ) { |
| 14198 |
return null; |
| 14199 |
} |
| 14200 |
|
| 14201 |
try { |
| 14202 |
$sessions = \Stripe\Checkout\Session::all( |
| 14203 |
array( |
| 14204 |
'payment_intent' => $payment_intent_id, |
| 14205 |
'limit' => 1, |
| 14206 |
) |
| 14207 |
); |
| 14208 |
} catch ( Exception $e ) { |
| 14209 |
ebook_store_log_stripe_event( |
| 14210 |
'Stripe session lookup by payment intent failed', |
| 14211 |
array( |
| 14212 |
'payment_intent_id' => $payment_intent_id, |
| 14213 |
'error' => $e->getMessage(), |
| 14214 |
) |
| 14215 |
); |
| 14216 |
|
| 14217 |
return null; |
| 14218 |
} |
| 14219 |
|
| 14220 |
return ! empty( $sessions->data[0] ) ? $sessions->data[0] : null; |
| 14221 |
} |
| 14222 |
|
| 14223 |
function ebook_store_find_stripe_session_by_ebook_key( $ebook_key ) { |
| 14224 |
$ebook_key = (string) $ebook_key; |
| 14225 |
if ( $ebook_key === '' ) { |
| 14226 |
return null; |
| 14227 |
} |
| 14228 |
|
| 14229 |
try { |
| 14230 |
$sessions = \Stripe\Checkout\Session::all( |
| 14231 |
array( |
| 14232 |
'limit' => 25, |
| 14233 |
) |
| 14234 |
); |
| 14235 |
} catch ( Exception $e ) { |
| 14236 |
ebook_store_log_stripe_event( |
| 14237 |
'Stripe session lookup by ebook key failed', |
| 14238 |
array( |
| 14239 |
'ebook_key' => $ebook_key, |
| 14240 |
'error' => $e->getMessage(), |
| 14241 |
) |
| 14242 |
); |
| 14243 |
|
| 14244 |
return null; |
| 14245 |
} |
| 14246 |
|
| 14247 |
foreach ( $sessions->data as $session ) { |
| 14248 |
if ( |
| 14249 |
! empty( $session->metadata ) |
| 14250 |
&& isset( $session->metadata->ebook_key ) |
| 14251 |
&& (string) $session->metadata->ebook_key === $ebook_key |
| 14252 |
) { |
| 14253 |
return $session; |
| 14254 |
} |
| 14255 |
} |
| 14256 |
|
| 14257 |
return null; |
| 14258 |
} |
| 14259 |
|
| 14260 |
function ebook_store_create_order_from_stripe_session( $session, $source = 'webhook' ) { |
| 14261 |
if ( empty( $session ) || empty( $session->id ) ) { |
| 14262 |
return new WP_Error( 'stripe_missing_session', 'Stripe session data is missing' ); |
| 14263 |
} |
| 14264 |
|
| 14265 |
$session_id = (string) $session->id; |
| 14266 |
$payment_status = isset( $session->payment_status ) ? (string) $session->payment_status : ''; |
| 14267 |
$payment_intent_id = ''; |
| 14268 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'stripe_session_id', $session_id ); |
| 14269 |
|
| 14270 |
if ( $existing_order_id ) { |
| 14271 |
return $existing_order_id; |
| 14272 |
} |
| 14273 |
|
| 14274 |
if ( ! empty( $session->payment_intent ) ) { |
| 14275 |
$payment_intent_id = is_object( $session->payment_intent ) && isset( $session->payment_intent->id ) |
| 14276 |
? (string) $session->payment_intent->id |
| 14277 |
: (string) $session->payment_intent; |
| 14278 |
|
| 14279 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'stripe_payment_intent_id', $payment_intent_id ); |
| 14280 |
if ( $existing_order_id ) { |
| 14281 |
return $existing_order_id; |
| 14282 |
} |
| 14283 |
} |
| 14284 |
|
| 14285 |
if ( $payment_status !== 'paid' ) { |
| 14286 |
return new WP_Error( |
| 14287 |
'stripe_payment_not_paid', |
| 14288 |
sprintf( 'Stripe session %s is not paid (%s)', $session_id, $payment_status ) |
| 14289 |
); |
| 14290 |
} |
| 14291 |
|
| 14292 |
$metadata = ! empty( $session->metadata ) ? $session->metadata : (object) array(); |
| 14293 |
$ebook_id = isset( $metadata->ebook_id ) ? absint( $metadata->ebook_id ) : 0; |
| 14294 |
$ebook_key = isset( $metadata->ebook_key ) ? (string) $metadata->ebook_key : ''; |
| 14295 |
|
| 14296 |
if ( ! $ebook_id && ! empty( $session->client_reference_id ) ) { |
| 14297 |
$ebook_id = absint( $session->client_reference_id ); |
| 14298 |
} |
| 14299 |
|
| 14300 |
if ( $ebook_key !== '' ) { |
| 14301 |
$existing_order_id = ebook_store_get_order_id_by_meta( 'ebook_key', $ebook_key ); |
| 14302 |
if ( $existing_order_id ) { |
| 14303 |
return $existing_order_id; |
| 14304 |
} |
| 14305 |
} |
| 14306 |
|
| 14307 |
if ( ! $ebook_id ) { |
| 14308 |
return new WP_Error( 'stripe_missing_ebook', sprintf( 'Stripe session %s has no ebook_id metadata', $session_id ) ); |
| 14309 |
} |
| 14310 |
|
| 14311 |
$ebook_post = get_post( $ebook_id ); |
| 14312 |
if ( ! $ebook_post || 'ebook' !== $ebook_post->post_type ) { |
| 14313 |
return new WP_Error( 'stripe_invalid_ebook', sprintf( 'Stripe session %s points to invalid ebook %s', $session_id, $ebook_id ) ); |
| 14314 |
} |
| 14315 |
|
| 14316 |
$billing_details = ebook_store_extract_stripe_billing_details( $session ); |
| 14317 |
$name_parts = ebook_store_parse_customer_name( $billing_details['name'] ); |
| 14318 |
$address = is_array( $billing_details['address'] ) ? $billing_details['address'] : array(); |
| 14319 |
$amount_total = isset( $session->amount_total ) ? ((float) $session->amount_total / 100) : 0; |
| 14320 |
$amount_value = isset( $metadata->price ) && $metadata->price !== '' ? (float) $metadata->price : $amount_total; |
| 14321 |
$txn_id = $payment_intent_id !== '' ? $payment_intent_id : $session_id; |
| 14322 |
|
| 14323 |
$order_data = array( |
| 14324 |
'first_name' => $name_parts[0], |
| 14325 |
'last_name' => $name_parts[1], |
| 14326 |
'payer_email' => (string) $billing_details['email'], |
| 14327 |
'payment_status' => 'Completed', |
| 14328 |
'txn_id' => $txn_id, |
| 14329 |
'payment_type' => 'stripe', |
| 14330 |
'mc_gross' => $amount_value, |
| 14331 |
'ebook_key' => $ebook_key, |
| 14332 |
'ebook' => $ebook_id, |
| 14333 |
'post_id' => $ebook_id, |
| 14334 |
'mc_currency' => isset( $session->currency ) && $session->currency ? strtoupper( (string) $session->currency ) : ebook_store_get_store_currency(), |
| 14335 |
'residence_country' => isset( $address['country'] ) ? (string) $address['country'] : '', |
| 14336 |
'address_name' => (string) $billing_details['name'], |
| 14337 |
'address_street' => isset( $address['line1'] ) ? (string) $address['line1'] : '', |
| 14338 |
'address_city' => isset( $address['city'] ) ? (string) $address['city'] : '', |
| 14339 |
'address_state' => isset( $address['state'] ) ? (string) $address['state'] : '', |
| 14340 |
'address_zip' => isset( $address['postal_code'] ) ? (string) $address['postal_code'] : '', |
| 14341 |
'address_country' => isset( $address['country'] ) ? (string) $address['country'] : '', |
| 14342 |
'address_status' => 'confirmed', |
| 14343 |
'downloads' => 0, |
| 14344 |
'stripe_session_id' => $session_id, |
| 14345 |
'stripe_payment_intent_id' => $payment_intent_id, |
| 14346 |
'stripe_order_source' => $source, |
| 14347 |
); |
| 14348 |
|
| 14349 |
$order_id = ebook_store_add_order( $order_data, true, false, $ebook_key ); |
| 14350 |
|
| 14351 |
if ( ! $order_id ) { |
| 14352 |
return new WP_Error( 'stripe_order_create_failed', sprintf( 'Failed to create order from Stripe session %s', $session_id ) ); |
| 14353 |
} |
| 14354 |
|
| 14355 |
ebook_store_ga4_track_purchase_for_order( $order_id ); |
| 14356 |
|
| 14357 |
return (int) $order_id; |
| 14358 |
} |
| 14359 |
|
| 14360 |
function ebook_store_recover_stripe_order_by_ebook_key( $ebook_key ) { |
| 14361 |
$bootstrap = ebook_store_bootstrap_stripe_sdk(); |
| 14362 |
if ( is_wp_error( $bootstrap ) ) { |
| 14363 |
return $bootstrap; |
| 14364 |
} |
| 14365 |
|
| 14366 |
$session = ebook_store_find_stripe_session_by_ebook_key( $ebook_key ); |
| 14367 |
if ( ! $session ) { |
| 14368 |
return new WP_Error( 'stripe_session_not_found', sprintf( 'No Stripe session found for ebook key %s', $ebook_key ) ); |
| 14369 |
} |
| 14370 |
|
| 14371 |
return ebook_store_create_order_from_stripe_session( $session, 'thank_you' ); |
| 14372 |
} |
| 14373 |
|
| 14374 |
function ebook_store_recover_stripe_order_by_session_id( $session_id ) { |
| 14375 |
$session_id = (string) $session_id; |
| 14376 |
if ( $session_id === '' ) { |
| 14377 |
return new WP_Error( 'stripe_missing_session_id', 'Stripe session ID is missing' ); |
| 14378 |
} |
| 14379 |
|
| 14380 |
$bootstrap = ebook_store_bootstrap_stripe_sdk(); |
| 14381 |
if ( is_wp_error( $bootstrap ) ) { |
| 14382 |
return $bootstrap; |
| 14383 |
} |
| 14384 |
|
| 14385 |
try { |
| 14386 |
$session = \Stripe\Checkout\Session::retrieve( $session_id ); |
| 14387 |
} catch ( Exception $e ) { |
| 14388 |
return new WP_Error( 'stripe_session_lookup_failed', $e->getMessage() ); |
| 14389 |
} |
| 14390 |
|
| 14391 |
return ebook_store_create_order_from_stripe_session( $session, 'thank_you_session' ); |
| 14392 |
} |
| 14393 |
|
| 14394 |
function ebook_store_create_stripe_checkout($ebook_id, $price, $title, $ebook_key) { |
| 14395 |
$ebook_id = absint($ebook_id); |
| 14396 |
if (!$ebook_id) { |
| 14397 |
$ebook_id = get_the_ID(); |
| 14398 |
} |
| 14399 |
|
| 14400 |
try { |
| 14401 |
$bootstrap = ebook_store_bootstrap_stripe_sdk(); |
| 14402 |
if (is_wp_error($bootstrap)) { |
| 14403 |
throw new Exception($bootstrap->get_error_message()); |
| 14404 |
} |
| 14405 |
|
| 14406 |
$checkout_page = get_option('ebook_store_checkout_page'); |
| 14407 |
$metadata = [ |
| 14408 |
'ebook_id' => $ebook_id, |
| 14409 |
'ebook_key' => $ebook_key, |
| 14410 |
'price' => $price, |
| 14411 |
'title' => $title |
| 14412 |
]; |
| 14413 |
|
| 14414 |
$success_url = add_query_arg( |
| 14415 |
array( |
| 14416 |
'ebook_key' => $metadata['ebook_key'], |
| 14417 |
'action' => 'thank_you', |
| 14418 |
'stripe_session_id' => '{CHECKOUT_SESSION_ID}' |
| 14419 |
), |
| 14420 |
get_permalink($checkout_page) |
| 14421 |
); |
| 14422 |
|
| 14423 |
$session_data = [ |
| 14424 |
'line_items' => [[ |
| 14425 |
'price_data' => [ |
| 14426 |
'currency' => ebook_store_get_store_currency(), |
| 14427 |
'product_data' => [ |
| 14428 |
'name' => $title, |
| 14429 |
], |
| 14430 |
'unit_amount' => $price * 100, |
| 14431 |
], |
| 14432 |
'quantity' => 1, |
| 14433 |
]], |
| 14434 |
'mode' => 'payment', |
| 14435 |
'success_url' => $success_url, |
| 14436 |
'cancel_url' => get_option('stripe_cancel_url', home_url()), |
| 14437 |
'metadata' => $metadata, |
| 14438 |
'client_reference_id' => (string) $ebook_id, |
| 14439 |
'payment_intent_data' => [ |
| 14440 |
'metadata' => $metadata, |
| 14441 |
], |
| 14442 |
'customer_email' => isset($_POST['email']) ? sanitize_email($_POST['email']) : null |
| 14443 |
]; |
| 14444 |
|
| 14445 |
$checkout_session = \Stripe\Checkout\Session::create($session_data); |
| 14446 |
|
| 14447 |
return $checkout_session->url; |
| 14448 |
} catch(Exception $e) { |
| 14449 |
ebook_store_log_stripe_event( |
| 14450 |
'Stripe checkout creation failed', |
| 14451 |
[ |
| 14452 |
'ebook_id' => $ebook_id, |
| 14453 |
'error' => $e->getMessage(), |
| 14454 |
] |
| 14455 |
); |
| 14456 |
return false; |
| 14457 |
} |
| 14458 |
} |
| 14459 |
|
| 14460 |
// Add this near the other add_action calls |
| 14461 |
add_action('rest_api_init', 'ebook_store_register_stripe_webhook'); |
| 14462 |
|
| 14463 |
function ebook_store_register_stripe_webhook() { |
| 14464 |
register_rest_route('ebook-store/v1', '/stripe-webhook', array( |
| 14465 |
'methods' => 'POST', |
| 14466 |
'callback' => 'ebook_store_handle_stripe_webhook', |
| 14467 |
'permission_callback' => '__return_true' // Public endpoint |
| 14468 |
)); |
| 14469 |
} |
| 14470 |
|
| 14471 |
function ebook_store_handle_stripe_webhook(WP_REST_Request $request) { |
| 14472 |
$payload = $request->get_body(); |
| 14473 |
$sig_header = $request->get_header( 'stripe-signature' ); |
| 14474 |
|
| 14475 |
try { |
| 14476 |
$bootstrap = ebook_store_bootstrap_stripe_sdk(); |
| 14477 |
if ( is_wp_error( $bootstrap ) ) { |
| 14478 |
throw new Exception( $bootstrap->get_error_message() ); |
| 14479 |
} |
| 14480 |
|
| 14481 |
$webhook_secret = (string) get_option( 'stripe_webhook_secret' ); |
| 14482 |
if ( $webhook_secret === '' ) { |
| 14483 |
throw new Exception( 'Stripe webhook secret not configured' ); |
| 14484 |
} |
| 14485 |
|
| 14486 |
$event = \Stripe\Webhook::constructEvent( $payload, $sig_header, $webhook_secret ); |
| 14487 |
|
| 14488 |
ebook_store_log_stripe_event( |
| 14489 |
'Stripe webhook verified', |
| 14490 |
array( |
| 14491 |
'type' => $event->type, |
| 14492 |
'id' => isset( $event->id ) ? $event->id : '', |
| 14493 |
) |
| 14494 |
); |
| 14495 |
|
| 14496 |
switch ( $event->type ) { |
| 14497 |
case 'checkout.session.completed': |
| 14498 |
case 'checkout.session.async_payment_succeeded': |
| 14499 |
$order_id = ebook_store_create_order_from_stripe_session( $event->data->object, 'webhook' ); |
| 14500 |
if ( is_wp_error( $order_id ) ) { |
| 14501 |
ebook_store_log_stripe_event( |
| 14502 |
'Stripe checkout session order creation skipped', |
| 14503 |
array( |
| 14504 |
'type' => $event->type, |
| 14505 |
'error' => $order_id->get_error_message(), |
| 14506 |
) |
| 14507 |
); |
| 14508 |
} else { |
| 14509 |
ebook_store_log_stripe_event( |
| 14510 |
'Stripe order created from checkout session', |
| 14511 |
array( |
| 14512 |
'type' => $event->type, |
| 14513 |
'order_id' => $order_id, |
| 14514 |
'session' => isset( $event->data->object->id ) ? $event->data->object->id : '', |
| 14515 |
) |
| 14516 |
); |
| 14517 |
} |
| 14518 |
break; |
| 14519 |
|
| 14520 |
case 'charge.updated': |
| 14521 |
case 'charge.succeeded': |
| 14522 |
$charge = $event->data->object; |
| 14523 |
|
| 14524 |
if ( empty( $charge->paid ) || $charge->status !== 'succeeded' ) { |
| 14525 |
break; |
| 14526 |
} |
| 14527 |
|
| 14528 |
$payment_intent_id = ! empty( $charge->payment_intent ) ? (string) $charge->payment_intent : ''; |
| 14529 |
$session = ebook_store_find_stripe_session_by_payment_intent( $payment_intent_id ); |
| 14530 |
|
| 14531 |
if ( ! $session ) { |
| 14532 |
ebook_store_log_stripe_event( |
| 14533 |
'Stripe charge event had no checkout session match', |
| 14534 |
array( |
| 14535 |
'type' => $event->type, |
| 14536 |
'payment_intent_id' => $payment_intent_id, |
| 14537 |
'charge_id' => isset( $charge->id ) ? $charge->id : '', |
| 14538 |
) |
| 14539 |
); |
| 14540 |
break; |
| 14541 |
} |
| 14542 |
|
| 14543 |
$order_id = ebook_store_create_order_from_stripe_session( $session, 'charge_event' ); |
| 14544 |
if ( is_wp_error( $order_id ) ) { |
| 14545 |
ebook_store_log_stripe_event( |
| 14546 |
'Stripe charge event order creation skipped', |
| 14547 |
array( |
| 14548 |
'type' => $event->type, |
| 14549 |
'payment_intent_id' => $payment_intent_id, |
| 14550 |
'error' => $order_id->get_error_message(), |
| 14551 |
) |
| 14552 |
); |
| 14553 |
} else { |
| 14554 |
ebook_store_log_stripe_event( |
| 14555 |
'Stripe order created from charge event', |
| 14556 |
array( |
| 14557 |
'type' => $event->type, |
| 14558 |
'order_id' => $order_id, |
| 14559 |
'payment_intent_id' => $payment_intent_id, |
| 14560 |
) |
| 14561 |
); |
| 14562 |
} |
| 14563 |
break; |
| 14564 |
|
| 14565 |
default: |
| 14566 |
ebook_store_log_stripe_event( |
| 14567 |
'Stripe webhook ignored event', |
| 14568 |
array( |
| 14569 |
'type' => $event->type, |
| 14570 |
) |
| 14571 |
); |
| 14572 |
break; |
| 14573 |
} |
| 14574 |
|
| 14575 |
return new WP_REST_Response( array( 'received' => true ), 200 ); |
| 14576 |
} catch ( Exception $e ) { |
| 14577 |
ebook_store_log_stripe_event( |
| 14578 |
'Stripe webhook error', |
| 14579 |
array( |
| 14580 |
'error_class' => get_class( $e ), |
| 14581 |
'message' => $e->getMessage(), |
| 14582 |
) |
| 14583 |
); |
| 14584 |
|
| 14585 |
return new WP_REST_Response( |
| 14586 |
array( 'error' => $e->getMessage() ), |
| 14587 |
$e instanceof \Stripe\Exception\SignatureVerificationException ? 400 : 500 |
| 14588 |
); |
| 14589 |
} |
| 14590 |
} |
| 14591 |
|