Changeset 3401089
- Timestamp:
- 11/22/2025 09:58:36 PM (4 months ago)
- Location:
- easy-critical-css
- Files:
-
- 295 added
- 17 edited
-
tags/1.4.3 (added)
-
tags/1.4.3/LICENSE (added)
-
tags/1.4.3/assets (added)
-
tags/1.4.3/assets/admin.css (added)
-
tags/1.4.3/assets/admin.js (added)
-
tags/1.4.3/build (added)
-
tags/1.4.3/build/index.asset.php (added)
-
tags/1.4.3/build/index.js (added)
-
tags/1.4.3/composer.json (added)
-
tags/1.4.3/easy-critical-css.php (added)
-
tags/1.4.3/inc (added)
-
tags/1.4.3/inc/class-admin-settings.php (added)
-
tags/1.4.3/inc/class-api-request-handler.php (added)
-
tags/1.4.3/inc/class-api-service.php (added)
-
tags/1.4.3/inc/class-compatibility-cache.php (added)
-
tags/1.4.3/inc/class-compatibility-cloudflare.php (added)
-
tags/1.4.3/inc/class-compatibility-ewww.php (added)
-
tags/1.4.3/inc/class-compatibility-perfmatters.php (added)
-
tags/1.4.3/inc/class-compatibility-trellis.php (added)
-
tags/1.4.3/inc/class-compatibility-woocommerce.php (added)
-
tags/1.4.3/inc/class-compatibility-wp-rocket.php (added)
-
tags/1.4.3/inc/class-critical-css-injector.php (added)
-
tags/1.4.3/inc/class-critical-css-regenerate.php (added)
-
tags/1.4.3/inc/class-critical-css-server.php (added)
-
tags/1.4.3/inc/class-critical-css-status.php (added)
-
tags/1.4.3/inc/class-critical-css.php (added)
-
tags/1.4.3/inc/class-database.php (added)
-
tags/1.4.3/inc/class-debug.php (added)
-
tags/1.4.3/inc/class-delete-handler.php (added)
-
tags/1.4.3/inc/class-gutenberg.php (added)
-
tags/1.4.3/inc/class-helpers.php (added)
-
tags/1.4.3/inc/class-individual-settings.php (added)
-
tags/1.4.3/inc/class-plugin.php (added)
-
tags/1.4.3/inc/class-reset-handler.php (added)
-
tags/1.4.3/inc/class-rest-api.php (added)
-
tags/1.4.3/inc/class-settings.php (added)
-
tags/1.4.3/inc/class-uninstall-handler.php (added)
-
tags/1.4.3/inc/load-freemius.php (added)
-
tags/1.4.3/package.json (added)
-
tags/1.4.3/readme.txt (added)
-
tags/1.4.3/src (added)
-
tags/1.4.3/src/components (added)
-
tags/1.4.3/src/components/generateButton.tsx (added)
-
tags/1.4.3/src/components/settingsField.tsx (added)
-
tags/1.4.3/src/components/settingsPanel.tsx (added)
-
tags/1.4.3/src/components/statusIndicator.tsx (added)
-
tags/1.4.3/src/hooks (added)
-
tags/1.4.3/src/hooks/useEffectiveSettings.ts (added)
-
tags/1.4.3/src/hooks/usePostSaving.ts (added)
-
tags/1.4.3/src/hooks/useSettings.ts (added)
-
tags/1.4.3/src/hooks/useSettingsVisibility.ts (added)
-
tags/1.4.3/src/hooks/useStatus.ts (added)
-
tags/1.4.3/src/index.tsx (added)
-
tags/1.4.3/src/types (added)
-
tags/1.4.3/src/types/types.ts (added)
-
tags/1.4.3/src/types/wordpress.d.ts (added)
-
tags/1.4.3/vendor (added)
-
tags/1.4.3/vendor/autoload.php (added)
-
tags/1.4.3/vendor/composer (added)
-
tags/1.4.3/vendor/composer/ClassLoader.php (added)
-
tags/1.4.3/vendor/composer/InstalledVersions.php (added)
-
tags/1.4.3/vendor/composer/LICENSE (added)
-
tags/1.4.3/vendor/composer/autoload_classmap.php (added)
-
tags/1.4.3/vendor/composer/autoload_files.php (added)
-
tags/1.4.3/vendor/composer/autoload_namespaces.php (added)
-
tags/1.4.3/vendor/composer/autoload_psr4.php (added)
-
tags/1.4.3/vendor/composer/autoload_real.php (added)
-
tags/1.4.3/vendor/composer/autoload_static.php (added)
-
tags/1.4.3/vendor/composer/installed.json (added)
-
tags/1.4.3/vendor/composer/installed.php (added)
-
tags/1.4.3/vendor/composer/platform_check.php (added)
-
tags/1.4.3/vendor/freemius (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/LICENSE.txt (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/account.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/add-ons.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/affiliation.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/checkout.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/clone-resolution.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/common.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/connect.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/debug.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/dialog-boxes.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/gdpr-optin-notice.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/optout.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/admin/plugins.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/customizer.css (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/css/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/img (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/img/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/img/plugin-icon.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/img/theme-icon.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/jquery.form.js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/nojquery.ba-postmessage.js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/postmessage.js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/14fb1bd5b7c41648488b06147f50a0dc.svg (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/178afa6030e76635dbe835e111d2c507.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/27b5a722a5553d9de0170325267fccec.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/4375c4a3ddc6f637c2ab9a2d7220f91e.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/45da596e2b512ffc3bb638baaf0fdc4e.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/a34e046aee1702a5690679750a7f4d0f.svg (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/b09d0b38b627c2fa564d050f79f2f064.svg (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/c03f665db27af43971565560adfba594.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/cb5fc4f6ec7ada72e986f6e7dde365bf.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/d65812c447b4523b42d59018e1c0bb53.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/f3aac72a8e63997d6bb888f816457e9b.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/fde48e4609a6ddc11d639fc2421f2afd.png (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/freemius-pricing.js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/assets/js/pricing/freemius-pricing.js.LICENSE.txt (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/composer.json (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/config.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-freemius-abstract.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-freemius.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-admin-notices.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-api.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-garbage-collector.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-hook-snapshot.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-lock.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-logger.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-options.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-plugin-updater.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-security.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-storage.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/class-fs-user-lock.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/customizer (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/customizer/class-fs-customizer-support-section.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/customizer/class-fs-customizer-upsell-control.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/customizer/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/debug (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/debug/class-fs-debug-bar-panel.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/debug/debug-bar-start.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/debug/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-affiliate-terms.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-affiliate.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-billing.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-entity.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-payment.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-plugin-info.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-plugin-license.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-plugin-plan.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-plugin-tag.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-plugin.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-pricing.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-scope-entity.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-site.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-subscription.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/class-fs-user.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/entities/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/fs-core-functions.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/fs-essential-functions.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/fs-html-escaping-functions.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/fs-plugin-info-dialog.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/l10n.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-admin-menu-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-admin-notice-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-cache-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-checkout-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-clone-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-contact-form-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-debug-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-gdpr-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-key-value-storage.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-license-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-option-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-permission-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-plan-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/class-fs-plugin-manager.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/managers/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/ArgumentNotExistException.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/EmptyArgumentException.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/Exception.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/InvalidArgumentException.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/OAuthException.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/Exceptions/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/FreemiusBase.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/FreemiusWordPress.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/LICENSE.txt (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/sdk/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/supplements (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/supplements/fs-essential-functions-1.1.7.1.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/supplements/fs-essential-functions-2.2.1.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/supplements/fs-migration-2.5.1.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/includes/supplements/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-cs_CZ.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-da_DK.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-de_DE.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-es_ES.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-fr_FR.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-he_IL.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-hu_HU.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-it_IT.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-ja.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-nl_NL.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-ru_RU.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-ta.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius-zh_CN.mo (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/freemius.pot (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/languages/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/require.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/start.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/billing.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/activate-license-button.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/addon.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/deactivate-license-button.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/disconnect-button.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/partials/site.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/account/payments.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/add-ons.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/add-trial-to-pricing.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/admin-notice.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/ajax-loader.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/api-connectivity-message-js.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/auto-installation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/checkout (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/checkout.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/checkout/frame.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/checkout/process-redirect.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/checkout/redirect.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/clone-resolution-js.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/connect (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/connect.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/connect/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/connect/permission.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/connect/permissions-group.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/contact.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug/api-calls.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug/logger.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug/plugins-themes-sync.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/debug/scheduled-crons.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/email.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/affiliation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/data-debug-mode.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/deactivation (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/deactivation/contact.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/deactivation/form.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/deactivation/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/deactivation/retry-skip.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/email-address-update.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/license-activation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/optout.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/premium-versions-upgrade-handler.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/premium-versions-upgrade-metadata.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/resend-key.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/subscription-cancellation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/trial-start.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/forms/user-change.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/gdpr-optin-js.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js/jquery.content-change.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js/open-license-activation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js/permissions.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/js/style-premium-theme.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/partials (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/partials/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/partials/network-activation.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-icon.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-info (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-info/description.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-info/features.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-info/index.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/plugin-info/screenshots.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/pricing.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/secure-https-header.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/sticky-admin-notice-js.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/tabs-capture-js.php (added)
-
tags/1.4.3/vendor/freemius/wordpress-sdk/templates/tabs.php (added)
-
trunk/assets/admin.css (modified) (1 diff)
-
trunk/assets/admin.js (modified) (7 diffs)
-
trunk/easy-critical-css.php (modified) (1 diff)
-
trunk/inc/class-admin-settings.php (modified) (10 diffs)
-
trunk/inc/class-api-request-handler.php (modified) (3 diffs)
-
trunk/inc/class-api-service.php (modified) (1 diff)
-
trunk/inc/class-compatibility-perfmatters.php (modified) (2 diffs)
-
trunk/inc/class-critical-css-status.php (modified) (4 diffs)
-
trunk/inc/class-critical-css.php (modified) (1 diff)
-
trunk/inc/class-database.php (modified) (5 diffs)
-
trunk/inc/class-helpers.php (modified) (3 diffs)
-
trunk/inc/class-individual-settings.php (modified) (1 diff)
-
trunk/inc/class-plugin.php (modified) (1 diff)
-
trunk/inc/class-rest-api.php (modified) (4 diffs)
-
trunk/inc/class-settings.php (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
easy-critical-css/trunk/assets/admin.css
r3388125 r3401089 6 6 7 7 .wp-core-ui button.button-danger:hover { 8 background: # a32b2d;9 border-color: # a32b2d;8 background: #b12331; 9 border-color: #b12331; 10 10 color: #fff; 11 } 12 13 /* Auto Mode Status Indicator Styles */ 14 .ecc-auto-mode-status { 15 padding: 12px 16px; 16 border-radius: 4px; 17 margin: 12px 0; 18 background-color: #f9f9f9; 19 border: 1px solid #e0e0e0; 20 } 21 22 .ecc-auto-mode-status.ecc-status-ready { 23 background-color: #f0f6f3; 24 border-color: #1e7e34; 25 } 26 27 .ecc-auto-mode-status.ecc-status-blocked { 28 background-color: #fef5f5; 29 border-color: #b12331; 30 } 31 32 .ecc-status-main { 33 display: flex; 34 align-items: center; 35 gap: 10px; 36 margin-bottom: 8px; 37 } 38 39 .ecc-status-icon { 40 font-size: 20px; 41 font-weight: bold; 42 min-width: 24px; 43 text-align: center; 44 } 45 46 .ecc-status-text { 47 font-weight: 500; 48 font-size: 14px; 49 } 50 51 .ecc-status-details { 52 margin: 10px 0; 53 padding-left: 34px; 54 font-size: 12px; 55 color: #666; 56 } 57 58 .ecc-status-details ul { 59 list-style: none; 60 margin: 0; 61 padding: 0; 62 } 63 64 .ecc-status-details li { 65 margin-bottom: 6px; 66 display: flex; 67 align-items: center; 68 gap: 8px; 69 } 70 71 .ecc-status-details strong { 72 min-width: 140px; 73 } 74 75 .ecc-check { 76 display: inline-block; 77 font-weight: bold; 78 font-size: 14px; 79 min-width: 16px; 80 text-align: center; 81 } 82 83 .ecc-check-ok { 84 color: #1e7e34; 85 } 86 87 .ecc-check-fail { 88 color: #b12331; 89 } 90 91 .ecc-status-toggle { 92 cursor: pointer; 93 margin-top: 4px; 94 font-size: 12px; 95 } 96 97 .ecc-status-refresh { 98 cursor: pointer; 11 99 } 12 100 -
easy-critical-css/trunk/assets/admin.js
r3394020 r3401089 29 29 } 30 30 31 function toggleAutoModeStatus() { 32 const mode = document.querySelector('input[name="easy_cc_critical_css_mode"]:checked')?.value 33 const statusContainer = document.getElementById('auto-mode-status-container') 34 if (!statusContainer) return 35 36 // Only show when mode is auto 37 statusContainer.style.display = (mode === 'auto') ? '' : 'none' 38 } 39 31 40 function toggleSettingVisibility(settingId, behavior = false) { 32 41 const mode = document.querySelector('input[name="easy_cc_critical_css_mode"]:checked')?.value … … 41 50 row.style.display = (mode === "manual") ? "none" : "table-row" 42 51 } 52 } 53 } 54 55 function updateExcludeCssLabel() { 56 var mode = document.querySelector('input[name="easy_cc_critical_css_mode"]:checked')?.value 57 var container = document.getElementById('exclude-css-files-container') 58 console.log(mode, container) 59 if (!container) return 60 var row = container.closest('tr') 61 var labelEl = null 62 console.log(row) 63 if (row) { 64 labelEl = row.querySelector('th') 65 } 66 var descEl = container.querySelector('.description') 67 console.log(labelEl, descEl) 68 if (!labelEl || !descEl) return 69 70 if (mode === 'manual') { 71 if (easyCriticalCss.excludeLabelManual) labelEl.textContent = easyCriticalCss.excludeLabelManual 72 if (easyCriticalCss.excludeDescManual) descEl.innerHTML = easyCriticalCss.excludeDescManual 73 } else { 74 if (easyCriticalCss.excludeLabelAuto) labelEl.textContent = easyCriticalCss.excludeLabelAuto 75 if (easyCriticalCss.excludeDescAuto) descEl.innerHTML = easyCriticalCss.excludeDescAuto 43 76 } 44 77 } … … 52 85 toggleSettingVisibility('forced-secondary-selectors-container', true) 53 86 toggleSettingVisibility('add-common-secondary-container', true) 54 toggleSettingVisibility('exclude-css-files-container')55 87 toggleSettingVisibility('ignore-cross-domain-css-container') 56 88 toggleSettingVisibility('serve-css-from-files-container') … … 61 93 toggleSettingVisibility('cloudflare-zone-id-container') 62 94 toggleDeleteSection() 95 toggleAutoModeStatus() 96 updateExcludeCssLabel() 63 97 }) 64 98 }) … … 140 174 } 141 175 176 // Auto mode status details toggle 177 document.querySelectorAll('.ecc-status-toggle').forEach(function(btn) { 178 btn.addEventListener('click', function(e) { 179 e.preventDefault() 180 const statusContainer = this.closest('.ecc-auto-mode-status') 181 if (!statusContainer) return 182 183 const details = statusContainer.querySelector('.ecc-status-details') 184 if (!details) return 185 186 const isVisible = details.style.display !== 'none' 187 details.style.display = isVisible ? 'none' : 'block' 188 this.textContent = isVisible ? 'Details' : 'Hide' 189 }) 190 }) 191 192 // Auto mode status refresh button 193 document.querySelectorAll('.ecc-status-refresh').forEach(function(btn) { 194 btn.addEventListener('click', function(e) { 195 e.preventDefault() 196 197 btn.disabled = true 198 btn.textContent = 'Refreshing...' 199 200 fetch(`${easyCriticalCss.restUrl}refresh-auto-mode-status`, { 201 method: 'POST', 202 headers: { 203 'Content-Type': 'application/json', 204 'X-WP-Nonce': easyCriticalCss.nonce 205 } 206 }) 207 .then(response => { 208 if (!response.ok) { 209 throw new Error('HTTP ' + response.status + ': ' + response.statusText) 210 } 211 return response.json() 212 }) 213 .then(data => { 214 // Update status display 215 const statusContainer = btn.closest('.ecc-auto-mode-status') 216 if (!statusContainer) return 217 218 const allOk = data.all_ok 219 const localOk = data.local_ok 220 const restApiOk = data.rest_api_ok 221 222 // Update main status 223 const icon = statusContainer.querySelector('.ecc-status-icon') 224 const text = statusContainer.querySelector('.ecc-status-text') 225 const statusClass = allOk ? 'ecc-status-ready' : 'ecc-status-blocked' 226 227 icon.style.color = allOk ? '#1e7e34' : '#b12331' 228 icon.textContent = allOk ? '✓' : '✕' 229 text.textContent = allOk ? 'Auto mode ready' : 'Auto mode cannot run' 230 231 statusContainer.className = 'ecc-auto-mode-status ' + statusClass 232 233 // Update details 234 const checks = statusContainer.querySelectorAll('.ecc-check') 235 const detailItems = statusContainer.querySelectorAll('.ecc-status-details li') 236 237 if (checks[0]) { 238 checks[0].className = 'ecc-check ' + (localOk ? 'ecc-check-ok' : 'ecc-check-fail') 239 checks[0].textContent = localOk ? '✓' : '✕' 240 } 241 242 if (checks[1]) { 243 checks[1].className = 'ecc-check ' + (restApiOk ? 'ecc-check-ok' : 'ecc-check-fail') 244 checks[1].textContent = restApiOk ? '✓' : '✕' 245 } 246 247 // Update error messages in detail items 248 if (detailItems[0]) { 249 const localErrorEl = detailItems[0].querySelector('em') || detailItems[0].lastChild 250 if (localErrorEl && !localOk) { 251 if (!detailItems[0].querySelector('em')) { 252 const em = document.createElement('em') 253 em.textContent = 'Site is local' 254 detailItems[0].appendChild(em) 255 } 256 } else if (localErrorEl && localOk && localErrorEl.tagName === 'EM') { 257 localErrorEl.remove() 258 } 259 } 260 261 if (detailItems[1]) { 262 const apiErrorEl = detailItems[1].querySelector('em') || detailItems[1].lastChild 263 if (apiErrorEl && !restApiOk) { 264 if (!detailItems[1].querySelector('em')) { 265 const em = document.createElement('em') 266 em.textContent = 'Generator cannot reach the site' 267 detailItems[1].appendChild(em) 268 } 269 } else if (apiErrorEl && restApiOk && apiErrorEl.tagName === 'EM') { 270 apiErrorEl.remove() 271 } 272 } 273 274 btn.disabled = false 275 btn.textContent = 'Refresh' 276 }) 277 .catch(error => { 278 console.error('Error refreshing auto mode status:', error) 279 btn.disabled = false 280 btn.textContent = 'Refresh' 281 }) 282 }) 283 }) 284 142 285 // Run on page load 143 286 toggleSettingVisibility('secondary-css-behavior-container') … … 145 288 toggleSettingVisibility('forced-secondary-selectors-container', true) 146 289 toggleSettingVisibility('add-common-secondary-container', true) 147 toggleSettingVisibility('exclude-css-files-container')148 290 toggleSettingVisibility('ignore-cross-domain-css-container') 149 291 toggleSettingVisibility('serve-css-from-files-container') … … 154 296 toggleSettingVisibility('cloudflare-zone-id-container') 155 297 toggleDeleteSection() 298 toggleAutoModeStatus() 299 updateExcludeCssLabel() 156 300 }) -
easy-critical-css/trunk/easy-critical-css.php
r3395875 r3401089 2 2 /** 3 3 * Plugin Name: Easy Critical CSS 4 * Description: Easily inject Critical CSS and optimized Secondary CSS to improve page speed and performance.5 * Version: 1.4. 24 * Description: Easily inject Critical CSS and Secondary CSS (with unused styles removed) to improve site speed and performance. 5 * Version: 1.4.3 6 6 * Requires at least: 6.2 7 7 * Tested up to: 6.8.3 -
easy-critical-css/trunk/inc/class-admin-settings.php
r3395875 r3401089 219 219 <?php } ?> 220 220 <?php 221 } elseif ( $type === 'status-indicator' ) { 222 $status = Helpers::get_auto_mode_status(); 223 $all_ok = $status['all_ok']; 224 225 $status_color = $all_ok ? '#1e7e34' : '#b12331'; 226 $status_icon = $all_ok ? '✓' : '✗'; 227 $status_text = $all_ok ? __( 'Auto mode ready', 'easy-critical-css' ) : __( 'Auto mode cannot run', 'easy-critical-css' ); 228 $status_class = $all_ok ? 'ecc-status-ready' : 'ecc-status-blocked'; 229 ?> 230 <div class="ecc-auto-mode-status <?php echo esc_attr( $status_class ); ?>"> 231 <div class="ecc-status-main"> 232 <span class="ecc-status-icon" style="color: <?php echo esc_attr( $status_color ); ?>;"> 233 <?php echo esc_html( $status_icon ); ?> 234 </span> 235 <span class="ecc-status-text"> 236 <?php echo esc_html( $status_text ); ?> 237 </span> 238 </div> 239 <div class="ecc-status-details" style="display: none;"> 240 <ul> 241 <li> 242 <strong><?php esc_html_e( 'Active API Key:', 'easy-critical-css' ); ?></strong> 243 <span class="ecc-check <?php echo $status['active_key'] ? 'ecc-check-ok' : 'ecc-check-fail'; ?>"> 244 <?php echo $status['active_key'] ? '✓' : '✕'; ?> 245 </span> 246 <?php 247 if ( ! $status['active_key'] ) { 248 esc_html_e( 'No active API key', 'easy-critical-css' ); 249 } 250 ?> 251 </li> 252 253 <li> 254 <strong><?php esc_html_e( 'Local Install:', 'easy-critical-css' ); ?></strong> 255 <span class="ecc-check <?php echo $status['local_ok'] ? 'ecc-check-ok' : 'ecc-check-fail'; ?>"> 256 <?php echo $status['local_ok'] ? '✓' : '✕'; ?> 257 </span> 258 <?php 259 if ( ! $status['local_ok'] ) { 260 esc_html_e( 'Site is local', 'easy-critical-css' ); 261 } 262 ?> 263 </li> 264 <li> 265 <strong><?php esc_html_e( 'REST API Reachable:', 'easy-critical-css' ); ?></strong> 266 <span class="ecc-check <?php echo $status['rest_api_ok'] ? 'ecc-check-ok' : 'ecc-check-fail'; ?>"> 267 <?php echo $status['rest_api_ok'] ? '✓' : '✕'; ?> 268 </span> 269 <?php 270 if ( ! $status['rest_api_ok'] ) { 271 esc_html_e( 'Generator cannot reach the site', 'easy-critical-css' ); 272 } 273 ?> 274 </li> 275 </ul> 276 </div> 277 <div style="margin-top: 10px;"> 278 <button type="button" class="ecc-status-toggle" style="background: none; border: none; padding: 0; color: #007cba; cursor: pointer; text-decoration: underline; font-size: 12px; margin-right: 15px;"> 279 <?php esc_html_e( 'Details', 'easy-critical-css' ); ?> 280 </button> 281 <button type="button" class="ecc-status-refresh" style="background: none; border: none; padding: 0; color: #007cba; cursor: pointer; text-decoration: underline; font-size: 12px;"> 282 <?php esc_html_e( 'Refresh', 'easy-critical-css' ); ?> 283 </button> 284 </div> 285 </div> 286 <?php 221 287 } 222 288 … … 247 313 if ( ! empty( $warning ) ) { 248 314 ?> 249 <p style="color: # a32b2d; font-weight: bold;">315 <p style="color: #b12331; font-weight: bold;"> 250 316 <?php echo wp_kses_post( $warning ); ?> 251 317 </p> … … 258 324 } 259 325 260 public static function get_settings_schema() { 326 public static function get_critical_css_mode_text() { 327 $getting_started_link = sprintf( 328 '<p style="margin:1.5em 0;"><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></p>', 329 'https://criticalcss.net/docs/', 330 esc_html__( 'Need help getting started? View the Easy Critical CSS guides.', 'easy-critical-css' ) 331 ); 332 261 333 $critical_css_mode_text = sprintf( 262 // translators: %s is a link to the API .263 __( ' The Auto mode sends page data to an API at %s that requires akey.', 'easy-critical-css' ),334 // translators: %s is a link to the API site. 335 __( 'Auto mode sends page data to an external API at %s and requires an API key.', 'easy-critical-css' ), 264 336 '<a href="https://criticalcss.net/" target="_blank" rel="noopener noreferrer">CriticalCSS.net</a>' 265 337 ); 266 $critical_css_mode_warn = "<p style=\"color: #a32b2d; font-weight: bold;\">$critical_css_mode_text</p>"; 267 // Add a simple get API link if they don't have one added. 338 339 // Base warning (shown in red). 340 $critical_css_mode_warn = $getting_started_link; 341 $critical_css_mode_warn .= sprintf( 342 '<p style="color:#b12331;font-weight:bold;">%s</p>', 343 $critical_css_mode_text 344 ); 345 268 346 if ( empty( Helpers::get_api_key() ) ) { 269 $critical_css_mode_warn = sprintf( 270 '<p style="color: #a32b2d; font-weight: bold;">%s <a href="%s">%s</a>. <span class="activate-license easy-critical-css"><a href="#">Add API Key</a>.</span></p><p style="font-weight: bold;">Want to get started without an API key? Use <a href="https://criticalcss.net/#pricing" target="_blank" rel="noopener noreferrer">CriticalCSS.net</a> to generate Critical CSS manually and paste it into your settings.</p>', 271 $critical_css_mode_text, 347 // Auto mode path. 348 $critical_css_mode_warn .= sprintf( 349 '<p style="font-weight:bold;">%1$s <a href="%2$s">%3$s</a> · <span class="activate-license easy-critical-css"><a href="#">%4$s</a></span></p>', 350 esc_html__( 'To use Auto mode, you need an API key.', 'easy-critical-css' ), 272 351 esc_url( admin_url( 'admin.php?page=easy-critical-css-settings-pricing' ) ), 273 __( 'Get an API key', 'easy-critical-css' ) 352 esc_html__( 'Get an API key', 'easy-critical-css' ), 353 esc_html__( 'Add your API key', 'easy-critical-css' ) 274 354 ); 275 } 355 356 357 // Free/manual path. 358 $critical_css_mode_warn .= sprintf( 359 '<p>%1$s <a href="https://criticalcss.net/#free-generator" target="_blank" rel="noopener noreferrer">%2$s</a> %3$s</p>', 360 esc_html__( 'Staying in free Manual mode? You can generate CSS manually at', 'easy-critical-css' ), 361 'CriticalCSS.net', 362 esc_html__( 'and paste it into your settings.', 'easy-critical-css' ) 363 ); 364 } 365 366 return $critical_css_mode_warn; 367 } 368 369 public static function get_exclude_css_strings() { 370 $label_auto = __( 'Exclude CSS Files from Critical CSS', 'easy-critical-css' ); 371 $label_manual = __( 'Always Loaded CSS Files', 'easy-critical-css' ); 372 373 $desc_auto = __( 'Enter the CSS file URLs or partial matches that should be excluded from Critical CSS generation.', 'easy-critical-css' ); 374 $desc_manual = __( 'Enter the CSS file URLs or partial matches that should always be loaded (not inlined) when using Manual mode.', 'easy-critical-css' ); 375 376 $common = '<br><em>' . sprintf( 377 // translators: %1$s and %2$s are opening and closing <strong> tags for emphasis. 378 __( 'One entry per line. Example: %1$shttps://example.com/style.css%2$s or %1$smy-style.css%2$s', 'easy-critical-css' ), 379 '<strong>', 380 '</strong>' 381 ) . '</em>'; 382 383 return [ 384 'label_auto' => $label_auto, 385 'label_manual' => $label_manual, 386 'desc_auto' => $desc_auto . $common, 387 'desc_manual' => $desc_manual . $common, 388 ]; 389 } 390 391 public static function get_settings_schema() { 392 $critical_css_mode_warn = self::get_critical_css_mode_text(); 276 393 277 394 $forced_selectors_desc = '<br><em>' . sprintf( … … 287 404 ) . '</em>'; 288 405 289 $exclude_css_files_desc = __( 'Enter the CSS file URLs or partial matches that should be excluded from Critical CSS generation.', 'easy-critical-css' ) 290 . '<br><em>' . sprintf( 291 // translators: %1$s and %2$s are opening and closing <strong> tags for emphasis. 292 __( 'One entry per line. Example: %1$shttps://example.com/style.css%2$s or %1$smy-style.css%2$s', 'easy-critical-css' ), 293 '<strong>', 294 '</strong>' 295 ) . '</em>'; 406 $exclude_strings = self::get_exclude_css_strings(); 296 407 297 408 $settings = [ … … 310 421 'basic' => true, 311 422 ], 423 'auto_mode_status' => [ 424 'label' => '', 425 'type' => 'status-indicator', 426 'value_type' => 'string', 427 'desc' => '', 428 'basic' => true, 429 'hidden' => true, // Don't register as a real setting 430 ], 312 431 'secondary_css_behavior' => [ 313 432 'label' => __( 'Secondary CSS Behavior', 'easy-critical-css' ), … … 355 474 ], 356 475 'exclude_css_files' => [ 357 'label' => __( 'Exclude CSS Files from Critical CSS', 'easy-critical-css' ),476 'label' => $exclude_strings['label_auto'], 358 477 'type' => 'textarea', 359 478 'value_type' => 'array', 360 'desc' => $exclude_ css_files_desc,479 'desc' => $exclude_strings['desc_auto'], 361 480 ], 362 481 'ignore_cross_domain_css' => [ … … 574 693 foreach ( $settings as $key => $setting ) { 575 694 $is_basic = isset( $setting['basic'] ) && $setting['basic']; 695 $is_hidden = isset( $setting['hidden'] ) && $setting['hidden']; 576 696 $section = $is_basic ? 'easy-critical-css-basic-section' : 'easy-critical-css-advanced-section'; 577 697 … … 579 699 "easy_cc_$key", 580 700 $setting['label'], 581 function () use ( $key, $setting, $is_basic ) {701 function () use ( $key, $setting, $is_basic, $is_hidden ) { 582 702 static $last_advanced_key = null; 583 703 static $all_keys = null; … … 617 737 ); 618 738 739 // Skip registering the setting if it's hidden (like auto_mode_status). 740 if ( $is_hidden ) { 741 continue; 742 } 743 619 744 // Plugin Checker warns against dynamic arguments in the third param. Sanitization has been double-checked. 620 745 register_setting( // phpcs:ignore PluginCheck.CodeAnalysis.SettingSanitization.register_settingDynamic … … 641 766 true 642 767 ); 768 769 $exclude_strings = self::get_exclude_css_strings(); 770 wp_localize_script( 771 'easy-critical-css-admin', 772 'easyCriticalCss', 773 [ 774 'restUrl' => esc_url_raw( rest_url( 'easy-critical-css/v1/' ) ), 775 'nonce' => wp_create_nonce( 'wp_rest' ), 776 'excludeLabelAuto' => $exclude_strings['label_auto'], 777 'excludeLabelManual' => $exclude_strings['label_manual'], 778 'excludeDescAuto' => $exclude_strings['desc_auto'], 779 'excludeDescManual' => $exclude_strings['desc_manual'], 780 ] 781 ); 643 782 644 783 wp_enqueue_style( -
easy-critical-css/trunk/inc/class-api-request-handler.php
r3388125 r3401089 39 39 $url_hash = md5( $url ); 40 40 41 // Get API-related data. 41 // Short cooldown to prevent immediate re-claim if a generation just completed. 42 $cooldown_seconds = 120; // 2 minutes 43 $existing_row = Database::get_row_by_url_hash( $url_hash ); 44 if ( $existing_row && ! empty( $existing_row['processing_status'] ) && $existing_row['processing_status'] === 'completed' ) { 45 if ( ! empty( $existing_row['generated_time'] ) ) { 46 $generated_ts = strtotime( $existing_row['generated_time'] ); 47 if ( $generated_ts !== false && ( time() - $generated_ts ) < $cooldown_seconds ) { 48 $remaining = $cooldown_seconds - ( time() - $generated_ts ); 49 $minutes = max( 1, ceil( $remaining / 60 ) ); 50 return new WP_Error( 51 'cooldown_active', 52 sprintf( __( 'Critical CSS was recently generated. Please try again in %d minute(s).', 'easy-critical-css' ), $minutes ) 53 ); 54 } 55 } 56 } 57 58 // Generate unique handshake and timestamp early to avoid race conditions with atomic DB claim. 59 $handshake = wp_generate_password( 20, false ); 60 $mysql_timestamp = current_time( 'mysql' ); 61 62 // Prepare table and stale threshold. 63 $table_name = esc_sql( Database::get_table_name() ); 64 $stale_threshold = gmdate( 'Y-m-d H:i:s', time() - ( 30 * MINUTE_IN_SECONDS ) ); 65 66 // Atomically claim non-pending existing row by updating it. 67 $update_sql = $wpdb->prepare( 68 "UPDATE {$table_name} 69 SET handshake = %s, page_url = %s, processing_status = 'pending', requested_time = %s 70 WHERE url_hash = %s AND (processing_status != 'pending' OR requested_time < %s)", 71 $handshake, 72 esc_url_raw( $url ), 73 $mysql_timestamp, 74 $url_hash, 75 $stale_threshold 76 ); 77 78 $wpdb->query( $update_sql ); 79 if ( $wpdb->rows_affected <= 0 ) { 80 // No existing row could be claimed. Check if non stale, pending row exists and return already_pending. 81 $existing_row = Database::get_row_by_url_hash( $url_hash ); 82 if ( $existing_row && isset( $existing_row['processing_status'] ) && $existing_row['processing_status'] === 'pending' ) { 83 $remaining = 0; 84 if ( ! empty( $existing_row['requested_time'] ) ) { 85 $remaining = Helpers::get_remaining_time( $existing_row['requested_time'] ); 86 } 87 88 if ( $remaining > 0 ) { 89 return new WP_Error( 90 'already_pending', 91 sprintf( __( 'Critical CSS is already being generated. Please try again in %d minutes.', 'easy-critical-css' ), $remaining ) 92 ); 93 } 94 } 95 96 // No non-stale pending row to block us, so insert a new row. 97 $insert_result = $wpdb->insert( 98 $table_name, 99 [ 100 'page_url' => esc_url_raw( $url ), 101 'url_hash' => $url_hash, 102 'post_id' => isset( $data['post_id'] ) ? (int) $data['post_id'] : null, 103 'handshake' => $handshake, 104 'processing_status' => 'pending', 105 'requested_time' => $mysql_timestamp, 106 ] 107 ); 108 109 if ( $insert_result === false ) { 110 // If insert failed, play safe and indicate another process likely claimed it. 111 return new WP_Error( 'already_pending', __( 'Critical CSS is already being generated.', 'easy-critical-css' ) ); 112 } 113 } 114 115 // Get API-related data for the outgoing request. 42 116 $uid = Helpers::get_uid(); 43 117 $api_key = Helpers::get_api_key(); 44 118 $install_id = Helpers::get_install_id(); 45 119 46 // Generate unique handshake. 47 $handshake = wp_generate_password( 20, false ); 48 49 // Construct the response URL dynamically. 120 // Construct the response URL dynamically and ensure SSL. 50 121 $response_url = site_url( '/wp-json/easy-critical-css/v1/receive' ); 51 52 // Response URL must be https. 53 $scheme = wp_parse_url( $response_url, PHP_URL_SCHEME ); 122 $scheme = wp_parse_url( $response_url, PHP_URL_SCHEME ); 54 123 if ( $scheme !== 'https' ) { 55 124 return new WP_Error( 'no_ssl', __( 'The response URL must have SSL.', 'easy-critical-css' ) ); 56 125 } 57 126 58 $mysql_timestamp = current_time( 'mysql' ); 59 60 // Convert timestamp to the URL formate we need. This prevents anjy sort of mismatch. 127 // Convert timestamp to the URL format we need for the skip param. 61 128 $url_timestamp = ''; 62 129 $datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $mysql_timestamp ); … … 64 131 $url_timestamp = $datetime->format( 'YmdHis' ); 65 132 } 66 67 $data = array_merge(68 $data,69 [70 'handshake' => $handshake,71 'page_url' => esc_url_raw( $url ),72 'processing_status' => 'pending',73 'requested_time' => $mysql_timestamp,74 'url_hash' => $url_hash,75 ]76 );77 78 Database::upsert_row( $data );79 133 80 134 $prepared_url = add_query_arg( … … 85 139 'nowprocket' => '1', 86 140 'no_optimize' => '1', 141 'perfmattersoff' => '1', 87 142 'two_nooptimize' => '1', 88 143 'mvt_flags' => 'disable_critical_css', -
easy-critical-css/trunk/inc/class-api-service.php
r3319025 r3401089 13 13 $api_url = 'https://api.criticalcss.net'; 14 14 return apply_filters( 'easy_cc_critical_css_api_url', $api_url ); 15 } 16 17 public static function test_receive_endpoint() { 18 // Check transient for cached result. 19 $cached = get_transient( 'easy_cc_is_rest_api_reachable' ); 20 if ( $cached !== false ) { 21 return (bool) $cached; 22 } 23 24 // Generate a unique handshake "nonce" for this test. 25 $nonce = wp_generate_password( 20, false ); 26 27 // Store the nonce in a transient for verification (5 minute expiry for the test). 28 set_transient( 'easy_cc_test_nonce_' . substr( $nonce, 0, 8 ), $nonce, 5 * MINUTE_IN_SECONDS ); 29 30 $api_url = self::get_api_url(); 31 $body = [ 32 'siteUrl' => esc_url_raw( home_url() ), 33 'nonce' => $nonce, 34 ]; 35 36 $response = wp_remote_post( 37 trailingslashit( $api_url ) . 'v1/test-receive-endpoint', 38 [ 39 'body' => wp_json_encode( $body ), 40 'headers' => [ 'Content-Type' => 'application/json' ], 41 'timeout' => 30, 42 ] 43 ); 44 45 if ( is_wp_error( $response ) ) { 46 set_transient( 'easy_cc_is_rest_api_reachable', '0', 6 * HOUR_IN_SECONDS ); 47 return false; 48 } 49 50 $code = wp_remote_retrieve_response_code( $response ); 51 $body_content = json_decode( wp_remote_retrieve_body( $response ), true ); 52 53 $success = ( 200 === $code && ! empty( $body_content['success'] ) ); 54 55 // Cache the result for 6 hours. 56 set_transient( 'easy_cc_is_rest_api_reachable', $success ? '1' : '0', 6 * HOUR_IN_SECONDS ); 57 58 return $success; 15 59 } 16 60 -
easy-critical-css/trunk/inc/class-compatibility-perfmatters.php
r3395875 r3401089 10 10 public static function init() { 11 11 add_filter( 'perfmatters_rest_api_exceptions', [ __CLASS__, 'add_rest_api_exception' ] ); 12 add_action( 'admin_notices', [ __CLASS__, 'display_admin_notice' ] ); 12 13 } 13 14 … … 17 18 return $exceptions; 18 19 } 20 21 public static function is_perfmatters_installed() { 22 return defined( 'PERFMATTERS_VERSION' ); 23 } 24 25 public static function has_conflicting_css_settings() { 26 $override = apply_filters( 'easy_cc_mock_perfmatters_conflict', null ); 27 if ( null !== $override ) { 28 return (bool) $override; 29 } 30 31 if ( ! self::is_perfmatters_installed() ) { 32 return false; 33 } 34 35 $options = get_option( 'perfmatters_options' ); 36 if ( empty( $options ) || ! is_array( $options ) ) { 37 return false; 38 } 39 40 $assets = isset( $options['assets'] ) && is_array( $options['assets'] ) ? $options['assets'] : []; 41 $remove_unused_css = ! empty( $assets['remove_unused_css'] ); 42 43 // Perfmatters exposes the Clear Used CSS control when Remove Unused CSS is active, so treat either as conflicting. 44 if ( ! $remove_unused_css && ! empty( $assets['clear_used_css'] ) ) { 45 $remove_unused_css = true; 46 } 47 48 $minify_css = ! empty( $assets['minify_css'] ); 49 50 return (bool) ( $remove_unused_css || $minify_css ); 51 } 52 53 public static function display_admin_notice() { 54 if ( ! self::has_conflicting_css_settings() ) { 55 return; 56 } 57 58 $settings_url = is_network_admin() 59 ? network_admin_url( 'settings.php?page=perfmatters#css' ) 60 : admin_url( 'options-general.php?page=perfmatters#css' ); 61 ?> 62 63 <div class="notice notice-warning"> 64 <p> 65 <?php esc_html_e( 'Perfmatters Clear Used CSS or Minify CSS is currently active. Easy Critical CSS has been paused to avoid conflicts.', 'easy-critical-css' ); ?> 66 </p> 67 <?php if ( current_user_can( 'manage_options' ) ) { ?> 68 <p> 69 <a href="<?php echo esc_url( $settings_url ); ?>" class="button button-primary"> 70 <?php esc_html_e( 'Open Perfmatters CSS Settings', 'easy-critical-css' ); ?> 71 </a> 72 </p> 73 <?php } ?> 74 </div> 75 <?php 76 } 19 77 } -
easy-critical-css/trunk/inc/class-critical-css-status.php
r3327873 r3401089 48 48 $generated = Critical_CSS::get_generated_css( $identifier ); 49 49 50 // No requested time or hash exists (unlikely). 50 // No requested time or hash exists (unlikely). No requested time means we cannot determine actual expires time. 51 51 if ( empty( $generated['requested_time'] ) || empty( $generated['url_hash'] ) ) { 52 return true;52 return false; 53 53 } 54 54 … … 223 223 224 224 wp_enqueue_script( 'wp-api-fetch' ); 225 226 $api_settings = [ 227 'root' => esc_url_raw( rest_url() ), 228 'nonce' => wp_create_nonce( 'wp_rest' ), 229 ]; 230 231 // Ensure apiFetch requests include the REST root and nonce on the front end. 232 wp_add_inline_script( 233 'wp-api-fetch', 234 sprintf( 235 'window.wpApiSettings = Object.assign({}, window.wpApiSettings || {}, %s);', 236 wp_json_encode( $api_settings ) 237 ), 238 'before' 239 ); 225 240 ?> 226 241 <script> 227 242 document.addEventListener('DOMContentLoaded', function () { 243 if (window.wp && window.wp.apiFetch && !window.easyCriticalCssApiFetchConfigured) { 244 const { apiFetch } = window.wp 245 const root = window.wpApiSettings && window.wpApiSettings.root 246 const nonce = window.wpApiSettings && window.wpApiSettings.nonce 247 248 if (typeof apiFetch.createRootURLMiddleware === 'function' && root) { 249 apiFetch.use(apiFetch.createRootURLMiddleware(root)) 250 } 251 252 if (typeof apiFetch.createNonceMiddleware === 'function' && nonce) { 253 apiFetch.use(apiFetch.createNonceMiddleware(nonce)) 254 } 255 256 window.easyCriticalCssApiFetchConfigured = true 257 } 258 228 259 const eccIdentifier = '<?php echo esc_js( $identifier ); ?>' 260 const apiRoot = (window.wpApiSettings && window.wpApiSettings.root) || '/wp-json/' 261 const buildUrl = (route) => { 262 try { 263 return new URL(route.replace(/^\//, ''), apiRoot).toString() 264 } catch (err) { 265 return apiRoot.replace(/\/?$/, '/') + route.replace(/^\//, '') 266 } 267 } 268 const buildHeaders = () => { 269 const headers = { 'Content-Type': 'application/json' } 270 if (window.wpApiSettings && window.wpApiSettings.nonce) { 271 headers['X-WP-Nonce'] = window.wpApiSettings.nonce 272 } 273 return headers 274 } 229 275 document.querySelectorAll('.critical-css-regenerate-link a').forEach(link => { 230 276 link.addEventListener('click', function (event) { … … 237 283 238 284 wp.apiFetch({ 239 path: '/easy-critical-css/v1/generate',285 url: buildUrl('/easy-critical-css/v1/generate'), 240 286 method: 'POST', 241 headers: { 'Content-Type': 'application/json' },287 headers: buildHeaders(), 242 288 body: JSON.stringify({ identifier: eccIdentifier }) 243 289 }) … … 256 302 257 303 wp.apiFetch({ 258 path: '/easy-critical-css/v1/delete',304 url: buildUrl('/easy-critical-css/v1/delete'), 259 305 method: 'POST', 260 headers: { 'Content-Type': 'application/json' },306 headers: buildHeaders(), 261 307 body: JSON.stringify({ identifier: eccIdentifier }) 262 308 }) -
easy-critical-css/trunk/inc/class-critical-css.php
r3336970 r3401089 111 111 $data = Database::get_row( $identifier ); 112 112 113 // Prefer canonical permalink hash for this post. 114 if ( is_numeric( $identifier ) ) { 115 $expected_hash = Helpers::get_url_hash( $identifier ); 116 if ( empty( $data['url_hash'] ) || $data['url_hash'] !== $expected_hash ) { 117 $by_hash = Database::get_row_by_url_hash( $expected_hash ); 118 if ( ! empty( $by_hash ) ) { 119 $data = $by_hash; 120 } 121 } 122 } 123 113 124 // If we've already retrieved generated Critical based off hash, then return that. 114 125 if ( ! empty( $data['url_hash'] ) && isset( self::$generated_css[ $data['url_hash'] ] ) ) { -
easy-critical-css/trunk/inc/class-database.php
r3336970 r3401089 140 140 $table_name = esc_sql( self::get_table_name() ); 141 141 142 return $wpdb->get_row( 143 $wpdb->prepare( "SELECT * FROM $table_name WHERE post_id = %d", $post_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 144 ARRAY_A 142 // Prefer canonical permalink hash for this post. 143 $expected_hash = Helpers::get_url_hash( (int) $post_id ); 144 if ( ! empty( $expected_hash ) ) { 145 $by_hash = self::get_row_by_url_hash( $expected_hash ); 146 if ( ! empty( $by_hash ) ) { 147 Debug::ecc_log( 148 [ 149 'step' => 'db_pick_by_hash', 150 'post_id' => $post_id, 151 'pick_id' => $by_hash['id'], 152 'url_hash'=> $expected_hash, 153 'reason' => 'matched_permalink_hash' 154 ] 155 ); 156 157 return $by_hash; 158 } 159 } 160 161 // If no row matches canonical hash, choose the "best" row by preferring completed or pending rows, 162 // then most recent generated_time, then newest id. This avoids returning an older/expired duplicate. 163 $sql = $wpdb->prepare( 164 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 165 "SELECT * FROM {$table_name} WHERE post_id = %d 166 ORDER BY FIELD(processing_status, 'completed','pending','unprocessed','failed','expired') DESC, 167 COALESCE(generated_time, '1970-01-01') DESC, 168 id DESC 169 LIMIT 1", 170 $post_id 145 171 ); 172 173 $row = $wpdb->get_row( $sql, ARRAY_A ); 174 175 if ( ! empty( $row ) ) { 176 Debug::ecc_log( 177 [ 178 'step' => 'db_pick_best_row', 179 'post_id' => $post_id, 180 'pick_id' => $row['id'], 181 'url_hash'=> isset( $row['url_hash'] ) ? $row['url_hash'] : null, 182 'status' => isset( $row['processing_status'] ) ? $row['processing_status'] : null, 183 'generated_time' => isset( $row['generated_time'] ) ? $row['generated_time'] : null, 184 ] 185 ); 186 } 187 188 return $row; 146 189 } 147 190 … … 154 197 // If we have a number, it's a post ID. 155 198 if ( is_numeric( $identifier ) ) { 199 // Get expected URL hash first, avoiding older duplicates for this post. 200 $expected_hash = Helpers::get_url_hash( $identifier ); 201 if ( ! empty( $expected_hash ) ) { 202 $by_hash = self::get_row_by_url_hash( $expected_hash ); 203 if ( ! empty( $by_hash ) ) { 204 return $by_hash; 205 } 206 } 207 156 208 return self::get_row_by_post_id( $identifier ); 157 209 } … … 173 225 } elseif ( ! empty( $sanitized_data['post_id'] ) ) { 174 226 $where['post_id'] = $sanitized_data['post_id']; 175 $existing_row = self::get_row_by_ url_hash( $sanitized_data['post_id'] );227 $existing_row = self::get_row_by_post_id( $sanitized_data['post_id'] ); 176 228 } elseif ( ! empty( $sanitized_data['page_url'] ) ) { 177 229 $where['page_url'] = $sanitized_data['page_url']; 178 $existing_row = self::get_row_by_url_hash( $sanitized_data['page_url'] ); 179 } else { 180 // We need at least one unique identifier (page_url, post_id, url_hash) 181 return new WP_Error( 'missing_identifier', __( 'No valid identifier provided for saving settings.', 'easy-critical-css' ) ); 182 } 183 184 // Do we update or insert? 185 if ( $existing_row ) { 186 $wpdb->update( self::get_table_name(), $sanitized_data, $where ); 230 $existing_row = self::get_row_by_url( $sanitized_data['page_url'] ); 231 } 232 233 // If existing row was found, update it. 234 if ( ! empty( $existing_row ) ) { 235 $updated = $wpdb->update( self::get_table_name(), $sanitized_data, $where ); 236 237 // If update returned false, it failed. 238 if ( $updated === false ) { 239 return new WP_Error( 'db_update_failed', __( 'Failed to update existing row.', 'easy-critical-css' ) ); 240 } 241 187 242 return [ 188 243 'row_id' => $existing_row['id'], 189 244 'status' => 'updated', 190 245 ]; 191 } else { 192 $wpdb->insert( self::get_table_name(), $sanitized_data ); 193 return [ 194 'row_id' => $wpdb->insert_id, 195 'status' => 'inserted', 196 ]; 197 } 246 } 247 248 // No existing row found, insert a new one. 249 $insert_result = $wpdb->insert( self::get_table_name(), $sanitized_data ); 250 if ( $insert_result === false ) { 251 return new WP_Error( 'db_insert_failed', __( 'Failed to insert row.', 'easy-critical-css' ) ); 252 } 253 254 return [ 255 'row_id' => $wpdb->insert_id, 256 'status' => 'inserted', 257 ]; 198 258 } 199 259 … … 210 270 ]; 211 271 212 // Add a status if we have one.213 272 if ( ! empty( $status ) ) { 214 $valid_statuses = [ 'completed', 'expired', 'failed', 'pending', 'unprocessed' ]; 215 if ( in_array( $status, $valid_statuses, true ) ) { 216 $data['processing_status'] = $status; 217 } 273 $data['processing_status'] = $status; 218 274 } 219 275 … … 309 365 ); 310 366 } 367 368 public static function dedupe_post_rows( $post_id = null, $handshake = null, $dry_run = true ) { 369 global $wpdb; 370 371 $table_name = esc_sql( self::get_table_name() ); 372 373 if ( empty( $post_id ) ) { 374 return [ 'action' => 'none', 'reason' => 'missing_post_id' ]; 375 } 376 377 $rows = $wpdb->get_results( 378 $wpdb->prepare( "SELECT * FROM {$table_name} WHERE post_id = %d", (int) $post_id ), 379 ARRAY_A 380 ); 381 382 if ( ! is_array( $rows ) || count( $rows ) <= 1 ) { 383 return [ 'action' => 'none', 'reason' => 'no_duplicates', 'rows' => $rows ]; 384 } 385 386 // Try to pick canonical row. 387 $canonical = null; 388 389 // 1) Prefer handshake match if provided. 390 if ( ! empty( $handshake ) ) { 391 foreach ( $rows as $r ) { 392 if ( ! empty( $r['handshake'] ) && hash_equals( $r['handshake'], $handshake ) ) { 393 $canonical = $r; 394 break; 395 } 396 } 397 } 398 399 // 2) Prefer canonical permalink hash. 400 if ( empty( $canonical ) ) { 401 $expected_hash = Helpers::get_url_hash( (int) $post_id ); 402 if ( ! empty( $expected_hash ) ) { 403 $by_hash = self::get_row_by_url_hash( $expected_hash ); 404 if ( ! empty( $by_hash ) && isset( $by_hash['post_id'] ) && (int) $by_hash['post_id'] === (int) $post_id ) { 405 $canonical = $by_hash; 406 } 407 } 408 } 409 410 // 3) Fallback: choose best by processing_status, generated_time, id. 411 if ( empty( $canonical ) ) { 412 $best = null; 413 $priority = [ 'completed', 'pending', 'unprocessed', 'failed', 'expired' ]; 414 foreach ( $rows as $r ) { 415 if ( empty( $best ) ) { 416 $best = $r; 417 continue; 418 } 419 420 $best_status_idx = array_search( isset( $best['processing_status'] ) ? $best['processing_status'] : 'unprocessed', $priority, true ); 421 $r_status_idx = array_search( isset( $r['processing_status'] ) ? $r['processing_status'] : 'unprocessed', $priority, true ); 422 423 if ( $r_status_idx === false ) { 424 $r_status_idx = count( $priority ); 425 } 426 if ( $best_status_idx === false ) { 427 $best_status_idx = count( $priority ); 428 } 429 430 if ( $r_status_idx < $best_status_idx ) { 431 $best = $r; 432 continue; 433 } 434 435 // If same status priority, prefer later generated_time. 436 $best_gen = ! empty( $best['generated_time'] ) ? strtotime( $best['generated_time'] ) : 0; 437 $r_gen = ! empty( $r['generated_time'] ) ? strtotime( $r['generated_time'] ) : 0; 438 if ( $r_gen > $best_gen ) { 439 $best = $r; 440 continue; 441 } 442 443 // Finally prefer higher id. 444 if ( (int) $r['id'] > (int) $best['id'] ) { 445 $best = $r; 446 } 447 } 448 449 $canonical = $best; 450 } 451 452 if ( empty( $canonical ) ) { 453 return [ 'action' => 'error', 'reason' => 'could_not_pick_canonical' ]; 454 } 455 456 $canonical_id = (int) $canonical['id']; 457 $canonical_hash = isset( $canonical['url_hash'] ) ? $canonical['url_hash'] : null; 458 459 // Build merged data starting from canonical. 460 $merged = $canonical; 461 462 foreach ( $rows as $r ) { 463 if ( (int) $r['id'] === $canonical_id ) { 464 continue; 465 } 466 467 // Merge settings (JSON) without losing keys. 468 $canon_settings = ! empty( $merged['settings'] ) ? json_decode( $merged['settings'], true ) : []; 469 $other_settings = ! empty( $r['settings'] ) ? json_decode( $r['settings'], true ) : []; 470 if ( is_array( $other_settings ) ) { 471 foreach ( $other_settings as $k => $v ) { 472 if ( ! isset( $canon_settings[ $k ] ) || $canon_settings[ $k ] === '' || $canon_settings[ $k ] === null ) { 473 $canon_settings[ $k ] = $v; 474 } 475 } 476 $merged['settings'] = wp_json_encode( $canon_settings ); 477 } 478 479 // For css fields, prefer non-empty and the most recent generated_time. 480 $fields = [ 'critical_css', 'remaining_css', 'secondary_css', 'size_savings', 'last_error' ]; 481 foreach ( $fields as $f ) { 482 $cur = ! empty( $merged[ $f ] ) ? $merged[ $f ] : ''; 483 $oth = ! empty( $r[ $f ] ) ? $r[ $f ] : ''; 484 if ( $oth === '' ) { 485 continue; 486 } 487 $cur_gen = ! empty( $merged['generated_time'] ) ? strtotime( $merged['generated_time'] ) : 0; 488 $oth_gen = ! empty( $r['generated_time'] ) ? strtotime( $r['generated_time'] ) : 0; 489 if ( $cur === '' || $oth_gen >= $cur_gen ) { 490 $merged[ $f ] = $oth; 491 } 492 } 493 494 // requested_time: prefer most recent non-empty requested_time to avoid losing it. 495 $cur_req = ! empty( $merged['requested_time'] ) ? strtotime( $merged['requested_time'] ) : 0; 496 $oth_req = ! empty( $r['requested_time'] ) ? strtotime( $r['requested_time'] ) : 0; 497 if ( $oth_req > $cur_req ) { 498 $merged['requested_time'] = $r['requested_time']; 499 } 500 501 // generated_time: prefer most recent. 502 $cur_gen = ! empty( $merged['generated_time'] ) ? strtotime( $merged['generated_time'] ) : 0; 503 $oth_gen = ! empty( $r['generated_time'] ) ? strtotime( $r['generated_time'] ) : 0; 504 if ( $oth_gen > $cur_gen ) { 505 $merged['generated_time'] = $r['generated_time']; 506 } 507 508 // processing_status: prefer higher priority status. 509 $priority = [ 'completed' => 5, 'pending' => 4, 'unprocessed' => 3, 'failed' => 2, 'expired' => 1 ]; 510 $cur_pr = isset( $merged['processing_status'] ) ? $merged['processing_status'] : 'unprocessed'; 511 $oth_pr = isset( $r['processing_status'] ) ? $r['processing_status'] : 'unprocessed'; 512 if ( isset( $priority[ $oth_pr ] ) && isset( $priority[ $cur_pr ] ) && $priority[ $oth_pr ] > $priority[ $cur_pr ] ) { 513 $merged['processing_status'] = $oth_pr; 514 } 515 } 516 517 // Prepare sanitized data for DB write. 518 $write_data = []; 519 $allowed_cols = [ 'page_url', 'url_hash', 'post_id', 'requested_time', 'generated_time', 'critical_css', 'remaining_css', 'secondary_css', 'size_savings', 'handshake', 'processing_status', 'settings', 'last_error' ]; 520 foreach ( $allowed_cols as $c ) { 521 if ( isset( $merged[ $c ] ) ) { 522 $write_data[ $c ] = $merged[ $c ]; 523 } 524 } 525 526 // Dry-run: return plan with what we'd do. 527 $duplicate_ids = array_map( function ( $r ) use ( $canonical_id ) { 528 return (int) $r['id']; 529 }, array_filter( $rows, function ( $r ) use ( $canonical_id ) { 530 return (int) $r['id'] !== $canonical_id; 531 } ) ); 532 533 $plan = [ 534 'action' => $dry_run ? 'dry_run' : 'apply', 535 'post_id' => (int) $post_id, 536 'canonical_id' => $canonical_id, 537 'canonical_hash'=> $canonical_hash, 538 'merged' => $write_data, 539 'delete_ids' => $duplicate_ids, 540 'rows' => $rows, 541 ]; 542 543 Debug::ecc_log( array_merge( [ 'step' => 'dedupe_plan' ], $plan ) ); 544 545 if ( $dry_run ) { 546 return $plan; 547 } 548 549 // Apply the merge in a transaction. 550 $wpdb->query( 'START TRANSACTION' ); 551 552 $sanitized = self::sanitize_data( $write_data ); 553 $updated = $wpdb->update( self::get_table_name(), $sanitized, [ 'id' => $canonical_id ] ); 554 if ( $updated === false ) { 555 $wpdb->query( 'ROLLBACK' ); 556 return [ 'action' => 'error', 'reason' => 'update_failed' ]; 557 } 558 559 // Delete duplicates 560 foreach ( $duplicate_ids as $del_id ) { 561 $del_ok = $wpdb->delete( self::get_table_name(), [ 'id' => $del_id ] ); 562 if ( $del_ok === false ) { 563 $wpdb->query( 'ROLLBACK' ); 564 return [ 'action' => 'error', 'reason' => 'delete_failed', 'id' => $del_id ]; 565 } 566 } 567 568 $wpdb->query( 'COMMIT' ); 569 570 // Clear static cache for affected identifier(s). 571 if ( ! empty( $canonical_hash ) ) { 572 Critical_CSS::clear_cache( $canonical_hash ); 573 } 574 Critical_CSS::clear_cache( $post_id ); 575 576 return [ 'action' => 'applied', 'plan' => $plan ]; 577 } 311 578 } -
easy-critical-css/trunk/inc/class-helpers.php
r3395875 r3401089 255 255 } 256 256 257 // Use rest route param to avoid potential redirect conflicts 258 $rest_url = home_url( '/index.php?rest_route=/wp/v2/types' ); 259 $response = wp_remote_get( 260 $rest_url, 261 [ 262 'timeout' => 5, 263 'blocking' => true, 264 ] 265 ); 266 267 $code = wp_remote_retrieve_response_code( $response ); 268 $is_reachable = ! is_wp_error( $response ) && ( 200 === $code ); 257 $is_reachable = API_Service::test_receive_endpoint(); 269 258 270 259 // We need to store both true and false values as a transient, but only if no debug. … … 275 264 return [ 276 265 'is_reachable' => $is_reachable, 277 'url' => rest_url( 'wp/v2/types' ), 278 'is_error' => is_wp_error( $response ), 279 'error' => is_wp_error( $response ) ? $response->get_error_message() : null, 280 'code' => $code, 281 'headers' => wp_remote_retrieve_headers( $response ), 266 'url' => rest_url( 'easy-critical-css/v1/receive-test' ), 267 'checked_via' => 'test_receive_endpoint', 282 268 ]; 283 269 } … … 343 329 return apply_filters( 'easy_cc_excluded_url_params', $excluded_url_params ); 344 330 } 331 332 public static function get_auto_mode_status() { 333 // Only show status if there's an active API key. 334 if ( empty( easy_cc_fs()->has_active_valid_license() ) ) { 335 return [ 336 'active_key' => false, 337 'local_ok' => false, 338 'rest_api_ok' => false, 339 'all_ok' => false, 340 ]; 341 } 342 343 $status = [ 344 'active_key' => true, 345 'local_ok' => ! self::is_local_site( wp_parse_url( site_url(), PHP_URL_HOST ) ), 346 'rest_api_ok' => API_Service::test_receive_endpoint(), 347 ]; 348 349 // Overall ready state requires an active key, non-local install, and REST API reachability. 350 $status['all_ok'] = $status['active_key'] && $status['local_ok'] && $status['rest_api_ok']; 351 352 return $status; 353 } 345 354 } -
easy-critical-css/trunk/inc/class-individual-settings.php
r3284313 r3401089 102 102 ], 103 103 'excluded_css_files' => [ 104 'label' => __( 'Excluded CSS Files', 'easy-critical-css' ), 104 'label' => [ 105 'auto' => __( 'Excluded CSS Files', 'easy-critical-css' ), 106 'manual' => __( 'Always Loaded CSS Files', 'easy-critical-css' ), 107 ], 105 108 'type' => 'textarea', 106 109 'default' => '', 107 110 'desc' => [ 108 111 'auto' => __( 'Enter one CSS file URL or partial match per line. Any matching file will be excluded from Critical CSS generation on this post. Excluded files will always load on the page. Keep blank to use global setting.', 'easy-critical-css' ), 109 'manual' => __( 'Enter one CSS file URL or partial match per line. Excluded files will always load on the page. Keep blank to use global setting.', 'easy-critical-css' ),112 'manual' => __( 'Enter one CSS file URL or partial match per line. These files will always be loaded on the page. Keep blank to use global setting.', 'easy-critical-css' ), 110 113 ], 111 114 'visible' => [ -
easy-critical-css/trunk/inc/class-plugin.php
r3395875 r3401089 10 10 private static $instance = null; 11 11 12 private static $plugin_version = '1.4. 2';12 private static $plugin_version = '1.4.3'; 13 13 14 14 private static $db_version = '2'; -
easy-critical-css/trunk/inc/class-rest-api.php
r3394020 r3401089 196 196 ] 197 197 ); 198 199 register_rest_route( 200 self::$route_namespace, 201 '/receive-test', 202 [ 203 'methods' => 'POST', 204 'callback' => [ __CLASS__, 'handle_receive_test' ], 205 // Open permission_callback because request validation is securely handled at the start of `handle_receive_test` callback through a handshake validation. 206 'permission_callback' => '__return_true', 207 ] 208 ); 209 210 register_rest_route( 211 self::$route_namespace, 212 '/refresh-auto-mode-status', 213 [ 214 'methods' => 'POST', 215 'callback' => [ __CLASS__, 'refresh_auto_mode_status' ], 216 'permission_callback' => function () { 217 return current_user_can( 'manage_options' ); 218 } 219 ] 220 ); 198 221 } 199 222 … … 488 511 ); 489 512 513 // Set requested_time now so later reads don't think it's missing. 514 $requested_time_to_save = $timestamp; 515 490 516 // Store generated CSS. 491 517 $new_data = [ … … 496 522 'size_savings' => $savings, 497 523 'generated_time' => $timestamp, 524 'requested_time' => $requested_time_to_save, 498 525 'handshake' => null, // Clear to prevent brute force attempts 499 526 'processing_status' => 'completed', 500 527 ]; 528 529 // Capture previous row for debugging and upsert. 530 $previous = Database::get_row_by_url_hash( $params['hash'] ); 531 501 532 Database::upsert_row( $new_data ); 533 534 // Clear in-memory/static cache to prevent stale cache from showing an outdated status such as 'expired'. 535 if ( ! empty( $params['hash'] ) ) { 536 Critical_CSS::clear_cache( $params['hash'] ); 537 } 538 539 // Dedupe any additional rows now that we have valid handshake and generated CSS. 540 $resolved_post_id = null; 541 if ( isset( $params['post_id'] ) && is_numeric( $params['post_id'] ) ) { 542 $resolved_post_id = (int) $params['post_id']; 543 } elseif ( ! empty( $previous ) && ! empty( $previous['post_id'] ) ) { 544 $resolved_post_id = (int) $previous['post_id']; 545 } 546 547 if ( empty( $resolved_post_id ) ) { 548 Debug::ecc_log( [ 'step' => 'receive_dedupe_skipped', 'reason' => 'no_post_id', 'hash' => $params['hash'] ] ); 549 } else { 550 global $wpdb; 551 $table = esc_sql( Database::get_table_name() ); 552 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE post_id = %d", $resolved_post_id ) ); 553 554 if ( (int) $count > 1 ) { 555 $dedupe_result = Database::dedupe_post_rows( $resolved_post_id, $handshake, false ); 556 Debug::ecc_log( [ 'step' => 'receive_dedupe_apply', 'result' => $dedupe_result ] ); 557 } else { 558 Debug::ecc_log( [ 'step' => 'receive_dedupe_skipped', 'reason' => 'no_duplicates', 'post_id' => $resolved_post_id ] ); 559 } 560 } 561 562 // Mark REST API reachability as successful so we don't trigger repeated 563 // remote tests immediately after receiving CSS. Cache for 6 hours. 564 set_transient( 'easy_cc_is_rest_api_reachable', '1', 6 * HOUR_IN_SECONDS ); 502 565 503 566 /** … … 736 799 return rest_ensure_response( $response ); 737 800 } 801 802 public static function handle_receive_test( WP_REST_Request $request ) { 803 global $wpdb; 804 805 $nonce = sanitize_text_field( $request->get_param( 'nonce' ) ); 806 807 if ( empty( $nonce ) ) { 808 return new WP_Error( 809 'missing_nonce', 810 __( 'Nonce is required.', 'easy-critical-css' ), 811 [ 'status' => 400 ] 812 ); 813 } 814 815 $test_nonce = get_transient( 'easy_cc_test_nonce_' . substr( $nonce, 0, 8 ) ); 816 if ( ! $test_nonce || $test_nonce !== $nonce ) { 817 return new WP_Error( 818 'invalid_nonce', 819 __( 'Invalid or expired nonce.', 'easy-critical-css' ), 820 [ 'status' => 403 ] 821 ); 822 } 823 824 return rest_ensure_response( [ 'success' => true ] ); 825 } 826 827 public static function refresh_auto_mode_status( WP_REST_Request $request ) { 828 $nonce = $request->get_header( 'X-WP-Nonce' ) ?: $request->get_param( '_wpnonce' ); 829 if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { 830 return new WP_Error( 831 'invalid_nonce', 832 __( 'Invalid security token.', 'easy-critical-css' ), 833 [ 'status' => 403 ] 834 ); 835 } 836 837 if ( ! current_user_can( 'manage_options' ) ) { 838 return new WP_Error( 839 'insufficient_permissions', 840 __( 'You do not have permission to perform this action.', 'easy-critical-css' ), 841 [ 'status' => 403 ] 842 ); 843 } 844 845 // Clear the REST API reachability cache and get fresh status. 846 delete_transient( 'easy_cc_is_rest_api_reachable' ); 847 $status = Helpers::get_auto_mode_status(); 848 849 return rest_ensure_response( $status ); 850 } 738 851 } -
easy-critical-css/trunk/inc/class-settings.php
r3395875 r3401089 43 43 // If Trellis is installed, Critical is paused 44 44 if ( Compatibility_Trellis::is_trellis_critical_activated() ) { 45 return false; 46 } 47 48 // If Perfmatters conflicting CSS settings are active, Critical is paused 49 if ( Compatibility_Perfmatters::has_conflicting_css_settings() ) { 45 50 return false; 46 51 } -
easy-critical-css/trunk/readme.txt
r3395875 r3401089 2 2 3 3 Contributors: criticalcss, sethta 4 Tags: critical css, performance, optimization, speed, lighthouse4 Tags: critical css, unused css, performance, optimization, lighthouse 5 5 Requires at least: 6.2 6 6 Tested up to: 6.8.3 … … 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 11 12 Easily inject Critical CSS and optimized Secondary CSS to improve page speed and performance.12 Easily inject Critical CSS and Secondary CSS (with unused styles removed) to improve site speed and performance. 13 13 14 14 == Description == … … 102 102 == Changelog == 103 103 104 = 1.4.3 = 105 - OPTIMIZATION: Adds better REST API check with more details on Settings page 106 - OPTIMIZATION: Cleans up duplicate database rows that are formed when a post slug is changed 107 - COMPATIBILITY: Fixes compatibility issue with Perfmatters's Critical CSS feature 108 - FIX: Fixes issue where Expired statuses could not be cleared 109 - FIX: Fixes issue with status bar Regenerate button not working when conflicting plugins break REST API url 110 - FIX: Renames Excluded CSS Files to Always Loaded CSS Files when on Manual mode 111 104 112 = 1.4.2 = 105 113 - COMPATIBILITY: Adds EWWW Image Optimizer compatibility for lazy-loaded images -
easy-critical-css/trunk/vendor/composer/installed.php
r3395875 r3401089 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => '7 6edc056ab47ce75b9c22841df314400ca3034b6',6 'reference' => '7cba3efe0d456a72032002b0be1b75841af0dbdc', 7 7 'type' => 'wordpress-plugin', 8 8 'install_path' => __DIR__ . '/../../', … … 23 23 'pretty_version' => 'dev-main', 24 24 'version' => 'dev-main', 25 'reference' => '7 6edc056ab47ce75b9c22841df314400ca3034b6',25 'reference' => '7cba3efe0d456a72032002b0be1b75841af0dbdc', 26 26 'type' => 'wordpress-plugin', 27 27 'install_path' => __DIR__ . '/../../',
Note: See TracChangeset
for help on using the changeset viewer.