Woocommerce - Alternative for woocommerce_locate_template - php

I am developing a plugin based on woocommerce and as a part of the I had to override the default template file location of woocommerce. I mean I am looking to have a custom woocommerce template loaded from my plugin.
For doing this I read about woocommerce_locate_template in woocommerce based on this article, but I noticed that the same function has been deprecated as per this link. Now I am wondering what could be an alternative function for this.
My whole intention was to change the default woocommerce template loading location to my plugin folder. Any help in resolving this?
Thanks in advance.

woocommerce_locate_template function is deprecated in favor of wc_locate_template: you can read the code here.
However, if you are looking for the filter, it's still woocommerce_locate_template and takes three arguments:
$template that is the result of the wp core function locate_template
$template_name that is only the filename
$template_path that is the woocommerce path for templates
So you can check if the $template_name is what you want to intercept and change the path if true, like this
function intercept_wc_template($template, $template_name, $template_path) {
if ($template_name == 'that_template.php') {
$template = 'the/path/of/your/plugin/template.php';
}
return $template;
}
add_filter('woocommerce_locate_template', 'intercept_wc_template', 20, 3);
I've not tested it, so sorry for any possible syntax error :)
Hope it helps!
-- Update 1: I forgot a semicolon :P --
-- Update 2: I made a mistake! --

I had to modify the above code in order to get it to correctly match the template file I needed, which in my case was "variable.php".
$template_name needs to be the full woocommerce root path, see below:
See the amended code below:
function intercept_wc_template($template, $template_name, $template_path) {
if ($template_name == 'single-product/add-to-cart/variable.php') {
$template = 'wp-content/themes/theme-name/woocommerce/single-product/add-to-cart/variable.php';
}
return $template;
}
add_filter('woocommerce_locate_template', 'intercept_wc_template', 20, 3);

If someone struggle with this as of 2021, worth mention that the filter 'woocommerce_locate_template' does not filter all templates inside woocommerce folder. Instead, You need to filter 2 other functions:
add_filter('wc_get_template', 'entex_wc_get_template', 20, 5);
add_filter('wc_get_template_part', 'entex_wc_get_template_part', 20, 3);
For example, The root woocommerce template content-single-product.php must be filtered with wc_get_template_part.
This is working for our plugin:
function template_base(){
return untrailingslashit(plugin_dir_path( __FILE__ )) .'/templates/';
}
function entex_wc_get_template($template, $template_name, $args, $template_path, $default_path){
/* custom theme templates has priority */
if(strpos($template, '/themes/') !== FALSE) return $template;
static $cache = array();
if(isset($cache[$template_name])) return $cache[$template_name];
$plugin_template = wc_locate_template($template_name, WC()->template_path(), $this->template_base());
if($plugin_template && file_exists($plugin_template)){
$template = $plugin_template;
$cache[$template_name] = $template;
}
return $template;
}
function entex_wc_get_template_part($template, $slug, $name){
/* custom theme templates has priority */
if(strpos($template, '/themes/') !== FALSE) return $template;
$template_name = '';
if($name){
$template_name = "{$slug}-{$name}.php";
} else if($slug){
$template_name = "{$slug}.php";
}
if(!$template_name) return $template;
static $cache = array();
if(isset($cache[$template_name])) return $cache[$template_name];
$plugin_template = template_base().$template_name;
if($plugin_template && file_exists($plugin_template)){
$template = $plugin_template;
$cache[$template_name] = $template;
}
return $template;
}
This is ripped out of the PHP class and pasted here so hopefully the code is not broken.
We recommend to register the templates for cleaner performance, if you only use a few, and add something like this early in the functions:
if(!in_array($template_name, array(
'archive-product.php',
'content-product.php',
'content-product-cat.php',
'content-single-product.php',
'content-widget-product.php',
'checkout/form-checkout.php',
'checkout/thankyou.php',
'loop/loop-start.php',
'loop/loop-end.php'
))) return $template;

Related

Remove a wordpress add_filter function from plugin

