Overriding widget via functions.php - php

Woocommerce comes with a default product filter widget, located in
/wp-content/plugins/woocommerce/includes/widgets/class-wc-widget-layered-nav-filters.php
I have a lot of products/categories etc and using this looks very messy. I have tried to tidy it up and turn it into an accordion menu which seems to work as can be seen from code at bottom.
I am struggling, however, to add it into my theme.
I have created my own widget using:
<?php>
class Custom_WC_Widget_Layered_Nav extends WC_Widget_Layered_Nav {
Public function widget( $args, $instance ) {
//taken details prom default plugin and inserted here
}
?>
<script>
var acc = document.getElementsByClassName("widget-title");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
}
</script>
I have saved this in:
/wp-content/themes/my-theme/woocommerce/includes/widgets/custom-wc-widget-layered-nav.php
I then added the following code into functions.php:
add_action( 'widgets_init', 'err_override_woocommerce_widgets', 15 );
function err_override_woocommerce_widgets() {
if ( class_exists( 'WC_Widget_Layered_Nav' ) ) {
unregister_widget( 'WC_Widget_Layered_Nav' );
include_once( 'wp-content/themes/my-theme/woocommerce/includes/widgets/custom-wc-widget-layered-nav.php' );
register_widget( 'Custom_WC_Widget_Layered_Nav' );
}
}
The new code in the functions.php seems to give errors:
Fatal error: Class 'Custom_WC_Widget_Layered_Nav' not found in /homepages/8/d692312546/htdocs/Mytheme/wp-includes/class-wp-widget-factory.php on line 106
Is there anything I can change to avoid this error?
as you can see below, the coding seems to work and have desired affect.
var acc = document.getElementsByClassName("widget-title");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
}
h1.widget-title {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 5px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
margin-top:5px;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
h1.widget-title.active, h1.widget-title:hover {
background-color: #ccc;
}
/* Style the accordion panel. Note: hidden by default */
aside>ul {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
h1.widget-title:after {
content: '\02795'; /* Unicode character for "plus" sign (+) */
font-size: 13px;
color: #777;
float: right;
margin-left: 5px;
}
h1.widget-title.active:after {
content: "\2796"; /* Unicode character for "minus" sign (-) */
}
#secondary .widget {
font-size: 0.86em;
}
.widget-area > aside {
background: white;
border: 1px solid #ededed;
border-radius: 6px;
padding: 5px 20px;
margin-bottom: 5px;
width:20em
}
.widget-title {
font-size: 1.43em;
}
<div class="widget-area" id="secondary" role="complementary">
<aside class="widget woocommerce widget_layered_nav" id="Woocommerce-layered-nav-1">
<h1 class="widget-title">Title1</h1>
<ul>
<li class="layered-nav-term">
nav term
<span class="count">(10)</span>
</li>
</ul>
</aside>
<aside class="widget woocommerce widget_layered_nav" id="Woocommerce-layered-nav-2">
<h1 class="widget-title">Title2</h1>
<ul>
<li class="layered-nav-term">
nav term2
<span class="count">(10)</span>
</li>
</ul>
</aside>
</div>

According to this article
you have to change the path according to where you saved this widget:
add_action( 'widgets_init', 'err_override_woocommerce_widgets', 15 );
function err_override_woocommerce_widgets() {
// Ensure our parent class exists to avoid fatal error (thanks Wilgert!)
if ( class_exists( 'WC_Widget_Layered_Nav' ) ) {
unregister_widget( 'WC_Widget_Layered_Nav' );
include_once( 'woocommerce/includes/widgets/custom-wc-widget-layered-nav.php' );
register_widget( 'Custom_WC_Widget_Layered_Nav' );
}
}
Hope it works!
UPDATE:
What comes in mind is first save the script inside your js folder and name it something like custom.js then you have to register that script as follow:
// Register your assets during `wp_enqueue_scripts` hook inside `functions.php`.
function custom_register_scripts() {
// Give the path of the script
wp_register_script('js-custom', 'path/to/js/custom.js',array('jquery'));
}
then you can enqueue your script into where your widget is.
public function widget( $args, $instance ) {
// Enqueue needed assets inside the `widget` function.
wp_enqueue_script('js-custom');
// Output widget contents.
}

Related

Woocommerce How to get Term id from multiple slugs in css

Good Day,
I want to have a custom CSS for certain woo-commerce product category pages, to exclude the sidebar and make the content full width of the page. now I can get the CSS to work on these pages but I have over 450 categories. is there a way to make the term-id dynamic for excluded categories
the script I have so far is as follows
if ( !is_product_category( array( 'cat1','cat3','cat4','cat6') ) ) {
add_action( 'wp_head', function () { ?>
<style>
/*Hide SideBar*/
#primary {
width: 100%!important;
border-right: 0px!important;
}
.term-1 .ast-right-sidebar #secondary,.term-3 .ast-right-sidebar #secondary,.term-4 .ast-right-sidebar #secondary,.term-6 .ast-right-sidebar #secondary{
border-left: 0px!important
}
.term-1.sidebar-main,.term-3 .sidebar-main,.term-4 .sidebar-main,.term-6 .sidebar-main {
display:none!important
}
.term-1 #secondary,.term-3 #secondary,.term-4 #secondary,.term-6 #secondary {
display: none!important;
border-right: 0px solid #eee!important;
}
.term-1 .widget-area .secondary,.term-3 .widget-area .secondary,.term-4 .widget-area .secondary,.term-6 .widget-area .secondary
{
display: none!important;
}
.term-1 .ast-right-sidebar #primary,.term-3 .ast-right-sidebar #primary,.term-4 .ast-right-sidebar #primary,.term-6 .ast-right-sidebar #primary {
border-right: 0px solid #eee!important;
}
</style>
<?php } );
}
I would like to dynamically add the term-id to the CSS so that the CSS is more compact and does not get too long. any suggestions, please
#CBroe thanks for pointing me in the right direction.
The first thing I did is make a code snippet that you can copy and paste into the functions.php page of your child's theme or use plugins like code snippet. so when your theme updates you do not lose any of your code.
the first part that I did was as follows, I added this CSS to the body of the page.
add_filter('body_class', 'add_custom_body_class');
function add_custom_body_class($classes){
if(is_product_category(array( 'cat1','cat3','cat4','cat6'))){
$classes[] = 'your_new_class_here';
}
return $classes;
}
then I create a new snippet where I will add the class into the header now keep in mind that you do not want to have this code on every page so we will also add the is_product_category to the snippet to show code only when we are on that page
if ( !is_product_category( array( 'cat1','cat3','cat4','cat6') ) ) {
add_action( 'wp_head', function () { ?>
<style>
/*Hide SideBar*/
body.your_new_class_here .ast-right-sidebar #secondary { border-left: 0px!important; }
body.your_new_class_here .sidebar-main { display:none!important; }
#primary {
width: 100%!important;
border-right: 0px!important;
}
#secondary {
display: none!important;
border-right: 0px solid #eee!important;
}
</style>
<?php } );
}
What this does is add the CSS classes to the pages that you specified, hope this helped someone else

