Changeset 3494980
- Timestamp:
- 03/30/2026 10:13:52 PM (42 hours ago)
- Location:
- pdfdk-block-optimize/trunk
- Files:
-
- 7 edited
-
admin/css/admin.css (modified) (1 diff)
-
admin/js/admin.js (modified) (11 diffs)
-
includes/class-pdfdk-ajax.php (modified) (2 diffs)
-
includes/class-pdfdk-blocker.php (modified) (3 diffs)
-
includes/class-pdfdk-media.php (modified) (2 diffs)
-
pdfdk-block-optimize.php (modified) (5 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pdfdk-block-optimize/trunk/admin/css/admin.css
r3488410 r3494980 960 960 } 961 961 } 962 963 /* ===================================================== 964 Batch Progress Notice 965 ===================================================== */ 966 967 .pdfdk-batch-notice { 968 border-left-color: var(--pdfdk-primary) !important; 969 padding: 0 !important; 970 overflow: hidden; 971 } 972 973 .pdfdk-batch-notice.pdfdk-batch-done { 974 border-left-color: var(--pdfdk-green) !important; 975 } 976 977 .pdfdk-batch-notice.pdfdk-batch-has-errors { 978 border-left-color: #f0b429 !important; 979 } 980 981 .pdfdk-batch-notice-inner { 982 display: flex; 983 align-items: center; 984 gap: 10px; 985 padding: 10px 12px; 986 } 987 988 .pdfdk-batch-icon { 989 font-size: 18px; 990 width: 20px; 991 height: 20px; 992 color: var(--pdfdk-primary); 993 flex-shrink: 0; 994 } 995 996 .pdfdk-batch-notice.pdfdk-batch-done .pdfdk-batch-icon { 997 color: var(--pdfdk-green); 998 } 999 1000 .pdfdk-batch-text { 1001 flex: 1; 1002 min-width: 0; 1003 } 1004 1005 .pdfdk-batch-title { 1006 display: block; 1007 font-size: 13px; 1008 color: var(--pdfdk-text); 1009 } 1010 1011 .pdfdk-batch-sub { 1012 display: block; 1013 font-size: 12px; 1014 color: var(--pdfdk-text-secondary); 1015 margin-top: 1px; 1016 } 1017 1018 .pdfdk-batch-dismiss { 1019 background: none; 1020 border: none; 1021 cursor: pointer; 1022 color: var(--pdfdk-text-secondary); 1023 font-size: 16px; 1024 line-height: 1; 1025 padding: 4px 6px; 1026 border-radius: 4px; 1027 flex-shrink: 0; 1028 transition: background 0.15s, color 0.15s; 1029 } 1030 1031 .pdfdk-batch-dismiss:hover { 1032 background: var(--pdfdk-border); 1033 color: var(--pdfdk-text); 1034 } 1035 1036 .pdfdk-batch-bar-wrap { 1037 height: 4px; 1038 background: var(--pdfdk-border); 1039 width: 100%; 1040 } 1041 1042 .pdfdk-batch-bar-fill { 1043 height: 100%; 1044 background: var(--pdfdk-primary); 1045 width: 0%; 1046 transition: width 0.5s ease, background 0.3s ease; 1047 } 1048 1049 .pdfdk-batch-notice.pdfdk-batch-done .pdfdk-batch-bar-fill { 1050 background: var(--pdfdk-green); 1051 } 1052 1053 /* Spin animation for the processing icon */ 1054 @keyframes pdfdk-spin { 1055 from { transform: rotate(0deg); } 1056 to { transform: rotate(360deg); } 1057 } 1058 1059 .pdfdk-spin { 1060 display: inline-block; 1061 animation: pdfdk-spin 1s linear infinite; 1062 } -
pdfdk-block-optimize/trunk/admin/js/admin.js
r3488410 r3494980 31 31 initWatermarkButtons(); 32 32 initBlockModeToggle(); 33 initBatchProgress(); 33 34 }); 34 35 … … 542 543 success: function(response) { 543 544 if (response.success) { 544 alert(response.data.message); 545 $btn.find('.pdfdk-btn-text').text('✓ Compressed'); 545 $btn.find('.pdfdk-btn-text').text('\u2713 Compressed'); 546 546 $btn.addClass('pdfdk-btn-done'); 547 547 … … 566 566 '<span class="pdfdk-status-text">Compressed</span></span>'); 567 567 } 568 569 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(true); 568 570 } else { 569 571 alert(response.data.message || 'Compression failed'); 570 572 $btn.find('.pdfdk-btn-text').text('Compress'); 573 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false); 571 574 } 572 575 }, … … 574 577 alert('Connection error. Please try again.'); 575 578 $btn.find('.pdfdk-btn-text').text('Compress'); 579 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false); 576 580 }, 577 581 complete: function() { … … 596 600 } 597 601 598 $status.text('Compressing 0/' + total + '...'); 602 // Show progress bar notice 603 if (window.pdfdkStartInlineBatch) { 604 window.pdfdkStartInlineBatch(total, 'compress'); 605 } else { 606 $status.text('Compressing 0/' + total + '...'); 607 } 599 608 600 609 $buttons.each(function(index) { … … 602 611 // Delay each request slightly to avoid overwhelming the server 603 612 setTimeout(function() { 613 var origSuccess = $btn.data('compress-success-cb'); 604 614 $btn.trigger('click'); 605 615 done++; 606 $status.text('Compressing ' + done + '/' + total + '...'); 607 if (done === total) { 608 setTimeout(function() { 609 $status.text('Done! All compressed.'); 610 setTimeout(function() { $status.text(''); }, 3000); 611 }, 500); 616 if (window.pdfdkTickInlineBatch) { 617 // tick happens after the AJAX returns; we hook it below 618 } else { 619 $status.text('Compressing ' + done + '/' + total + '...'); 620 if (done === total) { 621 setTimeout(function() { 622 $status.text('Done! All compressed.'); 623 setTimeout(function() { $status.text(''); }, 3000); 624 }, 500); 625 } 612 626 } 613 627 }, index * 2000); // 2 second delay between each … … 662 676 success: function(response) { 663 677 if (response.success) { 664 alert(response.data.message); 665 $btn.find('.pdfdk-btn-text').text('✓ Watermarked'); 678 $btn.find('.pdfdk-btn-text').text('\u2713 Watermarked'); 666 679 $btn.addClass('pdfdk-btn-done'); 667 680 … … 674 687 '<span class="pdfdk-status-text">Watermarked</span></span>'); 675 688 } 689 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(true); 676 690 } else { 677 691 alert(response.data.message || 'Watermarking failed'); 678 692 $btn.find('.pdfdk-btn-text').text('Watermark'); 693 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false); 679 694 } 680 695 }, … … 682 697 alert('Connection error. Please try again.'); 683 698 $btn.find('.pdfdk-btn-text').text('Watermark'); 699 if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false); 684 700 }, 685 701 complete: function() { … … 704 720 } 705 721 706 $status.text('Watermarking 0/' + total + '...'); 722 // Show progress bar notice 723 if (window.pdfdkStartInlineBatch) { 724 window.pdfdkStartInlineBatch(total, 'watermark'); 725 } else { 726 $status.text('Watermarking 0/' + total + '...'); 727 } 707 728 708 729 $buttons.each(function(index) { 709 730 var $btn = $(this); 710 // Delay each request slightly to avoid overwhelming the server711 731 setTimeout(function() { 712 732 $btn.trigger('click'); 713 733 done++; 714 $status.text('Watermarking ' + done + '/' + total + '...'); 715 if (done === total) { 716 setTimeout(function() { 717 $status.text('Done! All watermarked.'); 718 setTimeout(function() { $status.text(''); }, 3000); 719 }, 500); 720 } 721 }, index * 2000); // 2 second delay between each 734 if (!window.pdfdkTickInlineBatch) { 735 $status.text('Watermarking ' + done + '/' + total + '...'); 736 if (done === total) { 737 setTimeout(function() { 738 $status.text('Done! All watermarked.'); 739 setTimeout(function() { $status.text(''); }, 3000); 740 }, 500); 741 } 742 } 743 }, index * 2000); 722 744 }); 723 745 }); … … 738 760 } 739 761 762 /** 763 * Batch compress/watermark progress bar 764 */ 765 var batchPollTimer = null; 766 767 function initBatchProgress() { 768 var $notice = $('#pdfdk-batch-notice'); 769 if (!$notice.length) return; 770 771 // Dismiss button 772 $notice.on('click', '.pdfdk-batch-dismiss', function() { 773 clearInterval(batchPollTimer); 774 $notice.slideUp(200); 775 $.post(pdfdkAdmin.ajaxUrl, { 776 action: 'pdfdk_dismiss_batch', 777 nonce: pdfdkAdmin.nonce 778 }); 779 }); 780 781 // Start from server-provided state on page load (avoids initial round-trip) 782 var initial = (typeof pdfdkAdmin !== 'undefined') ? pdfdkAdmin.currentBatch : null; 783 if (initial && initial.status !== 'none') { 784 renderBatchNotice(initial); 785 if (initial.status === 'running') { 786 startBatchPolling(); 787 } 788 } 789 } 790 791 function startBatchPolling() { 792 clearInterval(batchPollTimer); 793 batchPollTimer = setInterval(function() { 794 $.ajax({ 795 url: pdfdkAdmin.ajaxUrl, 796 type: 'POST', 797 data: { 798 action: 'pdfdk_compress_progress', 799 nonce: pdfdkAdmin.nonce 800 }, 801 success: function(response) { 802 if (!response.success) return; 803 var batch = response.data; 804 805 if (!batch || batch.status === 'none') { 806 clearInterval(batchPollTimer); 807 $('#pdfdk-batch-notice').slideUp(300); 808 return; 809 } 810 811 renderBatchNotice(batch); 812 813 if (batch.status === 'completed') { 814 clearInterval(batchPollTimer); 815 // Auto-dismiss after 8 seconds 816 setTimeout(function() { 817 $('#pdfdk-batch-notice').slideUp(400); 818 $.post(pdfdkAdmin.ajaxUrl, { 819 action: 'pdfdk_dismiss_batch', 820 nonce: pdfdkAdmin.nonce 821 }); 822 }, 8000); 823 } 824 } 825 }); 826 }, 4000); 827 } 828 829 function renderBatchNotice(batch) { 830 var $notice = $('#pdfdk-batch-notice'); 831 var $icon = $notice.find('.pdfdk-batch-icon'); 832 var $title = $notice.find('.pdfdk-batch-title'); 833 var $sub = $notice.find('.pdfdk-batch-sub'); 834 var $fill = $notice.find('.pdfdk-batch-bar-fill'); 835 836 var total = batch.total || 0; 837 var completed = batch.completed || 0; 838 var failed = batch.failed || 0; 839 var processed = completed + failed; 840 var pct = total > 0 ? Math.round((processed / total) * 100) : 0; 841 var isCompress = batch.type !== 'watermark'; 842 var label = isCompress ? 'Compressing' : 'Watermarking'; 843 var labelDone = isCompress ? 'Compression complete' : 'Watermarking complete'; 844 845 $fill.css('width', pct + '%'); 846 847 if (batch.status === 'completed') { 848 $notice.addClass('pdfdk-batch-done').removeClass('pdfdk-batch-has-errors'); 849 if (failed > 0) { 850 $notice.addClass('pdfdk-batch-has-errors'); 851 $title.text(labelDone + ' (' + failed + ' failed)'); 852 } else { 853 $title.text(labelDone + ' \u2714'); 854 } 855 $sub.text(completed + ' of ' + total + ' PDFs processed'); 856 $icon.removeClass('pdfdk-spin dashicons-update').addClass('dashicons-yes-alt'); 857 } else { 858 $notice.removeClass('pdfdk-batch-done pdfdk-batch-has-errors'); 859 $title.text(label + ' PDFs\u2026 do not close this page'); 860 $sub.text(processed + ' of ' + total + ' done'); 861 $icon.addClass('pdfdk-spin dashicons-update').removeClass('dashicons-yes-alt'); 862 startBatchPolling(); // Ensure polling is active 863 } 864 865 $notice.slideDown(200); 866 } 867 868 /** 869 * Expose a function so the plugin page "Compress All" can also 870 * kick off a visual batch for its own inline AJAX processing. 871 */ 872 window.pdfdkStartInlineBatch = function(total, type) { 873 var $notice = $('#pdfdk-batch-notice'); 874 if (!$notice.length) return; 875 renderBatchNotice({ 876 status: 'running', 877 type: type || 'compress', 878 total: total, 879 completed: 0, 880 failed: 0, 881 }); 882 }; 883 884 window.pdfdkTickInlineBatch = function(succeeded) { 885 var $notice = $('#pdfdk-batch-notice'); 886 if (!$notice.length || !$notice.is(':visible')) return; 887 var $title = $notice.find('.pdfdk-batch-title'); 888 var $sub = $notice.find('.pdfdk-batch-sub'); 889 var $fill = $notice.find('.pdfdk-batch-bar-fill'); 890 891 // Parse current numbers from subtitle 892 var match = ($sub.text() || '').match(/(\d+) of (\d+)/); 893 var done = match ? parseInt(match[1], 10) : 0; 894 var total = match ? parseInt(match[2], 10) : 0; 895 896 done = Math.min(done + 1, total); 897 var pct = total > 0 ? Math.round((done / total) * 100) : 0; 898 $fill.css('width', pct + '%'); 899 $sub.text(done + ' of ' + total + ' done'); 900 901 if (done >= total) { 902 renderBatchNotice({ 903 status: 'completed', 904 type: $notice.hasClass('pdfdk-batch-watermark') ? 'watermark' : 'compress', 905 total: total, 906 completed: succeeded ? done : done - 1, 907 failed: succeeded ? 0 : 1, 908 }); 909 } 910 }; 911 740 912 })(jQuery); -
pdfdk-block-optimize/trunk/includes/class-pdfdk-ajax.php
r3488410 r3494980 37 37 // Toggle login requirement 38 38 add_action('wp_ajax_pdfdk_toggle_login', [$this, 'toggle_login']); 39 40 // Batch compress progress 41 add_action('wp_ajax_pdfdk_compress_progress', [$this, 'compress_progress']); 42 43 // Dismiss batch notice 44 add_action('wp_ajax_pdfdk_dismiss_batch', [$this, 'dismiss_batch']); 39 45 } 40 46 … … 479 485 ]); 480 486 } 487 488 /** 489 * Return current batch progress 490 */ 491 public function compress_progress() { 492 check_ajax_referer('pdfdk_nonce', 'nonce'); 493 494 if (!current_user_can('upload_files')) { 495 wp_send_json_error(['message' => 'Unauthorized'], 403); 496 } 497 498 $batch = get_transient('pdfdk_compress_batch'); 499 500 if (!is_array($batch)) { 501 wp_send_json_success(['status' => 'none']); 502 return; 503 } 504 505 wp_send_json_success($batch); 506 } 507 508 /** 509 * Dismiss (delete) the batch transient 510 */ 511 public function dismiss_batch() { 512 check_ajax_referer('pdfdk_nonce', 'nonce'); 513 514 if (!current_user_can('upload_files')) { 515 wp_send_json_error(['message' => 'Unauthorized'], 403); 516 } 517 518 delete_transient('pdfdk_compress_batch'); 519 wp_send_json_success(); 520 } 481 521 } -
pdfdk-block-optimize/trunk/includes/class-pdfdk-blocker.php
r3488410 r3494980 669 669 } 670 670 671 $file_succeeded = false; 672 $file_failed = false; 673 671 674 if ($compress) { 672 675 $result = $api->compress($file_url); … … 686 689 update_post_meta($attachment_id, '_pdfdk_certificate_id', $result['data']['certificate_id']); 687 690 } 688 } elseif (defined('WP_DEBUG') && WP_DEBUG) { 689 error_log('PDFDK Auto-compress failed: ' . wp_json_encode($result)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 691 $file_succeeded = true; 692 } else { 693 $file_failed = true; 694 if (defined('WP_DEBUG') && WP_DEBUG) { 695 error_log('PDFDK Auto-compress failed: ' . wp_json_encode($result)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 696 } 690 697 } 691 698 } … … 704 711 update_post_meta($attachment_id, '_pdfdk_certificate_id', $result['data']['certificate_id']); 705 712 } 706 } elseif (defined('WP_DEBUG') && WP_DEBUG) { 707 error_log('PDFDK Auto-watermark failed: ' . wp_json_encode($result)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 708 } 713 $file_succeeded = true; 714 } else { 715 $file_failed = true; 716 if (defined('WP_DEBUG') && WP_DEBUG) { 717 error_log('PDFDK Auto-watermark failed: ' . wp_json_encode($result)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 718 } 719 } 720 } 721 722 // Update batch progress counter 723 $batch = get_transient('pdfdk_compress_batch'); 724 if (is_array($batch) && $batch['status'] === 'running') { 725 if ($file_succeeded) { 726 $batch['completed']++; 727 } elseif ($file_failed) { 728 $batch['failed']++; 729 } 730 $processed = $batch['completed'] + $batch['failed']; 731 if ($processed >= $batch['total']) { 732 $batch['status'] = 'completed'; 733 } 734 set_transient('pdfdk_compress_batch', $batch, 2 * HOUR_IN_SECONDS); 709 735 } 710 736 } catch (Exception $e) { -
pdfdk-block-optimize/trunk/includes/class-pdfdk-media.php
r3488410 r3494980 176 176 177 177 case 'pdfdk_bulk_compress': 178 // Create batch tracking record 179 set_transient('pdfdk_compress_batch', [ 180 'type' => 'compress', 181 'total' => count($pdf_ids), 182 'completed' => 0, 183 'failed' => 0, 184 'started_at' => time(), 185 'status' => 'running', 186 ], 2 * HOUR_IN_SECONDS); 178 187 // Queue for async processing 179 188 foreach ($pdf_ids as $id) { … … 184 193 185 194 case 'pdfdk_bulk_watermark': 195 // Create batch tracking record 196 set_transient('pdfdk_compress_batch', [ 197 'type' => 'watermark', 198 'total' => count($pdf_ids), 199 'completed' => 0, 200 'failed' => 0, 201 'started_at' => time(), 202 'status' => 'running', 203 ], 2 * HOUR_IN_SECONDS); 186 204 // Queue for async processing 187 205 foreach ($pdf_ids as $id) { -
pdfdk-block-optimize/trunk/pdfdk-block-optimize.php
r3494977 r3494980 4 4 * Plugin URI: https://pdf.dk/en/wordpress 5 5 * Description: Block PDFs from Google indexing, compress and watermark. Nordic GDPR processing with deletion certificates. 6 * Version: 1.0.2 76 * Version: 1.0.28 7 7 * Author: PDF.dk 8 8 * Author URI: https://pdf.dk … … 19 19 20 20 // Plugin constants 21 define('PDFDK_VERSION', '1.0.2 7');21 define('PDFDK_VERSION', '1.0.28'); 22 22 define('PDFDK_PLUGIN_DIR', plugin_dir_path(__FILE__)); 23 23 define('PDFDK_PLUGIN_URL', plugin_dir_url(__FILE__)); … … 64 64 // Enqueue scripts 65 65 add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); 66 add_action('admin_notices', [$this, 'render_batch_notice']); 66 67 67 68 // Activation/deactivation hooks … … 105 106 106 107 public function enqueue_admin_scripts($hook) { 107 // Only loadon our settings page and media library108 // Load scripts on our settings page and media library 108 109 if ($hook === 'toplevel_page_pdfdk-settings' || $hook === 'upload.php') { 109 110 wp_enqueue_style( … … 123 124 124 125 wp_localize_script('pdfdk-admin', 'pdfdkAdmin', [ 125 'ajaxUrl' => admin_url('admin-ajax.php'),126 'nonce' => wp_create_nonce('pdfdk_nonce'),126 'ajaxUrl' => admin_url('admin-ajax.php'), 127 'nonce' => wp_create_nonce('pdfdk_nonce'), 127 128 'watermarkText' => get_option('pdfdk_watermark_text', get_bloginfo('name')), 128 'strings' => [ 129 'currentBatch' => get_transient('pdfdk_compress_batch') ?: null, 130 'strings' => [ 129 131 'compressing' => __('Compressing...', 'pdfdk-block-optimize'), 130 132 'watermarking' => __('Adding watermark...', 'pdfdk-block-optimize'), 131 'success' => __('Success!', 'pdfdk-block-optimize'),132 'error' => __('Error occurred', 'pdfdk-block-optimize'),133 ] 133 'success' => __('Success!', 'pdfdk-block-optimize'), 134 'error' => __('Error occurred', 'pdfdk-block-optimize'), 135 ], 134 136 ]); 135 137 } 138 139 // Always load progress bar script/style on all admin pages so the 140 // notice shows even after navigating away from the media library. 141 if ($hook !== 'toplevel_page_pdfdk-settings' && $hook !== 'upload.php') { 142 $batch = get_transient('pdfdk_compress_batch'); 143 if ($batch && $batch['status'] === 'running') { 144 wp_enqueue_style( 145 'pdfdk-admin', 146 PDFDK_PLUGIN_URL . 'admin/css/admin.css', 147 [], 148 PDFDK_VERSION 149 ); 150 wp_enqueue_script( 151 'pdfdk-admin', 152 PDFDK_PLUGIN_URL . 'admin/js/admin.js', 153 ['jquery'], 154 PDFDK_VERSION, 155 true 156 ); 157 wp_localize_script('pdfdk-admin', 'pdfdkAdmin', [ 158 'ajaxUrl' => admin_url('admin-ajax.php'), 159 'nonce' => wp_create_nonce('pdfdk_nonce'), 160 'currentBatch' => $batch, 161 'strings' => [ 162 'compressing' => __('Compressing...', 'pdfdk-block-optimize'), 163 'watermarking' => __('Adding watermark...', 'pdfdk-block-optimize'), 164 'success' => __('Success!', 'pdfdk-block-optimize'), 165 'error' => __('Error occurred', 'pdfdk-block-optimize'), 166 ], 167 ]); 168 } 169 } 170 } 171 172 /** 173 * Render the batch progress admin notice (hidden by default, JS shows it) 174 */ 175 public function render_batch_notice() { 176 if (!current_user_can('upload_files')) { 177 return; 178 } 179 ?> 180 <div id="pdfdk-batch-notice" class="notice pdfdk-batch-notice" style="display:none;" role="status" aria-live="polite"> 181 <div class="pdfdk-batch-notice-inner"> 182 <span class="pdfdk-batch-icon dashicons dashicons-update pdfdk-spin"></span> 183 <div class="pdfdk-batch-text"> 184 <strong class="pdfdk-batch-title"></strong> 185 <span class="pdfdk-batch-sub"></span> 186 </div> 187 <button type="button" class="pdfdk-batch-dismiss" aria-label="<?php esc_attr_e('Dismiss', 'pdfdk-block-optimize'); ?>">✕</button> 188 </div> 189 <div class="pdfdk-batch-bar-wrap"> 190 <div class="pdfdk-batch-bar-fill"></div> 191 </div> 192 </div> 193 <?php 136 194 } 137 195 -
pdfdk-block-optimize/trunk/readme.txt
r3494977 r3494980 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 1.0.2 76 Stable tag: 1.0.28 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 98 98 == Changelog == 99 99 100 = 1.0.28 = 101 * Added live progress bar notice when bulk compressing or watermarking PDFs 102 * Progress bar shows on all admin pages so you can navigate away and still see status 103 * Auto-dismisses 8 seconds after completion, or dismiss manually 104 * Works for both WP media library bulk actions and plugin page Compress/Watermark All buttons 105 100 106 = 1.0.27 = 101 107 * Increased compress API timeout from 60s to 300s to handle large PDF files reliably during bulk operations
Note: See TracChangeset
for help on using the changeset viewer.