I have a plugin that is creating a custom template via add_filter:
// Create a template view for the new CPT
add_filter('single_template', [$this, 'setTemplateAVATAR'] );
public function setTemplateAVATAR ($template) {
global $post;
if ( $post->post_type == 'AVATAR' ) {
return self::generateCustomTemplate('avatar.php');
}
return $template;
}
I need to disable this somehow via functions file so that that instead of using it's custom template is uses the template defined in my theme. How would I go about disabling this filter from running?
You would take the add_filter() hook and use remove_filter() and try the hook name and function name parameters the same.
remove_filter('single_template', 'setTemplateAVATAR' );

Disable Plugin Per Page - WordPress

I am working on a way to disable a specific plugin on a certain product page. I've cobbled this together from things I found online and the plugins code itself but its not working. Curious to have some fresh eyes have a look and let me know what might be failing. The post id of the product is 2679320. The actions I have set to remove are the ones referenced in the plugin wp_enqueue_scripts. Here is the code I'm trying by loading to snippets:
function remove__construct() {
global $post;
$ids = array(2679320);
if(in_array($post->ID,$ids)):
remove_action(‘wp_enqueue_scripts’,array($this,’enqueue_scripts’));
remove_action(‘plugins_loaded’,array($this,’load_txt_domain’),99);
remove_action(‘wp_footer’,array($this,’get_popup_markup’));
remove_filter( ‘pre_option_woocommerce_cart_redirect_after_add’, array($this,’prevent_cart_redirect’),10,1);
endif;
}
add_action(‘wp_head’, ‘remove__construct’, 1);
Any ideas why this isn't working? What did I miss? Anyone have better way to do this?
You can use Plugin Organizer. It allows you to selectively disable a plugin on a page or a complete post type.
There are 2 ways to disable plugin.
The first way is to create a custom plugin that removes the action that used to initialize your target plugin. The second way is to remove actions and filters which add scripts, styles and makes changes on a page.
Either way you choose, you have to remove actions after they've been added and before they actually worked. That means that for the first way in most cases you have to use plugins_loaded hook which can't be used in your functions.php (the first hook which can be used in functions.php is load_textdomain hook). in case you want to disable the plugin on certain pages you have to somehow get the current post ID, which isn't so easy because global $post variable is not available yet (The earliest hook with $post is wp).
Parameters for your remove_action depend on plugin add_action. The main point here is that all parameters of your remove_action must be the same as add_action parameters. Here are some examples :
add_action('plugins_loaded', 'init_function_name');
remove_action('plugins_loaded', 'init_function_name');
add_action('plugins_loaded', 'init_function_name', 100);
remove_action('plugins_loaded', 'init_function_name', 100);
class Plugin_Classname {
public static function init() {
add_action( 'plugins_loaded', array( __CLASS__, 'on_init' ) );
}
}
remove_action( 'plugins_loaded', array( 'Plugin_Classname', 'on_init' ) );
class Plugin_Classname {
public function __construct(){
add_action('plugins_loaded', array($this, 'init'), 99);
}
public static function get_instance(){
if(self::$instance === null){
self::$instance = new self();
}
return self::$instance;
}
}
remove_action('plugins_loaded', array( Plugin_Classname::get_instance() , 'init'), 99);
Let's begin with the easiest way. Assume that removing scripts and styles is enough. Then you have to find wp_enqueue_scripts hooks in the plugin source. Eq.:
class Xoo_CP_Public{
protected static $instance = null;
public function __construct(){
add_action('plugins_loaded',array($this,'load_txt_domain'),99);
add_action('wp_enqueue_scripts',array($this,'enqueue_scripts'));
add_action('wp_footer',array($this,'get_popup_markup'));
add_filter( 'pre_option_woocommerce_cart_redirect_after_add', array($this,'prevent_cart_redirect'),10,1);
}
public static function get_instance(){
if(self::$instance === null){
self::$instance = new self();
}
return self::$instance;
}
}
As we need global $post variable we gonna use wp hook. Place the code below in functions.php:
function disable_plugin() {
global $post;
$ids = array( 2679320 ); // Disable plugin at page with ID = 2679320
if( in_array( $post->ID ,$ids ) ) {
remove_action('wp_enqueue_scripts',array( Xoo_CP_Public::get_instance(),'enqueue_scripts'));
remove_action('plugins_loaded',array(Xoo_CP_Public::get_instance(),'load_txt_domain'),99);
remove_action('wp_footer',array(Xoo_CP_Public::get_instance(),'get_popup_markup'));
remove_filter( 'pre_option_woocommerce_cart_redirect_after_add', array(Xoo_CP_Public::get_instance(),'prevent_cart_redirect'),10,1);
}
}
add_action( 'wp', 'disable_plugin' );
What if we want to remove an action is used to initialize this plugin? Let's take a look at add_action:
add_action('plugins_loaded','xoo_cp_rock_the_world');
In this case we can't use plugins_loaded hook because add_action is being called without priority parameter. If it's being called with priority parameter we could just create disable-plugin.php file in /wp-content/plugins folder and place this code there:
function disable_plugin() {
remove_action('plugins_loaded', 'xoo_cp_rock_the_world', 100);
}
add_action('plugins_loaded','disable_plugin');
But it's useless in this case without priority parameter. Yet we can cheat! We don't have to use any hooks and call remove_action directly. We should call it after target plugin add_action was called. Plugins are loaded in alphabetical order so if we named our plugin 'zzz-disable-plugin.php` with this lines of code:
/* Plugin Name: zzz-disable-plugin */
remove_action('plugins_loaded', 'xoo_cp_rock_the_world');
The target plugin will be disabled. At all pages though. I haven't find a way to get ID of current page on such an early hook. But we can use URI :
/* Plugin Name: zzz-disable-plugin */
if( 'product/polo' == trim( $_SERVER[ 'REQUEST_URI' ], '/' ) ) {
remove_action('plugins_loaded', 'xoo_cp_rock_the_world');
}