Mark clicked menu item as active

So I was wondering how to make the menu item that has been clicked, shown as 'active'/'highligted', so the user can see which page they are on?
I've made this menu bar.
/* Add a black background color to the top navigation */
.topnav_menu {
background-color: #333;
overflow: hidden;
display: flex;
align-items: center;
}
/* Style the links inside the navigation bar */
.topnav_menu a {
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
cursor: pointer;
}
/* Change the color of links on hover */
.topnav_menu a:hover,
.engine_dropdown:hover .engine_dropbtn {
background-color: #ebebeb;
color: black;
}
<div class="topnav_menu">
<!-- # will be removed for enable redirect -->
Profile
Test Runs
Dashboard
TRT Tools
</div>
After the item has been clicked it should appear highlighted.
EDIT
So I created a solution, but is that the best way?
Created this function to check which page in on, and then if im on that page, I would set the class to active
function active($currect_page){
$url = basename(getcwd())."/".str_replace(".php", "", basename($_SERVER['PHP_SELF'])); // If filename is just "name" or "name.php"
// Also checks which directory the file is in, to avoid highlighting multiple menu items with same name
// $currect_page == $url :: parentFolder/fileName == parentFolder/fileName (without .php here)
if($currect_page == $url || $currect_page == basename(getcwd())."/".basename($_SERVER['PHP_SELF'])){
echo 'active'; // class name in css
}
}
<div class="topnav_menu">
<div id="imgDiv"></div>
Test Runs
Dashboard
TRT Tools
</div>
active class would look like this
/* Active menu item */
.topnav_menu a.active,
.topnav_menu a:focus {
/* When a-tags inside this class has been clicked */
background-color: #ffffff;
color: black;
}
Based on how you have written the HTML, bellow js will solve the problem.
//JS
let a = document.querySelectorAll('a')
for(let i =0; i< a.length; i++){
a[i].onclick = function(){
a.forEach(function(item){
item.classList.remove("active");
});
this.classList.add("active");
}
}

