Plugin Directory

Changeset 3394020


Ignore:
Timestamp:
11/12/2025 05:11:10 AM (3 months ago)
Author:
sethta
Message:

Update to version 1.4.1

Location:
easy-critical-css
Files:
296 added
1 deleted
10 edited

Legend:

Unmodified
Added
Removed
  • easy-critical-css/trunk/assets/admin.js

    r3388125 r3394020  
    7373  setupToggleListeners()
    7474
    75   // Cloudflare Change button logic
     75  // Cloudflare Change/Undo button logic
     76  function updateCloudflareChanged(key, add) {
     77    var hidden = document.getElementById('easy_cc_cloudflare_changed')
     78    if (!hidden) return
     79    var arr = hidden.value ? hidden.value.split(',') : []
     80    if (add) {
     81      if (!arr.includes(key)) arr.push(key)
     82    } else {
     83      arr = arr.filter(function(k) { return k !== key })
     84    }
     85    hidden.value = arr.join(',')
     86  }
     87
    7688  document.querySelectorAll('.ecc-cloudflare-change').forEach(function(btn) {
    7789    btn.addEventListener('click', function() {
    78       var field = document.querySelector('input[name="' + btn.getAttribute('data-field') + '"]')
     90      var key = btn.getAttribute('data-cloudflare-key')
     91      var field = document.querySelector('input[data-cloudflare-key="' + key + '"]')
     92      var undoBtn = document.querySelector('.ecc-cloudflare-undo[data-cloudflare-key="' + key + '"]')
    7993      if (field) {
    8094        field.disabled = false
     
    8296        field.focus()
    8397        btn.style.display = 'none'
     98        if (undoBtn) undoBtn.style.display = ''
     99        updateCloudflareChanged(key, true)
     100      }
     101    })
     102  })
     103
     104  document.querySelectorAll('.ecc-cloudflare-undo').forEach(function(btn) {
     105    btn.addEventListener('click', function() {
     106      var key = btn.getAttribute('data-cloudflare-key')
     107      var field = document.querySelector('input[data-cloudflare-key="' + key + '"]')
     108      var changeBtn = document.querySelector('.ecc-cloudflare-change[data-cloudflare-key="' + key + '"]')
     109      if (field) {
     110        field.disabled = true
     111        var masked = field.getAttribute('data-original-masked')
     112        if (masked !== null) field.value = masked
     113        btn.style.display = 'none'
     114        if (changeBtn) changeBtn.style.display = ''
     115        updateCloudflareChanged(key, false)
    84116      }
    85117    })
  • easy-critical-css/trunk/easy-critical-css.php

    r3388125 r3394020  
    33 * Plugin Name:       Easy Critical CSS
    44 * Description:       Easily inject Critical CSS and optimized Secondary CSS to improve page speed and performance.
    5  * Version:           1.4.0
     5 * Version:           1.4.1
    66 * Requires at least: 6.2
    77 * Tested up to:      6.8.2
  • easy-critical-css/trunk/inc/class-admin-settings.php

    r3388125 r3394020  
    5151                settings_fields( 'easy-critical-css' );
    5252                do_settings_sections( 'easy-critical-css' );
     53                echo '<input type="hidden" id="easy_cc_cloudflare_changed" name="easy_cc_cloudflare_changed" value="" />';
    5354                submit_button();
    5455                ?>
     
    126127        $warning = ''
    127128    ) {
    128         $value = get_option( "easy_cc_$key", $default_value );
    129         $disabled = false;
    130         $masked_value = '';
     129        $value           = get_option( "easy_cc_$key", $default_value );
     130        $disabled        = false;
     131        $masked_value    = '';
    131132        $show_change_btn = false;
    132133        // Cloudflare masking logic
     
    137138                'cloudflare_zone_id' => 'CLOUDFLARE_ZONE_ID',
    138139            ];
    139             $const = $const_map[ $key ];
     140            $const     = $const_map[ $key ];
    140141            if ( defined( $const ) && constant( $const ) ) {
    141142                $disabled = true;
    142                 $raw = (string) constant( $const );
    143                 $len = strlen( $raw );
     143                $raw      = (string) constant( $const );
     144                $len      = strlen( $raw );
    144145                if ( $len > 4 ) {
    145146                    $masked_value = substr( $raw, 0, 2 ) . str_repeat( '*', $len - 4 ) . substr( $raw, -2 );
     
    148149                }
    149150            } elseif ( ! empty( $value ) ) {
    150                 $disabled = true;
     151                $disabled        = true;
    151152                $show_change_btn = true;
    152                 $raw = (string) $value;
    153                 $len = strlen( $raw );
     153                $raw             = (string) $value;
     154                $len             = strlen( $raw );
    154155                if ( $len > 4 ) {
    155156                    $masked_value = substr( $raw, 0, 2 ) . str_repeat( '*', $len - 4 ) . substr( $raw, -2 );
     
    205206            } elseif ( $type === 'text' ) {
    206207                ?>
    207                 <input type="text" name="easy_cc_<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $disabled ? $masked_value : $value ); ?>" <?php disabled( $disabled ); ?> autocomplete="off" />
    208                 <?php if ( $disabled && $show_change_btn ) : ?>
    209                     <button type="button" class="button button-secondary ecc-cloudflare-change" data-field="easy_cc_<?php echo esc_attr( $key ); ?>"><?php esc_html_e( 'Change', 'easy-critical-css' ); ?></button>
    210                 <?php elseif ( $disabled ) : ?>
     208                <input type="text" name="easy_cc_<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $disabled ? $masked_value : $value ); ?>" <?php disabled( $disabled ); ?> autocomplete="off" data-cloudflare-key="<?php echo esc_attr( $key ); ?>"
     209                <?php
     210                if ( $disabled ) {
     211                    echo ' data-original-masked="' . esc_attr( $masked_value ) . '"';}
     212                ?>
     213                />
     214                <?php if ( $disabled && $show_change_btn ) { ?>
     215                    <button type="button" class="button button-secondary ecc-cloudflare-change" data-cloudflare-key="<?php echo esc_attr( $key ); ?>"><?php esc_html_e( 'Change', 'easy-critical-css' ); ?></button>
     216                    <button type="button" class="button button-secondary ecc-cloudflare-undo" data-cloudflare-key="<?php echo esc_attr( $key ); ?>" style="display:none; margin-left:4px;">Undo</button>
     217                <?php } elseif ( $disabled ) { ?>
    211218                    <span style="color:#888; font-size:12px; margin-left:8px;">(<?php esc_html_e( 'Set via constant', 'easy-critical-css' ); ?>)</span>
    212                 <?php endif; ?>
     219                <?php } ?>
    213220                <?php
    214221            }
     
    412419                'basic'         => true,
    413420            ],
    414             'cloudflare_email' => [
     421            'cloudflare_email'           => [
    415422                'label'      => __( 'Cloudflare Email', 'easy-critical-css' ),
    416423                'type'       => 'text',
     
    418425                'desc'       => __( 'Cloudflare account email for API access. If the CLOUDFLARE_EMAIL constant is defined, this field is disabled and the value is hidden for privacy.', 'easy-critical-css' ),
    419426            ],
    420             'cloudflare_api_key' => [
     427            'cloudflare_api_key'         => [
    421428                'label'      => __( 'Cloudflare API Key', 'easy-critical-css' ),
    422429                'type'       => 'text',
     
    424431                'desc'       => __( 'Cloudflare API key for API access. If the CLOUDFLARE_API_KEY constant is defined, this field is disabled and the value is hidden for privacy.', 'easy-critical-css' ),
    425432            ],
    426             'cloudflare_zone_id' => [
     433            'cloudflare_zone_id'         => [
    427434                'label'      => __( 'Cloudflare Zone ID', 'easy-critical-css' ),
    428435                'type'       => 'text',
     
    447454
    448455    public static function sanitize_settings( $value ) {
    449         $key = current_filter(); // Get current setting being sanitized
    450         $key = str_replace( 'sanitize_option_easy_cc_', '', $key ); // Determine actual key
     456        $key = current_filter(); // Get current setting being sanitized.
     457        $key = str_replace( 'sanitize_option_easy_cc_', '', $key ); // Determine actual key.
     458
     459        // Only update Cloudflare if value marked for change or previously empty.
     460        $cloudflare_keys = [ 'cloudflare_email', 'cloudflare_api_key', 'cloudflare_zone_id' ];
     461        if ( in_array( $key, $cloudflare_keys, true ) ) {
     462            $changed = [];
     463            if ( isset( $_POST['easy_cc_cloudflare_changed'] ) ) {
     464                // Only allow known Cloudflare keys for safety reasons.
     465                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce checked below.
     466                $cloudflare_changed_post = '';
     467                if ( isset( $_POST['easy_cc_cloudflare_changed'] ) ) {
     468                    $cloudflare_changed_post = sanitize_text_field( wp_unslash( $_POST['easy_cc_cloudflare_changed'] ) );
     469                }
     470                $raw = explode( ',', $cloudflare_changed_post );
     471                foreach ( $raw as $item ) {
     472                    $item = trim( $item );
     473                    if ( in_array( $item, $cloudflare_keys, true ) ) {
     474                        $changed[] = $item;
     475                    }
     476                }
     477            }
     478            $existing = get_option( 'easy_cc_' . $key, '' );
     479            if ( ! in_array( $key, $changed, true ) && $existing !== '' ) {
     480                // Not changed and already set, preserve existing value
     481                return $existing;
     482            }
     483        }
     484
     485        // Nonce verification for Cloudflare settings change.
     486        if ( in_array( $key, $cloudflare_keys, true ) && isset( $_POST['easy_cc_cloudflare_changed'] ) ) {
     487            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce checked here.
     488            if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'easy-critical-css-options' ) ) {
     489                // Nonce missing or invalid, do not process.
     490                return get_option( 'easy_cc_' . $key, '' );
     491            }
     492        }
    451493
    452494        // Checkboxes.
     
    512554            'easy-critical-css-advanced-section',
    513555            '',
    514             function() {
     556            function () {
    515557                ?>
    516558                <div id="ecc-advanced-settings-toggle">
     
    532574        foreach ( $settings as $key => $setting ) {
    533575            $is_basic = isset( $setting['basic'] ) && $setting['basic'];
    534             $section = $is_basic ? 'easy-critical-css-basic-section' : 'easy-critical-css-advanced-section';
    535            
     576            $section  = $is_basic ? 'easy-critical-css-basic-section' : 'easy-critical-css-advanced-section';
     577
    536578            add_settings_field(
    537579                "easy_cc_$key",
     
    539581                function () use ( $key, $setting, $is_basic ) {
    540582                    static $last_advanced_key = null;
    541                     static $all_keys = null;
    542                    
     583                    static $all_keys          = null;
     584
    543585                    if ( $all_keys === null ) {
    544586                        $all_keys = array_keys( self::get_settings_schema() );
    545587                        // Find the last advanced setting
    546588                        foreach ( array_reverse( $all_keys ) as $check_key ) {
    547                             $check_setting = self::get_settings_schema()[$check_key];
     589                            $check_setting = self::get_settings_schema()[ $check_key ];
    548590                            if ( ! isset( $check_setting['basic'] ) || ! $check_setting['basic'] ) {
    549591                                $last_advanced_key = $check_key;
     
    552594                        }
    553595                    }
    554                    
     596
    555597                    $wrapper_class = $is_basic ? 'ecc-basic-setting' : 'ecc-advanced-setting';
    556598                    echo '<div class="' . esc_attr( $wrapper_class ) . '">';
     
    565607                    );
    566608                    echo '</div>';
    567                    
     609
    568610                    // Close advanced settings div after the last advanced setting
    569611                    if ( $key === $last_advanced_key ) {
  • easy-critical-css/trunk/inc/class-compatibility-cloudflare.php

    r3388125 r3394020  
    2828        }
    2929
    30         // Prefer constants, fallback to settings.
    31         $cloudflare_email = defined( 'CLOUDFLARE_EMAIL' ) ? CLOUDFLARE_EMAIL : Settings::get_global_cloudflare_email();
    32         if ( empty( $cloudflare_email ) ) {
    33             return false;
    34         }
    35 
    36         $cloudflare_api_key = defined( 'CLOUDFLARE_API_KEY' ) ? CLOUDFLARE_API_KEY : Settings::get_global_cloudflare_api_key();
    37         if ( empty( $cloudflare_api_key ) ) {
    38             return false;
    39         }
    40 
    41         $cloudflare_zone_id = defined( 'CLOUDFLARE_ZONE_ID' ) ? CLOUDFLARE_ZONE_ID : Settings::get_global_cloudflare_zone_id();
    42         if ( empty( $cloudflare_zone_id ) ) {
     30        $cloudflare_email   = Settings::get_global_cloudflare_email();
     31        $cloudflare_api_key = Settings::get_global_cloudflare_api_key();
     32        $cloudflare_zone_id = Settings::get_global_cloudflare_zone_id();
     33        if ( empty( $cloudflare_email ) || empty( $cloudflare_api_key ) || empty( $cloudflare_zone_id ) ) {
    4334            return false;
    4435        }
  • easy-critical-css/trunk/inc/class-plugin.php

    r3336970 r3394020  
    1010    private static $instance = null;
    1111
    12     private static $plugin_version = '1.3.1';
     12    private static $plugin_version = '1.4.1';
    1313
    1414    private static $db_version = '2';
  • easy-critical-css/trunk/inc/class-reset-handler.php

    r3327873 r3394020  
    2424            'regenerate_triggers',
    2525            'expired_css_behavior',
     26            'cloudflare_email',
     27            'cloudflare_api_key',
     28            'cloudflare_zone_id',
     29            'debug_mode',
    2630        ];
    2731        foreach ( $settings as $setting ) {
  • easy-critical-css/trunk/inc/class-rest-api.php

    r3336970 r3394020  
    1212class REST_API {
    1313    public static $route_namespace = 'easy-critical-css/v1';
     14
     15    public static $max_raw_bytes     = 5242880; // 5 * 1024 * 1024 (5 MB).
     16    public static $max_decoded_bytes = 12582912; // 12 * 1024 * 1024 (12 MB).
     17    public static $max_css_bytes     = 2097152; // 2 * 1024 * 1024 (2 MB).
     18
    1419    public static function init() {
    1520        add_action( 'rest_api_init', [ __CLASS__, 'register_routes' ] );
     
    2530                // Open permission_callback because request validation is securely handled at the start of `handle_critical_css` callback through a handshake validation.
    2631                'permission_callback' => '__return_true',
    27                 'args'                => [
    28                     'hash'            => [
    29                         'required' => true,
    30                         'type'     => 'string',
    31                     ],
    32                     'handshake'       => [
    33                         'required' => true,
    34                         'type'     => 'string',
    35                     ],
    36                     'criticalCSS'     => [
    37                         'required' => false,
    38                         'type'     => 'string',
    39                     ],
    40                     'remainingCSS'    => [
    41                         'required' => false,
    42                         'type'     => 'string',
    43                     ],
    44                     'secondaryCSS'    => [
    45                         'required' => false,
    46                         'type'     => 'string',
    47                     ],
    48                     'criticalCSSUrl'  => [
    49                         'required'          => false,
    50                         'type'              => 'string',
    51                         'validate_callback' => [ __CLASS__, 'validate_criticalcss_net_url' ],
    52                     ],
    53                     'remainingCSSUrl' => [
    54                         'required'          => false,
    55                         'type'              => 'string',
    56                         'validate_callback' => [ __CLASS__, 'validate_criticalcss_net_url' ],
    57                     ],
    58                     'secondaryCSSUrl' => [
    59                         'required'          => false,
    60                         'type'              => 'string',
    61                         'validate_callback' => [ __CLASS__, 'validate_criticalcss_net_url' ],
    62                     ],
    63                     'url'             => [
    64                         'required'          => true,
    65                         'type'              => 'string',
    66                         'validate_callback' => function ( $url ) {
    67                             return (bool) filter_var( $url, FILTER_VALIDATE_URL );
    68                         },
    69                     ],
    70                 ],
     32                // Intentionally omitting 'args' here so the REST server does not pre-validate/parse the request body and gzipped content can be passed through.
     33                // Validation and parsing (including gzip handling) is performed inside the callback.
    7134            ]
    7235        );
     
    265228    }
    266229
     230    private static function validate_receive_params( $params ) {
     231        // Basic shape
     232        if ( ! is_array( $params ) ) {
     233            return new WP_Error( 'invalid_params', __( 'Invalid request parameters.', 'easy-critical-css' ), [ 'status' => 400 ] );
     234        }
     235
     236        // Required fields
     237        if ( empty( $params['hash'] ) ) {
     238            return new WP_Error( 'missing_hash', __( 'Hashed URL is missing.', 'easy-critical-css' ), [ 'status' => 400 ] );
     239        }
     240
     241        if ( ! preg_match( '/^[A-Fa-f0-9]{32}$/', $params['hash'] ) ) {
     242            return new WP_Error( 'invalid_hash', __( 'Invalid hash format.', 'easy-critical-css' ), [ 'status' => 400 ] );
     243        }
     244
     245        if ( empty( $params['handshake'] ) ) {
     246            return new WP_Error( 'missing_handshake', __( 'Handshake key is missing.', 'easy-critical-css' ), [ 'status' => 400 ] );
     247        }
     248
     249        if ( ! is_string( $params['handshake'] ) ) {
     250            return new WP_Error( 'invalid_type', __( 'handshake must be a string.', 'easy-critical-css' ), [ 'status' => 400 ] );
     251        }
     252
     253        // URL if present should be valid and a string.
     254        if ( isset( $params['url'] ) ) {
     255            if ( ! is_string( $params['url'] ) || filter_var( $params['url'], FILTER_VALIDATE_URL ) === false ) {
     256                return new WP_Error( 'invalid_url', __( 'Invalid URL provided.', 'easy-critical-css' ), [ 'status' => 400 ] );
     257            }
     258        }
     259
     260        // Per-CSS type & size checks.
     261        foreach ( [ 'criticalCSS', 'secondaryCSS', 'remainingCSS' ] as $css_key ) {
     262            if ( isset( $params[ $css_key ] ) ) {
     263                if ( ! is_string( $params[ $css_key ] ) ) {
     264                    // translators: %s is the CSS parameter name.
     265                    return new WP_Error( 'invalid_type', sprintf( __( '%s must be a string.', 'easy-critical-css' ), $css_key ), [ 'status' => 400 ] );
     266                }
     267                if ( strlen( $params[ $css_key ] ) > self::$max_css_bytes ) {
     268                    // translators: %s is the CSS parameter name.
     269                    return new WP_Error( 'css_too_large', sprintf( __( '%s too large.', 'easy-critical-css' ), $css_key ), [ 'status' => 413 ] );
     270                }
     271            }
     272        }
     273
     274        // Validate CSS URLs.
     275        foreach ( [ 'criticalCSSUrl', 'remainingCSSUrl', 'secondaryCSSUrl' ] as $url_key ) {
     276            if ( isset( $params[ $url_key ] ) && $params[ $url_key ] !== '' ) {
     277                if ( ! is_string( $params[ $url_key ] ) || ! self::validate_criticalcss_net_url( $params[ $url_key ] ) ) {
     278                    // translators: %s is the CSS URL parameter name.
     279                    return new WP_Error( 'invalid_css_url', sprintf( __( '%s is not a valid criticalcss.net URL.', 'easy-critical-css' ), $url_key ), [ 'status' => 400 ] );
     280                }
     281            }
     282        }
     283
     284        return true;
     285    }
     286
    267287    private static function remove_old_css_files( $hash ) {
    268288        $upload_dir = wp_upload_dir();
     
    299319
    300320    public static function handle_critical_css( WP_REST_Request $request ) {
    301         $params    = $request->get_params(); // Get all request parameters at once.
    302         $handshake = sanitize_text_field( $request->get_param( 'handshake' ) );
    303 
    304         // Check for required params.
    305         if ( empty( $handshake ) ) {
     321        // Diagnostic logs: log content encoding and content length for debugging.
     322        $content_encoding = ! empty( $_SERVER['HTTP_CONTENT_ENCODING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_CONTENT_ENCODING'] ) ) : '';
     323        $content_length   = ! empty( $_SERVER['CONTENT_LENGTH'] ) ? intval( wp_unslash( $_SERVER['CONTENT_LENGTH'] ) ) : 0;
     324
     325        // Quick reject if Content-Length advertises a too-large body.
     326        if ( $content_length > 0 && $content_length > self::$max_raw_bytes ) {
    306327            return new WP_Error(
    307                 'missing_handshake',
    308                 __( 'Handshake key is missing.', 'easy-critical-css' ),
    309                 [ 'status' => 400 ]
    310             );
    311         }
    312 
    313         if ( empty( $params['hash'] ) ) {
    314             return new WP_Error(
    315                 'missing_hash',
    316                 __( 'Hashed URL is missing.', 'easy-critical-css' ),
    317                 [ 'status' => 400 ]
    318             );
     328                'request_too_large',
     329                __( 'Request body too large.', 'easy-critical-css' ),
     330                [ 'status' => 413 ]
     331            );
     332        }
     333
     334        // Accept gzipped request bodies if sent that way.
     335        $raw = file_get_contents( 'php://input' );
     336
     337        if ( stripos( $content_encoding, 'gzip' ) !== false ) {
     338            if ( function_exists( 'gzdecode' ) ) {
     339                $decoded = gzdecode( $raw );
     340                if ( $decoded === false ) {
     341                    return new WP_Error(
     342                        'invalid_gzip',
     343                        __( 'Invalid gzip encoding.', 'easy-critical-css' ),
     344                        [ 'status' => 400 ]
     345                    );
     346                }
     347                $raw = $decoded;
     348            } else {
     349                return new WP_Error(
     350                    'no_gzdecode',
     351                    __( 'Server does not support gzip decoding.', 'easy-critical-css' ),
     352                    [ 'status' => 501 ]
     353                );
     354            }
     355        }
     356
     357        // If gzipped and decoded above, parse JSON from $raw; otherwise, use normal param parsing.
     358        if ( ! empty( $raw ) && stripos( $content_encoding, 'gzip' ) !== false ) {
     359            // Stop any too large payloads before JSON decoding.
     360            if ( strlen( $raw ) > self::$max_decoded_bytes ) {
     361                return new WP_Error(
     362                    'request_too_large',
     363                    __( 'Decoded request body too large.', 'easy-critical-css' ),
     364                    [ 'status' => 413 ]
     365                );
     366            }
     367            $params = json_decode( $raw, true );
     368            if ( ! is_array( $params ) ) {
     369                return new WP_Error(
     370                    'invalid_json',
     371                    __( 'Invalid JSON in gzipped body.', 'easy-critical-css' ),
     372                    [ 'status' => 400 ]
     373                );
     374            }
     375            $handshake = ! empty( $params['handshake'] ) ? sanitize_text_field( $params['handshake'] ) : '';
     376        } else {
     377            $params    = $request->get_params(); // Get all request parameters at once.
     378            $handshake = sanitize_text_field( $request->get_param( 'handshake' ) );
     379        }
     380
     381        // Centralized validation of incoming params (hash, handshake, sizes, urls).
     382        $valid = self::validate_receive_params( $params );
     383        if ( is_wp_error( $valid ) ) {
     384            return $valid;
    319385        }
    320386
  • easy-critical-css/trunk/inc/class-settings.php

    r3388125 r3394020  
    298298     */
    299299    public static function get_global_cloudflare_email() {
     300        if ( defined( 'CLOUDFLARE_EMAIL' ) ) {
     301            return (string) CLOUDFLARE_EMAIL;
     302        }
    300303        return (string) self::get_global_setting( 'cloudflare_email', '' );
    301304    }
     
    308311     */
    309312    public static function get_global_cloudflare_api_key() {
     313        if ( defined( 'CLOUDFLARE_API_KEY' ) ) {
     314            return (string) CLOUDFLARE_API_KEY;
     315        }
    310316        return (string) self::get_global_setting( 'cloudflare_api_key', '' );
    311317    }
     
    318324     */
    319325    public static function get_global_cloudflare_zone_id() {
     326        if ( defined( 'CLOUDFLARE_ZONE_ID' ) ) {
     327            return (string) CLOUDFLARE_ZONE_ID;
     328        }
    320329        return (string) self::get_global_setting( 'cloudflare_zone_id', '' );
    321330    }
  • easy-critical-css/trunk/readme.txt

    r3388125 r3394020  
    102102== Changelog ==
    103103
     104= 1.4.1 =
     105- OPTIMIZATION: Allows larger Critical CSS payloads via REST API
     106- FIX: Fixes Cloudflare settings persistency issue
     107
    104108= 1.4.0 =
    105109- FEATURE: Separates advanced settings for better usability
     
    161165- FEATURE: Automated Critical CSS generation through API
    162166- FEATURE: Integration with popular caching plugins
     167
     168== Upgrade Notice ==
     169
     170= 1.4.1 =
     171* This update provides bug fixes and optimizations to the plugin.
  • easy-critical-css/trunk/vendor/composer/installed.php

    r3388125 r3394020  
    44        'pretty_version' => 'dev-main',
    55        'version' => 'dev-main',
    6         'reference' => '91fed063ca758df5f87fa7fc19d112f4c438b387',
     6        'reference' => '7fc1015d3f099e77bc59b0e47121c84041185aa0',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../',
     
    2323            'pretty_version' => 'dev-main',
    2424            'version' => 'dev-main',
    25             'reference' => '91fed063ca758df5f87fa7fc19d112f4c438b387',
     25            'reference' => '7fc1015d3f099e77bc59b0e47121c84041185aa0',
    2626            'type' => 'wordpress-plugin',
    2727            'install_path' => __DIR__ . '/../../',
Note: See TracChangeset for help on using the changeset viewer.