How to use files conditionally in WordPress?

I want to use a file conditionally upon user input. I am using WooCommerce templates in my child theme. Of course, the file (content-single-product.php) is customized in the child theme. However, I also wish to give users an option to use the default WC file if they want to. In short, I want to use that file conditionally which user will choose.
I am using the PHP copy() and unlink() functions. I just want your expert opinion if this is the best solution or if you have any suggestions better than mine. Here are the functions in my child theme.
/*...for copying the file from a child theme folder to the woocommerce folder...*/
function ac_wc_files_to_theme()
{
$theme_dir = get_stylesheet_directory() . '/woocommerce/files/content-single-product.php';
$theme_dir_file = get_stylesheet_directory() . '/woocommerce/content-single-product.php';
if (!copy($theme_dir, $theme_dir_file)) {
echo "failed to copy $theme_dir to $theme_dir_file...\n";
}
}
/*...for removing the file from a the woocommerce folder...*/
function ac_wc_delete_wc_file(){
$fileArray = array(
get_stylesheet_directory() . '/woocommerce/content-single-product.php'
);
foreach ($fileArray as $value) {
if (file_exists($value)) {
unlink($value);
} else {
echo 'file not found';
}
}
}
/*...calling the options from the theme settings area....*/
if (get_option('ac_wc_default_single') == 1) {
add_action('init', 'ac_wc_delete_wc_file');
remove_action('init', 'ac_wc_files_to_theme');
}
else {
add_action('init', 'ac_wc_files_to_theme');
remove_action('init', 'ac_wc_delete_wc_file');
}
The codes are working fine. Just I need your view if this will do. Thanks
Here's my best guess at filtering the template without extensive testing.
// change the default template path to `woocommerce/files`
function ac_wc_override_template_path(){
return 'woocommerce/files/';
}
add_filter( 'woocommerce_template_path', 'ac_wc_override_template_path' );
// now filter the woocommerce template
function ac_wc_override_single_product($template, $template_name, $template_path) {
$alt_template = '';
if ($template_name == 'content-single-product.php') {
$alt_template = locate_template( trailingslashit( $template_path ) . 'content-single-product-1.php' );
}
if( $alt_template && get_option( 'use_my_themes_templates' ) ){
return $alt_template;
} else {
return $template;
}
}
add_filter('woocommerce_locate_template', 'ac_wc_override_single_product', 20, 3);

