Author Archives: admin

Server security headers

Added to .htaccess file on 20i

# The HTTP Strict-Transport-Security response header (often abbreviated as HSTS) lets a website tell browsers that it should only be accessed using HTTPS, instead of using HTTP.
# The 'max-age' is the time in days you want the warning to be cached for, the below (5184000) would set that to a default of 60 days.
# Be aware that if you set this header, the site will only ever load over HTTPS.
Header set Strict-Transport-Security "max-age=5184000; includeSubDomains; preload"

And

# CORS (Cross-Origin Resource Sharing) is the site is trying to use a mechanism that uses special HTTP headers that tells the browser that the site is requesting access to resources from a different origin/domain
# Apache (the webserver software we utilise) supports CORS out of the box but for security reasons requires a .htaccess rule to add CORS to the HTTP header. The following rule can be added to allow CORS.
Header set Access-Control-Allow-Origin "https://devonccg.nhs.uk"

In 20i security headers admin

Content-Security-Policy

This response header allows web site administrators to control resources the user agent is allowed to load for a given page. Ie only these sources are allowed in iframe content.

frame-src https://devonccg.nhs.uk/ https://intranet.devonccg.nhs.uk/ https://api-bridge.azurewebsites.net/ https://www.googletagmanager.com https://*.google.com https://www.youtube.com https://googleads.g.doubleclick.net  https://plus.browsealoud.com/

Preload styles

In functions.php

// set these styles as PRELOAD
function add_rel_preload($html, $handle, $href, $media) {
    if (is_admin()) {return $html;}

	$html = '<!--'.$handle.'-->'.$html;
	if( $handle=='infocus-style' || $handle=='wp-block-library' ){
		$html ='<link rel="preload" href="'.$href.'" as="style" onload="this.onload=null;this.rel=\'stylesheet\'" id="'.$handle.'" media="all"><noscript><link rel="stylesheet" href="'.$href.'"></noscript>';
	}
    return $html;
}
add_filter( 'style_loader_tag', 'add_rel_preload', 10, 4 );

Validate CF7 email address field

/* validate email address on forms */
add_filter( 'wpcf7_validate_email*', 'custom_email_confirmation_validation_filter', 20, 2 );
function custom_email_confirmation_validation_filter( $result, $tag ) {
	if ( 'your-email-confirm' == $tag->name ) {
		$your_email = isset( $_POST['your-email'] ) ? trim( $_POST['your-email'] ) : '';
		$your_email_confirm = isset( $_POST['your-email-confirm'] ) ? trim( $_POST['your-email-confirm'] ) : '';
	  
		if ( $your_email != $your_email_confirm ) {
		  $result->invalidate( $tag, "Email addresses do not match, please check and try again" );
		}
	}
	return $result;
}

Full width Gutenberg editor

// Full width Gutenberg editor
add_action('admin_head', 'editor_full_width_gutenberg');
function editor_full_width_gutenberg() {
  echo '<style>
    .block-editor__container .wp-block {
        max-width: none !important;
	}
	.editor-styles-wrapper {
	    padding: 5% !important;
	} 
    /*code editor*/
    .edit-post-text-editor__body,edit-post-visual-editor {
    	max-width: none !important;	
    	margin-left: 5%;
    	margin-right: 5%;
    }
  </style>';
}
// add styles to gutenberg
function gutenbergtheme_editor_styles() {
    wp_enqueue_style( 'gutenbergtheme-blocks-style', get_template_directory_uri() . '/blocks.css');
}
add_action( 'enqueue_block_editor_assets', 'gutenbergtheme_editor_styles' );

Expires headers

<IfModule mod_expires.c>
  ExpiresActive On

  # Images
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/x-icon "access plus 1 year"

  # Video
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/mpeg "access plus 1 year"

  # CSS, JavaScript
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType text/x-javascript "access 1 month"
  ExpiresByType application/javascript "access plus 1 month"

  # Others
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType application/x-shockwave-flash "access plus 1 month"
</IfModule>

Add woocommerce product category description to main wordpress search

