Skip to content

Commit

Permalink
Do not cache error pages
Browse files Browse the repository at this point in the history
  • Loading branch information
arunshenoy99 committed Jan 9, 2025
1 parent 0aaf7c2 commit 3dd9475
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 55 deletions.
69 changes: 49 additions & 20 deletions components/cacheExclusion/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,75 @@ import {
Button,
Container,
TextareaField,
Checkbox,
} from '@newfold/ui-component-library';

const CacheExclusion = ( { methods, constants } ) => {
const [ isEdited, setIsEdited ] = methods.useState( false );
const [ isError, setIsError ] = methods.useState( false );
const [ isSaved, setIsSaved ] = methods.useState( false );
const [ currentValue, setCurrentValue ] = methods.useState(
methods.NewfoldRuntime.sdk.cacheExclusion

// Separate states for excluded URLs and error page exclusion
const [ excludedUrls, setExcludedUrls ] = methods.useState(
methods.NewfoldRuntime.sdk.performance.excludedUrls || ''
);
const [ cacheExclusion, setCacheExclusion ] = methods.useState(
methods.NewfoldRuntime.sdk.cacheExclusion
const [ doNotCacheErrorPages, setDoNotCacheErrorPages ] = methods.useState(
methods.NewfoldRuntime.sdk.performance.doNotCacheErrorPages || false
);

const apiUrl = methods.NewfoldRuntime.createApiUrl(
'/newfold-performance/v1/cache-exclusion/update'
);

const handleCacheExclusionChange = ( e ) => {
if ( e.target.value !== currentValue ) {
// Handle changes to excluded URLs
const handleExcludedUrlsChange = ( e ) => {
if ( e.target.value !== excludedUrls ) {
setIsEdited( true );
} else {
setIsEdited( false );
}
setCurrentValue( e.target.value );
setExcludedUrls( e.target.value );
};

// Handle checkbox toggle for error page exclusion
const handleDoNotCacheErrorPagesChange = () => {
const newValue = ! doNotCacheErrorPages;
setDoNotCacheErrorPages( newValue );
setIsEdited( true );
};

const handlingSaveButton = () => {
// Save settings to the API
const handleSaveButton = () => {
methods
.apiFetch( {
url: apiUrl,
method: 'POST',
data: { cacheExclusion: currentValue },
data: {
excludedUrls,
doNotCacheErrorPages,
},
} )
.then( () => {
setIsSaved( true );
setCacheExclusion( currentValue );
setIsEdited( false );
} )
.catch( ( error ) => {
setIsError( error.message );
} );
};

// Update notices and store on save/error
methods.useUpdateEffect( () => {
methods.setStore( {
...constants.store,
CacheExclusion: cacheExclusion,
performance: {
excludedUrls,
doNotCacheErrorPages,
},
} );

methods.makeNotice(
'cache-exlusion-notice',
'cache-exclusion-notice',
constants.text.cacheExclusionTitle,
! isError ? constants.text.cacheExclusionSaved : isError,
! isError ? 'success' : 'error',
Expand All @@ -65,19 +84,29 @@ const CacheExclusion = ( { methods, constants } ) => {
description={ constants.text.cacheExclusionDescription }
>
<TextareaField
id="cache-exclusion"
name="cache-exclusion"
onChange={ handleCacheExclusionChange }
value={ currentValue }
id="excluded-urls"
name="excluded-urls"
onChange={ handleExcludedUrlsChange }
value={ excludedUrls }
rows="1"
label={ constants.text.cacheExclusionTitle }
label={ constants.text.excludedUrlsLabel }
/>
<Checkbox
id="do-not-cache-error-pages"
name="do-not-cache-error-pages"
className="nfd-performance-cache-exclusion-checkbox"
onChange={ handleDoNotCacheErrorPagesChange }
value={ doNotCacheErrorPages }
checked={ doNotCacheErrorPages }
label={ constants.text.doNotCacheErrorPagesLabel }
/>

{ isEdited && (
<Button
variant="secondary"
className="save-cache-exclusion-button"
disabled={ isEdited ? false : true }
onClick={ handlingSaveButton }
className="nfd-performance-save-cache-exclusion-button"
disabled={ ! isEdited }
onClick={ handleSaveButton }
>
{ constants.text.cacheExclusionSaveButton }
</Button>
Expand Down
8 changes: 6 additions & 2 deletions components/performance/defaultText.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,18 @@ const defaultText = {
),
cacheExclusionSaved: __( 'Cache Exclusion saved', 'wp-module-performance' ),
cacheExclusionSaveButton: __( 'Save', 'wp-module-performance' ),
// Skip 404
doNotCacheErrorPagesLabel: __(
'Exclude Error Pages from Cache',
'wp-module-performance'
),
// Skip 404
skip404Title: __( 'Skip 404', 'wp-module-performance' ),
skip404Description: __(
'When enabled, static resources like images and fonts will use a default server 404 page and not WordPress 404 pages. Pages and posts will continue using WordPress for 404 pages. This can considerably speed up your website if a static resource like an image or font is missing.',
'wp-module-performance'
),
skip404NoticeTitle: __( 'Skip 404 saved', 'wp-module-performance' ),
skip404Notice: __( 'Skip 404 saved', 'wp-module-performance' ),
skip404Notice: __( 'Skip 404 saved', 'wp-module-performance' ),
// Image Optimization
imageOptimizationSettingsTitle: __(
'Image Optimization',
Expand Down
19 changes: 17 additions & 2 deletions includes/CacheExclusion.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class CacheExclusion {
protected $container;

/**
* Option used to store all pages should be excluded from cache.
* Option used to store cache exclusion settings.
*
* @var string
*/
Expand All @@ -33,6 +33,7 @@ public function __construct( Container $container ) {

add_filter( 'newfold-runtime', array( $this, 'add_to_runtime' ) );
}

/**
* Add values to the runtime object.
*
Expand All @@ -41,6 +42,20 @@ public function __construct( Container $container ) {
* @return array
*/
public function add_to_runtime( $sdk ) {
return array_merge( $sdk, array( 'cacheExclusion' => get_option( self::OPTION_CACHE_EXCLUSION, get_default_cache_exclusions() ) ) );
$cache_exclusion = get_option( self::OPTION_CACHE_EXCLUSION, $this->get_default_cache_exclusion() );

return array_merge( $sdk, $cache_exclusion );
}

/**
* Get default cache exclusion settings.
*
* @return array
*/
private function get_default_cache_exclusion() {
return array(
'excludedUrls' => get_default_cache_exclusions(),
'doNotCacheErrorPages' => false,
);
}
}
50 changes: 35 additions & 15 deletions includes/CacheTypes/Browser.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ public static function shouldEnable( Container $container ) {
* Constructor.
*/
public function __construct() {

new OptionListener( Performance::OPTION_CACHE_LEVEL, array( __CLASS__, 'maybeAddRules' ) );

new OptionListener( CacheExclusion::OPTION_CACHE_EXCLUSION, array( __CLASS__, 'exclusionChange' ) );

add_filter( 'newfold_update_htaccess', array( $this, 'onRewrite' ) );
Expand All @@ -53,7 +51,7 @@ public function onRewrite() {
}

/**
* Manage on exlcusion option change.
* Manage on exclusion option change.
*/
public static function exclusionChange() {
self::maybeAddRules( getCacheLevel() );
Expand Down Expand Up @@ -83,7 +81,6 @@ public static function removeRules() {
* @return bool
*/
public static function addRules( $cacheLevel ) {

$fileTypeExpirations = self::getFileTypeExpirations( $cacheLevel );

$tab = "\t";
Expand All @@ -100,22 +97,45 @@ public static function addRules( $cacheLevel ) {
}
$rules[] = '</IfModule>';

$cache_exclusion = get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, '' );
if ( is_string( $cache_exclusion ) && '' !== $cache_exclusion ) {
$cache_exclusion_parameters = array_map( 'trim', explode( ',', sanitize_text_field( get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, '' ) ) ) );
$cache_exclusion = get_option(
CacheExclusion::OPTION_CACHE_EXCLUSION,
array(
'excludedUrls' => '',
'doNotCacheErrorPages' => false,
)
);

if ( ! empty( $cache_exclusion['excludedUrls'] ) ) {
$cache_exclusion_parameters = array_map( 'trim', explode( ',', sanitize_text_field( $cache_exclusion['excludedUrls'] ) ) );
$cache_exclusion_parameters = implode( '|', $cache_exclusion_parameters );

// Add the cache exclusion rules.
$rules[] = '<IfModule mod_rewrite.c>';
$rules[] = 'RewriteEngine On';
$rules[] = "RewriteCond %{REQUEST_URI} ^/({$cache_exclusion_parameters}) [NC]";
$rules[] = '<IfModule mod_headers.c>';
$rules[] = 'Header set Cache-Control "no-cache, no-store, must-revalidate"';
$rules[] = 'Header set Pragma "no-cache"';
$rules[] = 'Header set Expires 0';
$rules[] = "{$tab}RewriteEngine On";
$rules[] = "{$tab}RewriteCond %{REQUEST_URI} ^/({$cache_exclusion_parameters}) [NC]";
$rules[] = "{$tab}<IfModule mod_headers.c>";
$rules[] = "{$tab}{$tab}Header set Cache-Control \"no-cache, no-store, must-revalidate\"";
$rules[] = "{$tab}{$tab}Header set Pragma \"no-cache\"";
$rules[] = "{$tab}{$tab}Header set Expires 0";
$rules[] = "{$tab}</IfModule>";
$rules[] = '</IfModule>';
$rules[] = '</IfModule>';
// Add the end of the rules about cache exclusion.

}

// Handle "Do Not Cache Error Pages"
if ( ! empty( $cache_exclusion['doNotCacheErrorPages'] ) ) {
$expr_condition = '"expr=%{REQUEST_STATUS} >= 400 && %{REQUEST_STATUS} < 600"';
$rules = array_merge(
$rules,
array(
"{$tab}# Set Cache-Control headers for 400 and 500 status codes",
'<IfModule mod_headers.c>',
"{$tab}Header always set Cache-Control \"no-store, no-cache, must-revalidate\" {$expr_condition}",
"{$tab}Header always set Pragma \"no-cache\" {$expr_condition}",
"{$tab}Header always set Expires 0 {$expr_condition}",
'</IfModule>',
)
);
}

$htaccess = new htaccess( self::MARKER );
Expand Down
58 changes: 55 additions & 3 deletions includes/RestApi/CacheExclusionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function register_routes() {
),
)
);

\register_rest_route(
$this->namespace,
$this->rest_base . '/update',
Expand All @@ -50,6 +51,32 @@ public function register_routes() {
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'update_settings' ),
'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
'args' => array(
'excludedUrls' => array(
'type' => 'string',
'description' => 'Comma-separated string of URLs to exclude from caching.',
'required' => true, // Now marked as required
'sanitize_callback' => function ( $value ) {
// Sanitize each URL in the comma-separated string
return implode(
',',
array_map( 'sanitize_text_field', explode( ',', $value ) )
);
},
),

'doNotCacheErrorPages' => array(
'type' => 'boolean',
'description' => 'Whether to prevent caching of error pages (400 and 500).',
'required' => true,
'sanitize_callback' => 'rest_sanitize_boolean',
'validate_callback' => function ( $param ) {
// Ensure the value is a valid boolean or equivalent
return is_bool( $param ) || in_array( $param, array( 'true', 'false', true, false ), true );
},
),

),
),
)
);
Expand All @@ -61,23 +88,48 @@ public function register_routes() {
* @return \WP_REST_Response
*/
public function get_settings() {
// Retrieve the cache exclusion option once
$cache_exclusion = get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, get_default_cache_exclusions() );

// Extract the specific keys
$excluded_urls = $cache_exclusion['excludedUrls'] ?? '';
$do_not_cache_error_pages = $cache_exclusion['doNotCacheErrorPages'] ?? false;

return new \WP_REST_Response(
array(
'cacheExclusion' => get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, get_default_cache_exclusions() ),
'excludedUrls' => $excluded_urls,
'doNotCacheErrorPages' => $do_not_cache_error_pages,
),
200
);
}


/**
* Update the settings
*
* @param \WP_REST_Request $request the request.
* @return \WP_REST_Response
*/
public function update_settings( \WP_REST_Request $request ) {
$cache_exclusion = $request->get_param( 'cacheExclusion' );
if ( update_option( CacheExclusion::OPTION_CACHE_EXCLUSION, $cache_exclusion ) ) {

// Retrieve current settings and merge with defaults
$current_cache_exclusion = get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, array() );

// Extract and sanitize the new values from the request
$excluded_urls = $request->get_param( 'excludedUrls' );
$do_not_cache_error_pages = $request->get_param( 'doNotCacheErrorPages' );

// Merge the updated values into the current settings
$updated_cache_exclusion = array_merge(
$current_cache_exclusion,
array(
'excludedUrls' => $excluded_urls,
'doNotCacheErrorPages' => $do_not_cache_error_pages,
)
);

if ( update_option( CacheExclusion::OPTION_CACHE_EXCLUSION, $updated_cache_exclusion ) ) {
return new \WP_REST_Response(
array(
'result' => true,
Expand Down
Loading

0 comments on commit 3dd9475

Please sign in to comment.