Override Plugin Function in WordPress

I have a Plugin installed on my WordPress site.
I'd like to override a function within the Plugin. Do I override this in my theme's functions.php and if so, how do I do this?
Here's the original function in my plugin:
/**
* sensei_start_course_form function.
*
* #access public
* #param mixed $course_id
* #return void
*/
function sensei_start_course_form( $course_id ) {
$prerequisite_complete = sensei_check_prerequisite_course( $course_id );
if ( $prerequisite_complete ) {
?><form method="POST" action="<?php echo esc_url( get_permalink() ); ?>">
<input type="hidden" name="<?php echo esc_attr( 'woothemes_sensei_start_course_noonce' ); ?>" id="<?php echo esc_attr( 'woothemes_sensei_start_course_noonce' ); ?>" value="<?php echo esc_attr( wp_create_nonce( 'woothemes_sensei_start_course_noonce' ) ); ?>" />
<span><input name="course_start" type="submit" class="course-start" value="<?php echo apply_filters( 'sensei_start_course_text', __( 'Start taking this Course', 'woothemes-sensei' ) ); ?>"/></span>
</form><?php
} // End If Statement
} // End sensei_start_course_form()
You can't really "override" a function. If a function is defined, you can't redefine or change it. Your best option is to create a copy of the plugin and change the function directly. Of course you will have to repeat this everytime the plugin is updated.
Give the plugin a different name to distinguish them in the plugin listing. Disable the original, enable your copy.
You can do it by using add_filter() function.
See wordpress stackexchange: Override plugin with functions.php
Just add the below code in theme's functions.php file.
add_filter('sensei_start_course_form','MyCustomfilter',$priority = 10, $args = 1);
function MyCustomfilter($course_id) {
// Do your logics here
}
I know this is late but in the event that someone else finds this post. A simpler solution is to make a copy of the function if you can to your themes functions file and rename it so that it doesn't conflict with the original function. Then use your new function in place of the original. That way you can update the plugin files without affecting your changes.
A bit late on this (november 2021) but I still found this answer today, so I'll add a solution I didn't see around:
For some historical reasons, WP still has the ability to add "must use" plugins that runs before all other plugins. So this give us the opportunity to add the function you want to override, so it already exists when the original plugin run.
In your case
add a .php file in the folder wp-content/mu-plugins
let say
wp-content/mu-plugins/custom-override.php
add your function in custom-override.php :
if ( ! function_exists( 'sensei_start_course_form' ) ) {
function sensei_start_course_form( $course_id ) {
//your magic here
//...
}
}
be sure that the original plugin also has this conditional "if function doesn't already exists..."
if ( ! function_exists( 'sensei_start_course_form' ) ) { ...
This did the trick for me ;-)
PD: I'm not an expert, please give me some feedback if this is wrong somehow. Thanks
REF: https://wordpress.org/support/article/must-use-plugins/
I also needed to change some code in a WordPress plugin. So I created a function that can be placed in functions.php in your child-theme. Please test before use! It is probably poor written since I'm no PHP expert. But the concept works for me. I tested it first outside WordPress so some variables like $root should/could be modified.
Situation was that I had to change some values in two different files in the plugin Email posts to subscribers.
I needed to change $home_url = home_url('/'); to $home_url = 'custom-redirect-url'; and 'content="10; to 'content="1; in the files optin.php and unsubscribe.php.
Every time the plugin gets updated it runs a function after the update. This is the code I use:
// Function that replaces the code
function replace_text_plugin_email_posts_to_subscribers($pluginTargetFile, $replaceURL) {
$root = $_SERVER['DOCUMENT_ROOT'];
$replaceThis = array("\$home_url = home_url('/');", "content=\"10;");
$withThis = array($replaceURL, "content=\"1;");
$fname = $root . $pluginTargetFile;
$fhandle = fopen($fname,"r");
$content = fread($fhandle,filesize($fname));
$content = str_replace($replaceThis, $withThis, $content);
$fhandle = fopen($fname,"w");
fwrite($fhandle,$content);
fclose($fhandle);
}
//Function that runs every time that email-posts-to-subscribers is updated
function my_upgrade_function( $upgrader_object, $options ) {
$current_plugin_path_name = 'email-posts-to-subscribers/email-posts-to-subscribers.php';
if ($options['action'] == 'update' && $options['type'] == 'plugin' ) {
foreach($options['plugins'] as $each_plugin) {
if ($each_plugin==$current_plugin_path_name) {
replace_text_plugin_email_posts_to_subscribers("/wp-content/plugins/email-posts-to-subscribers/job/optin.php","\$home_url = 'https://example.com/redirect-optin';");
replace_text_plugin_email_posts_to_subscribers("/wp-content/plugins/email-posts-to-subscribers/job/unsubscribe.php","\$home_url = 'https://example.com/redirect-unsubscribe';");
}
}
}
}
add_action( 'upgrader_process_complete', 'my_upgrade_function',10, 2);
I guess this wil only be of use when you have to adjust some minor things. Rewriting complete code is maybe not working with this code, but I didn't test this.