WooCommerce: Remove thumbnails from gallery but keep slider navigation

I want to remove the thumbnails from the gallery (flexslider) of the product single page.
But I want to keep the arrow for the previous/next images (in case there is more than 1 image).
I found the following code:
add_action( 'woocommerce_product_thumbnails', 'enable_gallery_for_multiple_thumbnails_only', 5 );
function enable_gallery_for_multiple_thumbnails_only() {
global $product;
if( ! is_a($product, 'WC_Product') ) {
$product = wc_get_product( get_the_id() );
}
if( empty( $product->get_gallery_image_ids() ) ) {
remove_action( 'woocommerce_product_thumbnails', 'woocommerce_show_product_thumbnails', 20 );
}
}
Source: https://stackoverflow.com/a/56238267/1788961
The problem is, that the function removes the thumbnail and the arrows.
Is there any way to keep the arrows?
And I know, that I could use display:none or maybe change the template file.
But I'm searching a solution with an own function.
if you want keep only arrow then you just put this code in functions.php:
// for arrow on single product page slide
add_filter( 'woocommerce_single_product_carousel_options', 'sf_update_woo_flexslider_options' );
/**
* Filer WooCommerce Flexslider options - Add Navigation Arrows
*/
function sf_update_woo_flexslider_options( $options ) {
$options['directionNav'] = true;
return $options;
}
And this code put it in your theme style.css file:
/*add for arrow on main image slide*/
ul.flex-direction-nav {
position: absolute;
top: 30%;
z-index: 99999;
width: 100%;
left: 0;
margin: 0;
padding: 0px;
list-style: none;
}
li.flex-nav-prev {float: left;}
li.flex-nav-next {float: right;}
a.flex-next {visibility:hidden;}
a.flex-prev {visibility:hidden;}
a.flex-next::after {
visibility:visible;content: '\f054';
font-family: 'Font Awesome 5 Free';
margin-right: 10px;
font-size: 20px;
font-weight: bold;
}
a.flex-prev::before {
visibility:visible;
content: '\f053';
font-family: 'Font Awesome 5 Free';
margin-left: 10px;
font-size: 20px;
font-weight: bold;
}
ul.flex-direction-nav li a {
color: black;
}
ul.flex-direction-nav li a:hover {
text-decoration: none;
}
.flex-control-nav .flex-control-thumbs{
display: none;
}
add_filter( 'woocommerce_single_product_carousel_options', 'sf_update_woo_flexslider_options' );
/**
* Filer WooCommerce Flexslider options - Add Navigation Arrows
*/
function sf_update_woo_flexslider_options( $options ) {
$options['directionNav'] = true;
$options['controlNav'] = false;
return $options;
}
The given approved answer is close but still resorts to CSS to hide the thumbnails.
You can disable controlNav directly on the same filter you use to show the arrows.

Inserting content inside a web-based accordion device stops it working

