Plugin Directory

Changeset 3494980


Ignore:
Timestamp:
03/30/2026 10:13:52 PM (42 hours ago)
Author:
pdfdk
Message:

v1.0.28: Live progress bar for bulk compress/watermark operations

Location:
pdfdk-block-optimize/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • pdfdk-block-optimize/trunk/admin/css/admin.css

    r3488410 r3494980  
    960960    }
    961961}
     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  
    3131        initWatermarkButtons();
    3232        initBlockModeToggle();
     33        initBatchProgress();
    3334    });
    3435
     
    542543                success: function(response) {
    543544                    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');
    546546                        $btn.addClass('pdfdk-btn-done');
    547547
     
    566566                                '<span class="pdfdk-status-text">Compressed</span></span>');
    567567                        }
     568
     569                        if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(true);
    568570                    } else {
    569571                        alert(response.data.message || 'Compression failed');
    570572                        $btn.find('.pdfdk-btn-text').text('Compress');
     573                        if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false);
    571574                    }
    572575                },
     
    574577                    alert('Connection error. Please try again.');
    575578                    $btn.find('.pdfdk-btn-text').text('Compress');
     579                    if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false);
    576580                },
    577581                complete: function() {
     
    596600            }
    597601
    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            }
    599608
    600609            $buttons.each(function(index) {
     
    602611                // Delay each request slightly to avoid overwhelming the server
    603612                setTimeout(function() {
     613                    var origSuccess = $btn.data('compress-success-cb');
    604614                    $btn.trigger('click');
    605615                    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                        }
    612626                    }
    613627                }, index * 2000); // 2 second delay between each
     
    662676                success: function(response) {
    663677                    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');
    666679                        $btn.addClass('pdfdk-btn-done');
    667680
     
    674687                                '<span class="pdfdk-status-text">Watermarked</span></span>');
    675688                        }
     689                        if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(true);
    676690                    } else {
    677691                        alert(response.data.message || 'Watermarking failed');
    678692                        $btn.find('.pdfdk-btn-text').text('Watermark');
     693                        if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false);
    679694                    }
    680695                },
     
    682697                    alert('Connection error. Please try again.');
    683698                    $btn.find('.pdfdk-btn-text').text('Watermark');
     699                    if (window.pdfdkTickInlineBatch) pdfdkTickInlineBatch(false);
    684700                },
    685701                complete: function() {
     
    704720            }
    705721
    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            }
    707728
    708729            $buttons.each(function(index) {
    709730                var $btn = $(this);
    710                 // Delay each request slightly to avoid overwhelming the server
    711731                setTimeout(function() {
    712732                    $btn.trigger('click');
    713733                    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);
    722744            });
    723745        });
     
    738760    }
    739761
     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
    740912})(jQuery);
  • pdfdk-block-optimize/trunk/includes/class-pdfdk-ajax.php

    r3488410 r3494980  
    3737        // Toggle login requirement
    3838        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']);
    3945    }
    4046
     
    479485        ]);
    480486    }
     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    }
    481521}
  • pdfdk-block-optimize/trunk/includes/class-pdfdk-blocker.php

    r3488410 r3494980  
    669669        }
    670670
     671        $file_succeeded = false;
     672        $file_failed    = false;
     673
    671674        if ($compress) {
    672675            $result = $api->compress($file_url);
     
    686689                    update_post_meta($attachment_id, '_pdfdk_certificate_id', $result['data']['certificate_id']);
    687690                }
    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                }
    690697            }
    691698        }
     
    704711                    update_post_meta($attachment_id, '_pdfdk_certificate_id', $result['data']['certificate_id']);
    705712                }
    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);
    709735        }
    710736    } catch (Exception $e) {
  • pdfdk-block-optimize/trunk/includes/class-pdfdk-media.php

    r3488410 r3494980  
    176176
    177177            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);
    178187                // Queue for async processing
    179188                foreach ($pdf_ids as $id) {
     
    184193
    185194            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);
    186204                // Queue for async processing
    187205                foreach ($pdf_ids as $id) {
  • pdfdk-block-optimize/trunk/pdfdk-block-optimize.php

    r3494977 r3494980  
    44 * Plugin URI: https://pdf.dk/en/wordpress
    55 * Description: Block PDFs from Google indexing, compress and watermark. Nordic GDPR processing with deletion certificates.
    6  * Version: 1.0.27
     6 * Version: 1.0.28
    77 * Author: PDF.dk
    88 * Author URI: https://pdf.dk
     
    1919
    2020// Plugin constants
    21 define('PDFDK_VERSION', '1.0.27');
     21define('PDFDK_VERSION', '1.0.28');
    2222define('PDFDK_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2323define('PDFDK_PLUGIN_URL', plugin_dir_url(__FILE__));
     
    6464        // Enqueue scripts
    6565        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
     66        add_action('admin_notices', [$this, 'render_batch_notice']);
    6667
    6768        // Activation/deactivation hooks
     
    105106
    106107    public function enqueue_admin_scripts($hook) {
    107         // Only load on our settings page and media library
     108        // Load scripts on our settings page and media library
    108109        if ($hook === 'toplevel_page_pdfdk-settings' || $hook === 'upload.php') {
    109110            wp_enqueue_style(
     
    123124
    124125            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'),
    127128                'watermarkText' => get_option('pdfdk_watermark_text', get_bloginfo('name')),
    128                 'strings' => [
     129                'currentBatch'  => get_transient('pdfdk_compress_batch') ?: null,
     130                'strings'       => [
    129131                    'compressing' => __('Compressing...', 'pdfdk-block-optimize'),
    130132                    '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                ],
    134136            ]);
    135137        }
     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'); ?>">&#x2715;</button>
     188            </div>
     189            <div class="pdfdk-batch-bar-wrap">
     190                <div class="pdfdk-batch-bar-fill"></div>
     191            </div>
     192        </div>
     193        <?php
    136194    }
    137195
  • pdfdk-block-optimize/trunk/readme.txt

    r3494977 r3494980  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 1.0.27
     6Stable tag: 1.0.28
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    9898== Changelog ==
    9999
     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
    100106= 1.0.27 =
    101107* 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.