Remove main editor from wordpress edit page screen

Anyone know of a way to remove the main editor from the page edit screen? And not just with css. I've added a few other meta boxes with the tinymce and they collide with the main one.
I have a class that removes other meta boxes from the edit screen, but I cant get rid of the main editor this way. I've tried to add 'divpostrich' and 'divpost' to the array in the class (but with no luck):
class removeMetas{
public function __construct(){
add_action('do_meta_boxes', array($this, 'removeMetaBoxes'), 10, 3);
}
public function removeMetaBoxes($type, $context, $post){
/**
* usages
* remove_meta_box($id, $page, $context)
* add_meta_box($id, $title, $callback, $page, $context = 'advanced', $priority = 'default')
*/
$boxes = array( 'slugdiv', 'postexcerpt', 'passworddiv', 'categorydiv',
'tagsdiv', 'trackbacksdiv', 'commentstatusdiv', 'commentsdiv',
'authordiv', 'postcustom');
foreach ($boxes as $box){
foreach (array('link', 'post', 'page') as $page){
foreach (array('normal', 'advanced', 'side') as $context){
remove_meta_box($box, $type, $context);
}
}
}
}
}
$removeMetas = new removeMetas();
I have also tried removing the 'divpostrich' with jquery. But cant figure out where to put the js for it to work. When I remove the 'postdivrich' in the browser with firebug - my remaining tinymce fields work perfect.
Any ideas?
There is built in WP support for this so you don't have to fiddle directly with the globals and ensure forwards compatibility if they ever change how features are handled. The WP Core code does pretty much the exact same logic as #user622018 answer however
function remove_editor() {
remove_post_type_support('page', 'editor');
}
add_action('admin_init', 'remove_editor');
What you are looking for is the global $_wp_post_type_features array.
Below is a quick example of how it can be used
function reset_editor()
{
global $_wp_post_type_features;
$post_type="page";
$feature = "editor";
if ( !isset($_wp_post_type_features[$post_type]) )
{
}
elseif ( isset($_wp_post_type_features[$post_type][$feature]) )
unset($_wp_post_type_features[$post_type][$feature]);
}
add_action("init","reset_editor");
Add the following code to your functions.
function remove_editor_init() {
if ( is_admin() ) {
$post_id = 0;
if(isset($_GET['post'])) $post_id = $_GET['post'];
$template_file = get_post_meta($post_id, '_wp_page_template', TRUE);
if ($template_file == 'page-home.php') {
remove_post_type_support('page', 'editor');
}
}
}
add_action( 'init', 'remove_editor_init' );
Couldn't you just disable the TinyMCE editor, leaving the HTML editor, as your meta boxes are colliding with it? :)
To disable the editor you will need to edit your wp-config.php file and add this line to the top:
define('DISALLOW_FILE_EDIT', true);

Categories