The only way I could find to do this was to search for the terms separately and then insert the results before the main result (only on page 1 though).

add_action( 'loop_start', function(){
  $searchvar = get_search_query();
  $paged = get_query_var('paged'); 
  $debug = 'search: '. $searchvar;
  // only show on seach page and only for first page of results
  if($searchvar>'' && $paged==0){
	$s = $searchvar;
	$results = array();
	$searchterms = explode(' ',$s); // "+" is removed!
	//print_r($searchterms);
	$termcount=0;
	foreach($searchterms as $searchterm){
		$debug .= '

' . $searchterm . '
--------------------------'; $termcount++; $exclude_arr = array(); ${'termsfound'.$termcount} = array($searchterm); // search product category title $debug .= '

category title'; $terms = get_terms(array( 'taxonomy' => 'product_cat', 'name__like' => $searchterm, 'hide_empty' => true, // Optional )); if ( count($terms) > 0 ){ foreach ( $terms as $term ) { $results[] = $term; $exclude_arr[] = $term->term_id; ${'termsfound'.$termcount}[] = $term->term_taxonomy_id; } $debug .= '
excluded=' . implode(',',$exclude_arr); $debug .= '
terms=' . implode(',',${'termsfound'.$termcount}); } // search product category description $debug .= '

category description'; $exclude = implode(",", $exclude_arr); $terms2 = get_terms(array( 'taxonomy' => 'product_cat', 'description__like' => $searchterm, 'hide_empty' => true, // Optional 'exclude' => $exclude, )); if ( count($terms2) > 0 ){ foreach ( $terms2 as $term2 ) { $results[] = $term2; $exclude_arr[] = $term2->term_id; ${'termsfound'.$termcount}[] = $term2->term_taxonomy_id; } $debug .= '
excluded=' . implode(',',$exclude_arr); $debug .= '
terms2=' . implode(',',${'termsfound'.$termcount}); } // search product category acf description2 field $debug .= '

acf description2 field'; $exclude = implode(",", $exclude_arr); $description2terms = get_terms(array( 'taxonomy' => 'product_cat', 'hide_empty' => true, 'exclude' => $exclude, 'meta_query' => array( array( 'key' => 'description_2', 'value' => $searchterm, 'compare' => 'LIKE' ), ), )); if ( count($description2terms) > 0 ){ foreach ( $description2terms as $term3 ) { $results[] = $term3; ${'termsfound'.$termcount}[] = $term3->term_taxonomy_id; } $debug .= '
terms3=' . implode(',',${'termsfound'.$termcount}); } } if ($termcount>1) { // search is AND, therefore only show results which appear in all result sets $commonterms = $termsfound1; for ($i=2; $i<=$termcount; $i++) { $commonterms = array_intersect($commonterms, ${'termsfound'.$i}); } $commonterms = array_values($commonterms); if(count($commonterms) > 0){ $debug .= '

common terms=' . implode(',',$commonterms); $terms3 = get_terms(array( 'taxonomy' => 'product_cat', 'include' => $commonterms, )); foreach ( $terms3 as $term ) { $finalresults[] = $term; } } else { $finalresults = array(); // no common results //echo 'NOTHING!'; } } else { $finalresults = $results; // one one term searched for } //echo '
'; echo $debug; echo '

';
// combine results
if ( count($finalresults) > 0 ){
/* [term_id] [name] [slug] [term_group] [term_taxonomy_id] [taxonomy] [description] [parent] [count] [filter] [term_order] */
foreach ( $finalresults as $result ) {
$thumbnail_id = get_woocommerce_term_meta( $result->term_id, 'thumbnail_id', true );
$imageurl = wp_get_attachment_image_url( $thumbnail_id, 'swiper_news_image_size' );
$imageurlsm = wp_get_attachment_image_url( $thumbnail_id, 'post_image_thumb' );
//$excerpt = esc_html( $result->description );
$excerpt = substr(esc_html( $result->description ),0,strpos(esc_html( $result->description ),' ',250));
echo '

';
}
}
}
});