I have set up an accordion to present terms and conditions on a website which works until I attempt to pull a page from elsewhere on the server.
My coding so far is as follows:
CSS:
/* Accordion controls & Styles */
/* Style the buttons that are used to open and close the accordion panel */
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.active, .accordion:hover {
background-color: #ccc;
}
.accordion:after {
content: '\002B';
color: #777;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
Accordion:
<button class="accordion">Standard Terms</button>
<div class="panel">
<p>
<?php include(SHARED_PATH . '/legal/inserts/standard_terms.php'); ?>
</p>
</div>
and JavaScript:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
Standard terms page to render:
<?php include(SHARED_PATH . '/public_header.php'); ?>
<ol>
<h3><li>What we provide</li></h3>
</ol>
<?php include(SHARED_PATH . '/public_footer.php');?>
Everything works as it should until I put the <?php include(SHARED_PATH . '/legal/inserts/standard_terms.php'); ?> in if I remove it and place outside of accordion it renders fine and displays the content.
Only inside accordion does it stop everything in its tracks.
It doesn't render the rest of the page, and no errors are thrown.
Any Ideas?
Sorry found the answer, realised my mistake after posting the standard terms page. header and footer have already been called by previous page.

Display a custom button with a dynamic Product ID using a shortcode in Woocommerce

i using Wordpress button plugin called Max Buttons to generate button as i want. But in that button creation into URL where need to point button have only URL where need to point button. This looks like this:
So as you can see, i use URL link to autoupdate coupon code to product and redirect to checkout.. But, that is for static product ID. So my question is how to generate that to each product , to autoget product ID at end of URL? MaxButton plugin generate shortcode where i inserting into place where i want.
Current URL is:
https://testsite/checkout/?apply_coupon=5%off&fill_cart=4004
How to get that fill_cart=PRODUCT_ID to be dynamic?
Updated (for simple and variable products, using jQuery)
You can build a custom shortcode like Max buttons with 3 arguments (attributes):
class (the css class of the button)
coupon (the coupon code that will be added in the url)
text (the button text)
1) The code (The CSS styles are embedded in the 1st function. The jQuery code is in the footer):
add_shortcode('max_btn', 'custom_dynamic_max_button');
function custom_dynamic_max_button( $atts ) {
if( ! is_product() ) return; // exit
global $post;
// Shortcode attributes
$atts = shortcode_atts(
array(
'class' => '',
'coupon' => '',
'text' => '',
),
$atts, 'max_btn');
// Formatting CSS class
$class = ! empty($atts['class']) ? 'max-btn ' . $atts['class'] : 'max-btn';
// Format the coupon code if it's set as an argument
$coupon = ! empty($atts['coupon']) ? 'apply_coupon=' . $atts['coupon'] . '&' : '';
// Format the url with the dynamic Product ID
$link = wc_get_checkout_url() . '?' . $coupon . 'fill_cart=' . $post->ID;
// The button code:
ob_start();
?>
<style>
.max-btn.flash-btn {
position: relative;
text-decoration: none;
display: inline-block;
vertical-align: middle;
border-color: #ef2409;
width: 225px;
height: 43px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-style: solid;
border-width: 2px;
background-color: rgba(239, 36, 9, 1);
-webkit-box-shadow: 0px 0px 2px 0px #333;
-moz-box-shadow: 0px 0px 2px 0px #333;
box-shadow: 0px 0px 2px 0px #333;
color: #c8146e;
}
.max-btn.flash-btn {
animation-name: flash;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
-webkit-animation-name: flash;
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
-moz-animation-name: flash;
-moz-animation-duration: 1s;
-moz-animation-timing-function: linear;
-moz-animation-iteration-count: infinite;
}
.max-btn:hover.flash-btn {
border-color: #505ac7;
background-color: rgba(255, 255, 255, 1);
-webkit-box-shadow: 0px 0px 2px 0px #333;
-moz-box-shadow: 0px 0px 2px 0px #333;
box-shadow: 0px 0px 2px 0px #333;
}
#keyframes flash {
0% { opacity: 1.0; }
50% { opacity: 0.5; }
100% { opacity: 1.0; }
}
#-moz-keyframes flash {
0% { opacity: 1.0; }
50% { opacity: 0.5; }
100% { opacity: 1.0; }
}
.max-btn.flash-btn > .mb-text {
color: #fff;
font-family: Tahoma;
font-size: 20px;
text-align: center;
font-style: normal;
font-weight: bold;
padding-top: 11px;
padding-right: 0px;
padding-bottom: 0px;
padding-left: 0px;
line-height: 1em;
box-sizing: border-box;
display: block;
background-color: unset;
outline: none;
}
.max-btn:hover.flash-btn > .mb-text {
color: #505ac7;
}
.max-btn.disabled,
.max-btn:hover.disabled {
cursor: not-allowed;
background-color: rgba(160, 160, 160, 1) !important;
border-color: rgba(160, 160, 160, 1) !important;
animation-name: unflash !important;
-webkit-animation-name: unflash !important;
-moz-animation-name: unflash !important;
}
.max-btn:hover.flash-btn.disabled > .mb-text {
color: #fff !important;
}
</style>
<a class="<?php echo $class; ?>" href="<?php echo $link; ?>">
<span class="mb-text"><?php echo $atts['text']; ?></span>
</a>
<input type="hidden" class="ccoupon" name="ccoupon" value="<?php echo $atts['coupon']; ?>">
<?php
return ob_get_clean(); // Output
}
add_action('wp_footer','custom_jquery_single_product_script');
function custom_jquery_single_product_script(){
// Only for single product pages
if ( ! is_product() ) return;
// Get an instance of the WC_Product object
$product = wc_get_product(get_the_id());
// Only for variable products
if( ! $product->is_type('variable') ) return;
// Pass the partial link to jQuery
$partial_link = wc_get_checkout_url() . '?';
?>
<script type="text/javascript">
(function($){
// variables initialization
var a = '<?php echo $partial_link; ?>',
b = 'input[name="variation_id"]',
c = 'a.max-btn.flash-btn',
d = '.variations select',
e = 'input.ccoupon';
// Get the partial link (without the product ID)
if( $(e).val() != '' )
a += 'apply_coupon=' + $(e).val() + '&fill_cart=';
else
a += 'fill_cart=';
// Utility function to enable button with the correct variation ID
function enableButton(){
// Set the correct URL with the dynamic variation ID and remove "disable" class
$(c).attr("href", a+$(b).val()).removeClass('disabled');
}
// Utility function to disable button
function disableButton(){
// Remove href attribute and set "disable" class
$(c).removeAttr('href').addClass('disabled');
}
// -- 1. Once DOM is loaded
// Remove href attribute and set "disable" class
disableButton();
// If variation ID exist, we enable the button with the correct variation ID
setTimeout(function(){
if($(b).val() > 0)
enableButton();
}, 800);
// -- 2. On live events
// On product attribute select fields "blur" event
$(d).blur( function(){
// If variation ID exist (all product attributes are selected)
if( $(b).val() > 0 )
enableButton();
// If variation ID doesn't exist (all product attributes are NOT selected)
else
disableButton();
console.log('select: '+$(b).val());
});
})(jQuery);
</script>
<?php
}
Code goes in function.php file of your active child theme (or active theme).
2) Possible shortcode USAGE:
In the product Wordpress post, custom post or page editor:
[max_btn class="flash-btn" coupon="5%off" text="Buy Now Get 5% off"]
In a php file, template or function:
echo do_shortcode('[max_btn class="flash-btn" coupon="5%off" text="Buy Now Get 5% off"]');
Or (inside html):
<?php echo do_shortcode('[max_btn class="flash-btn" coupon="5%off" text="Buy Now Get 5% off"]'); ?>
The generated html code will be something like (and it works for variable products too):
<a class="max-btn flash-btn" href="http://www.example.com/checkout/?apply_coupon=5%off&fill_cart=37">
<span class="mb-text">Buy Now Get 5% off</span>
</a>
<input type="hidden" class="ccoupon" name="ccoupon" value="5%off">
The Url will be auto generated with a dynamic product ID on single product pages. Tested and works.
For variable products:
When all the product attributes are not selected and the variation ID is not set, the button is disabled:
When all the product attributes are selected and the variation ID is set, the button is enabled and blink:

Categories