WooCommerce Product tabs

Create WooCommerce custom product tabs plugin

I love WooCommerce and use it for most of my clients e-commerce sites and needs from building the simplest to the most advanced and complex online shops.
Its manly for the flexibility it provides and the ease of customization. There are too many extensions to count and they cover almost every feature or functionality you need. And the best part is if you can’t find an extension that is what you need you can always write it yourself.

Product Tabs

The Plan

WooCommerce product tabs are a great way to tell your customers everything and anything there is to know about your product and there are many extensions that allow you to add tabs but none are implemented in the way I was looking for so in this tutorial we are going to create a simple plugin to add custom product tabs to any product we want and as many as we need.

To avoid messing with filters, template files, or modifying your theme we are going to create a simple plugin that will ease the way we create new tabs and add them to our product pages. We are going to use a few action and filter hooks provided by WooCommerce, add our own custom post type for tabs and we are also going to setup an options panel to allow custom tabs to be set as global tabs so they will show up on all product pages automatically.
This tutorial is a bit more advanced then the previous ones but nothing too complex.

Coding the plugin

The whole plugin is available for download at GitHub.
To start coding our plugin we create a new file named Simple_Custom_Product_Tabs.php and we load it up with Standard WordPress Plugin Information header.

<?php
/*
Plugin Name: GWP Custom Product Tabs
Plugin URI:
Description: A plugin to add Custom product tabs for WooCommerce
Version: 1.0
Author: Ohad Raz
Author URI: https://generatewp.com
*/

Then we create a main class for the plugin with the methods we want to implement:

/**
* GWP_Custom_Product_Tabs
*/
class GWP_Custom_Product_Tabs{
	/**
	 * $post_type
	 * holds custo post type name
	 * @var string
	 */
	public $post_type = 'c_p_tab';
	/**
	 * $id
	 * holds settings tab id
	 * @var string
	 */
	public $id = 'gwp_custom_tabs';

	/**
	* __construct
	* class constructor will set the needed filter and action hooks
	*/
	function __construct(){}

	/**
	 * woocommerce_settings_tabs_array
	 * Used to add a WooCommerce settings tab
	 * @param  array $settings_tabs
	 * @return array
	 */
	function woocommerce_settings_tabs_array( $settings_tabs ) {}

	/**
	 * show_settings_tab
	 * Used to display the WooCommerce settings tab content
	 * @return void
	 */
	function show_settings_tab(){}

	/**
	 * update_settings_tab
	 * Used to save the WooCommerce settings tab values
	 * @return void
	 */
	function update_settings_tab(){}

	/**
	 * get_settings
	 * Used to define the WooCommerce settings tab fields
	 * @return void
	 */
	function get_settings(){}

	/**
	 * show_c_p_tab_field
	 * Used to print the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function show_c_p_tab_field($field){}

	/**
	 * save_c_p_tab_field
	 * Used to save the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function save_c_p_tab_field($field){}

	/**
	 * ajax_footer_js
	 * Used to add needed javascript to product edit screen and custom settings tab
	 * @return void
	 */
	function ajax_footer_js(){}

	/**
	 * woocommerce_product_write_panel_tabs
	 * Used to add a product custom tab to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panel_tabs(){}

	/**
	 * woocommerce_product_write_panels
	 * Used to display a product custom tab content (fields) to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panels() {}

	/**
	 * woocommerce_process_product_meta
	 * used to save product custom tabs meta
	 * @param  int $post_id
	 * @return void
	 */
	function woocommerce_process_product_meta( $post_id ) {}

	/**
	 * woocommerce_json_custom_tabs
	 * An AJAX handler to list tabs for tabs field
	 * prints out json of {tab_id: tab_name}
	 * @return void
	 */
	function woocommerce_json_custom_tabs(){}

	/**
	 * woocommerce_product_tabs
	 * Used to add tabs to product view page
	 * @param  array $tabs
	 * @return array
	 */
	function woocommerce_product_tabs($tabs){}

	/**
	 * render_tab
	 * Used to render tabs on product view page
	 * @param  string $key
	 * @param  array  $tab
	 * @return void
	 */
	function render_tab($key,$tab){}

	/**
	 * custom_product_tabs_post_type
	 * Register custom tabs Post Type
	 * @return void
	 */
	function custom_product_tabs_post_type() {}
}//end GWP_Custom_Product_Tabs class.
new GWP_Custom_Product_Tabs();

Upgrade to GenerateWP Premium Enjoy better WordPress generators  Upgrade Now

Next we are going to implement the class methods and we actually start from the end and the easy part which is creating our “custom tabs” custom post type, to do that we use use the handy Custom Post Type Generator and we get this:

// Register Custom Post Type
function custom_product_tabs_post_type() {

	$labels = array(
		'name'                => _x( 'Product Tabs', 'Post Type General Name', 'GWP' ),
		'singular_name'       => _x( 'Product Tab', 'Post Type Singular Name', 'GWP' ),
		'menu_name'           => __( 'product Tabs', 'GWP' ),
		'parent_item_colon'   => __( '', 'GWP' ),
		'all_items'           => __( 'Product Tabs', 'GWP' ),
		'view_item'           => __( '', 'GWP' ),
		'add_new_item'        => __( 'Add Product Tab', 'GWP' ),
		'add_new'             => __( 'Add New', 'GWP' ),
		'edit_item'           => __( 'Edit Product Tab', 'GWP' ),
		'update_item'         => __( 'Update Product Tab', 'GWP' ),
		'search_items'        => __( 'Search Product Tab', 'GWP' ),
		'not_found'           => __( 'Not found', 'GWP' ),
		'not_found_in_trash'  => __( 'Not found in Trash', 'GWP' ),
	);
	$args = array(
		'label'               => __( 'Product Tabs', 'GWP' ),
		'description'         => __( 'Custom Product Tabs', 'GWP' ),
		'labels'              => $labels,
		'supports'            => array( 'title', 'editor', ),
		'hierarchical'        => false,
		'public'              => true,
		'show_ui'             => true,
		'show_in_menu'        => 'edit.php?post_type=product',
		'show_in_nav_menus'   => false,
		'show_in_admin_bar'   => true,
		'menu_position'       => 5,
		'menu_icon'           => 'dashicons-feedback',
		'can_export'          => true,
		'has_archive'         => false,
		'exclude_from_search' => true,
		'publicly_queryable'  => false,
		'capability_type'     => 'post',
	);
	register_post_type( 'c_p_tab', $args );

}

// Hook into the 'init' action
add_action( 'init', 'custom_product_tabs_post_type', 0 );

View snippet Clone snippet Download snippet

We only modify the add_action to hook the method from within the class and drop that line of code in the class constructor so it should look like this:

	/**
	* __construct
	* class constructor will set the needed filter and action hooks
	*/
	function __construct(){
		//register_post_type
		add_action( 'init', array($this,'custom_product_tabs_post_type'), 0 );
	}

And Once we do that we can activate our plugin and we should see a new menu item under products named “product tabs”.
product tabs menu item

So now we can start adding our custom product tabs using the familiar WordPress UI, Title will be used for custom tab link/title and the content editor will be used to the custom tab content.
custom tab edit screen

Like I said we start we the easy part, moving on we need a way to link our custom tabs with a specific single product and a way to make this custom tabs global so they will show up on all products without having to link the custom tabs to each product one at a time. So first we are going to add a settings tab to WooCommerce settings panel which will allow as to select which custom product tabs we want link to all products or as we are going to call it: “Make the custom product tab a global tab”. We start by implementing the woocommerce_settings_tabs_array method of our class which we hook to woocommerce_settings_tabs_array filter hook provided by WooCommerce and we hook the method in our class constructor.

	/**
	 * woocommerce_settings_tabs_array
	 * Used to add a WooCommerce settings tab
	 * @param  array $settings_tabs
	 * @return array
	 */
	function woocommerce_settings_tabs_array( $settings_tabs ) {
		$settings_tabs[$this->id] = __('GWP Custom Tabs','GWP');
		return $settings_tabs;
	}

As you see its a simple method to add our WooCommerce settings tab to the tabs array.

Side note:

You may notice that I name (as much as I can) the methods of the class in the name of the filter or action hook that will be used to execute that method. This helps identify the method’s purpose and since we are coding this plugin using OOP (Object-oriented programming) approach we don’t have to worry about having a function or a method with that same name.

Next we Implement the show_settings_tab and update_settings_tab methods which are used to display the WooCommerce settings tab and save it when we hit the save button:

	/**
	 * show_settings_tab
	 * Used to display the WooCommerce settings tab content
	 * @return void
	 */
	function show_settings_tab(){
		woocommerce_admin_fields($this->get_settings());
	}

	/**
	 * update_settings_tab
	 * Used to save the WooCommerce settings tab values
	 * @return void
	 */
	function update_settings_tab(){
		woocommerce_update_options($this->get_settings());
	}

Two simple methods which both use WooCommerce provided functions woocommerce_admin_fields which accepts an array of settings fields to display and woocommerce_update_options which accepts that same array of settings fields to save or update on submit. And because both of theme accept that same array we create a method get_settings to return that array instead of writing it twice (once in each method) so our get_settings should look like this:

	/**
	 * get_settings
	 * Used to define the WooCommerce settings tab fields
	 * @return void
	 */
	function get_settings(){
		$settings = array(
			'section_title' => array(
				'name'     => __('GWP Custom Tabs','GWP'),
				'type'     => 'title',
				'desc'     => '',
				'id'       => 'wc_'.$this->id.'_section_title'
			),
			'title' => array(
				'name'     => __( 'Global Custom Tabs', 'GWP' ),
				'type'     => $this->post_type,
				'desc'     => __( 'Start typing the Custom Tab name, Used for including custom tabs on all products.', 'GWP' ),
				'desc_tip' => true,
				'default'  => '',
				'id'       => 'wc_'.$this->id.'_globals'
			),
			'section_end' => array(
				'type' => 'sectionend',
				'id'   => 'wc_'.$this->id.'_section_end'
			)
		);
		return apply_filters( 'wc_'.$this->id.'_settings', $settings );
	}

WooCommerce comes ready with these field types:

  • text
  • color
  • image_width
  • select
  • checkbox
  • textarea
  • single_select_page
  • single_select_country
  • multi_select_countries
  • email
  • pasword
  • number
  • multiselect
  • radio
  • Maybe a few others

Other then that you define your own custom type, providing the implementation via the woocommerce_admin_field_{field_type} action hook to display it and woocommerce_update_option_{field_type} action hook to save its value. You can see that we define 3 settings fields in our get_settings first a simple `title` field, last `sectionend` field (which tells WooCommerce its the end of our settings section and in the middle we create a custom type field with the same name of our custom tabs post type (using $this->post_type). So we need to define a method named show_c_p_tab_field which we will hook using the woocommerce_admin_field_c_p_tab action hook to display our custom settings field and another method named save_c_p_tab_field to save the settings which we will hook using woocommerce_update_option_c_p_tab action hook:

	/**
	 * show_c_p_tab_field
	 * Used to print the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function show_c_p_tab_field($field){
		global $woocommerce;
		?><tr valign="top">
			<th scope="row" class="titledesc">
				<label for="<?php echo esc_attr( $field['id'] ); ?>"><?php echo esc_html( $field['title'] ); ?></label>
				<?php echo '<img class="help_tip" data-tip="' . esc_attr( $field['desc'] ) . '" src="' . $woocommerce->plugin_url() . '/assets/images/help.png" height="16" width="16" />'; ?>
			</th>
			<td class="forminp forminp-<?php echo sanitize_title( $field['type'] ) ?>">
				<p class="form-field custom_product_tabs">
					<select id="custom_product_tabs" style="min-width: 350px;" name="<?php echo $field['id'];?>[]" class="ajax_chosen_select_tabs" multiple="multiple" data-placeholder="<?php _e( 'Search for a custom tab…', 'GWP' ); ?>">
						<?php
							$tabs_ids = get_option($field['id']);
							$_ids = ! empty( $tabs_ids ) ? array_map( 'absint',  $tabs_ids ) : null;
							if ( $_ids ) {
								foreach ( $_ids as $id ) {
									$tab_name = get_the_title($id);
									echo '<option value="' . esc_attr( $id ) . '" selected="selected">' . esc_html( $tab_name ) . '</option>';
								}
							}
						?>
					</select>
				</p>
			</td>
		</tr><?php
		add_action('admin_footer',array($this,'ajax_footer_js'));
	}

	/**
	 * save_c_p_tab_field
	 * Used to save the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function save_c_p_tab_field($field){
		if (isset($_POST[$field['id']])){
			$option_value =   $_POST[$field['id']];
			update_option($field['id'],$option_value);
		}else{
			delete_option($field['id']);
		}
	}

In save_c_p_tab_field method we simple save the value if it is posted as an option or delete the option if nothing is posted.

Upgrade to GenerateWP Premium Enjoy better WordPress generators  Upgrade Now

In show_c_p_tab_field we mimic the markup of the “Products Select Field Type” which is used by WooCommerce for selecting linked products, up sale products, cross sale products and probbly in other places as well. We do that to get the same functionality of these fields which is: you start typing the product name or id and an AJAX call is made to search the database for products with that string in the name or with that id (depends on what you are typing).
Products Select Field Type

We change the select tag element class attribute to ajax_chosen_select_tabs because we don’t want to search for products, we want to search for tabs. So next we need to tell WooCommerce (well not as much WooCommerce as ajaxChosen library which is included by WooCommerce) to search for tabs when we type in a name or an id and we do so by hooking another method named ajax_footer_js on line 33, so let implement that:

	/**
	 * ajax_footer_js
	 * Used to add needed javascript to product edit screen and custom settings tab
	 * @return void
	 */
	function ajax_footer_js(){
		?>
		<script type="text/javascript">
		jQuery(document).ready(function($){
			// Ajax Chosen Product Selectors
			jQuery("select.ajax_chosen_select_tabs").ajaxChosen({
				method: 'GET',
				url: ajaxurl,
				dataType: 'json',
				afterTypeDelay: 100,
				data: {
					action  : 'woocommerce_json_custom_tabs',
					security: '<?php echo wp_create_nonce( "search-products-tabs" ); ?>'
				}
			}, function (data) {
				var terms = {};
				$.each(data, function (i, val) {
					terms[i] = val;
				});
				return terms;
			});
		});
		</script>
		<?php
	}

Notce:

A better practice solution would be to include this method as an external JavaScript file using wp_enqueue_script but for this tutorial I wanted to keep it as a snigle file plugin.

We simply define a GET request method, json data type, admin ajax url and data parameters action named woocommerce_json_custom_tabs and security nonce. This tell `ajaxChosen` to make an ajax request to the server to look for tabs, so we need to implement that and we do it in woocommerce_json_custom_tabs method:

	/**
	 * woocommerce_json_custom_tabs
	 * An AJAX handler to list tabs for tabs field
	 * prints out json of {tab_id: tab_name}
	 * @return void
	 */
	function woocommerce_json_custom_tabs(){
		check_ajax_referer( 'search-products-tabs', 'security' );
		header( 'Content-Type: application/json; charset=utf-8' );
		$term = (string) urldecode(stripslashes(strip_tags($_GET['term'])));
		if (empty($term)) die();
		$post_types = array($this->post_type);
		if ( is_numeric( $term ) ) {
			//by tab id
			$args = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'post__in'       => array(0, $term),
				'fields'         => 'ids'
			);

			$args2 = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'post_parent'    => $term,
				'fields'         => 'ids'
			);

			$posts = array_unique(array_merge( get_posts( $args ), get_posts( $args2 )));

		} else {
			//by tab name
			$args = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				's'              => $term,
				'fields'         => 'ids'
			);
			$posts = array_unique( get_posts( $args ) );
		}

		$found_tabs = array();

		if ( $posts ) foreach ( $posts as $post_id ) {

			$found_tabs[ $post_id ] = get_the_title($post_id);
		}

		$found_tabs = apply_filters( 'woocommerce_json_search_found_tabs', $found_tabs );
		echo json_encode( $found_tabs );

		die();
	}

Breakdown – first on line 8 we validate the request to prevent processing requests external of the site using check_ajax_referer. Then we define the response header on line 9. On line 10 we sanitize the search term and check that its not empty on line 11. moving on to lines 13-43 we check if the search term is numeric then we threat it as a tab id and we search for custom product tabs with that id, if its not numeric we search for custom product tabs with that string. On line 45-55 we format the found tabs in array of tab id => tab title printout a json encoded version of that array and die() to end the ajax request.

It seem like a lot but we are all most half way there. We can already navigate to our custom WooCommerce settings panel tab and select and save our global custom product tabs.
custom wocoomerce settings tab

So moving on, we said that we are going to provide an easy way to link a custom product tab to specific single product and to do that we are going to create a new tab in the WooCommerce product data metabox which will have two field which will be tab select field just like the one we used in the custom settings panel tab:

  1. product Tabs select field to include/link specific product tabs to the product
  2. product Tabs select field to exclude/unlink global product tabs from the product

To do that we first tell WooCommerce to add our fields tab to the metabox using the woocommerce_product_write_panel_tabs action hook in the code>woocommerce_product_write_panel_tabs method:

	/**
	 * woocommerce_product_write_panel_tabs
	 * Used to add a product custom tab to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panel_tabs(){
		?>
		<li class="custom_tab">
			<a href="#custom_tab_data_ctabs">
				<?php _e('Custom Tabs', 'GWP'); ?>
			</a>
		</li>
		<?php
	}

Then we render the content of the metabox tab using the woocommerce_product_write_panels action hook in the code>woocommerce_product_write_panels method:

	/**
	 * woocommerce_product_write_panels
	 * Used to display a product custom tab content (fields) to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panels() {
		global $post,$woocommerce;
		$fields = array(
			array(
				'key'   => 'custom_tabs_ids',
				'label' => __( 'Select Custom Tabs', 'GWP' ),
				'desc'  => __( 'Start typing the Custom Tab name, Used for including custom tabs.', 'GWP' )
			),
			array(
				'key'   => 'exclude_custom_tabs_ids',
				'label' => __( 'Select Global Tabs to exclude', 'GWP' ),
				'desc'  => __( 'Start typing the Custom Tab name. used for excluding global tabs.', 'GWP' )
			)
		);
		?>
		<div id="custom_tab_data_ctabs" class="panel woocommerce_options_panel">
			<?php
			foreach ($fields as $f) {
				$tabs_ids = get_post_meta( $post->ID, $f['key'], true );
				$_ids = ! empty( $tabs_ids ) ? array_map( 'absint',  $tabs_ids ) : null;
				?>
				<div class="options_group">
					<p class="form-field custom_product_tabs">
						<label for="custom_product_tabs"><?php echo $f['label']; ?></label>
						<select id="<?php echo $f['key']; ?>" name="<?php echo $f['key']; ?>[]" class="ajax_chosen_select_tabs" multiple="multiple" data-placeholder="<?php _e( 'Search for a custom tab…', 'GWP' ); ?>">
							<?php
								if ( $_ids ) {
									foreach ( $_ids as $id ) {
										$tab_name = get_the_title($id);
										echo '<option value="' . esc_attr( $id ) . '" selected="selected">' . esc_html( $tab_name ) . '</option>';
									}
								}
							?>
						</select> <img class="help_tip" data-tip="<?php echo esc_attr($f['desc']); ?>" src="<?php echo $woocommerce->plugin_url(); ?>/assets/images/help.png" height="16" width="16" />
					</p>
				</div>
				<?php
			}
			?>
		</div>
		<?php
		add_action('admin_footer',array($this,'ajax_footer_js'));
	}

Breakdown On lines 8-19 we define an array of the two fields arguments and on line 23-45 we simply output the fields markup one at a time. Last one line 48 we hook our ajax_footer_js method from before which we will use once (reusable code my 5th love in life 🙂 ).

To finish up the meta box methods we need to implement the method which we will use to save these fields values as product meta.

	/**
	 * woocommerce_process_product_meta
	 * used to save product custom tabs meta
	 * @param  int $post_id
	 * @return void
	 */
	function woocommerce_process_product_meta( $post_id ) {
		foreach (array('exclude_custom_tabs_ids','custom_tabs_ids') as $key) {
			if (isset($_POST[$key]))
				update_post_meta( $post_id, $key, $_POST[$key]);
			else
				delete_post_meta( $post_id, $key);
		}
	}

And we now have a functional working WooCommerce product data metabox custom tab with two fields.
custom tabs tab
Which brings us to the last part of this plugin and that is actually displaying the custom product tabs. We implement the woocommerce_product_tabs method which is hooked to the woocommerce_product_tabs filter hook:

	/**
	 * woocommerce_product_tabs
	 * Used to add tabs to product view page
	 * @param  array $tabs
	 * @return array
	 */
	function woocommerce_product_tabs($tabs){
		global $post;
		//get global tabs
		$global_tabs = get_option('wc_'.$this->id.'_globals');
		$global_tabs_ids = ! empty( $global_tabs ) ? array_map( 'absint',  $global_tabs ) : array();

		//get tabs to exclude from this product
		$exclude_tabs = get_post_meta( $post->ID, 'exclude_custom_tabs_ids', true );
		$exclude_tabs_ids = ! empty($exclude_tabs  ) ? array_map( 'absint',  $exclude_tabs ) : array();

		//get tabs to include with current product
		$product_tabs = get_post_meta( $post->ID, 'custom_tabs_ids', true );
		$_ids = ! empty($product_tabs  ) ? array_map( 'absint',  $product_tabs ) : null;

		//combine global and product specific tabs and remove excluded tabs
		$_ids = array_merge((array)$_ids,(array)array_diff((array)$global_tabs_ids, (array)$exclude_tabs_ids));

		if ($_ids){
			//fix order
			$_ids = array_reverse($_ids);
			//loop over tabs and add them
			foreach ($_ids as $id) {
				$tabs['customtab_'.$id] = array(
					'title'    => get_the_title($id),
					'priority' => 50,
					'callback' => array($this,'render_tab'),
					'content'  => apply_filters('the_content',get_post_field( 'post_content', $id)) //this allows shortcodes in custom tabs
				);
			}
		}
		return $tabs;
	}

Breakdown on lines 10-11 we get the global tabs, on lines 14-15 we get global tabs to exclude from current product, on lines 18-19 we get tabs to include with current product and one line 22 we combine global and product specific tabs to a single array removing excluded tabs. Then on lines 28-35 we loop over the tabs and add them to the tabs array, setting the callback argument as our render_tab method which will implement in a second and last on line 37 we return the modified array of tabs to display.

Upgrade to GenerateWP Premium Enjoy better WordPress generators  Upgrade Now

And after we added the tabs to the tab array we only need to display theme so our render_tab method should look like this:

	/**
	 * render_tab
	 * Used to render tabs on product view page
	 * @param  string $key
	 * @param  array  $tab
	 * @return void
	 */
	function render_tab($key,$tab){
		global $post;
		echo '<h2>'.apply_filters('GWP_custom_tab_title',$tab['title'],$tab,$key).'</h2>';
		echo apply_filters('GWP_custom_tab_content',$tab['content'],$tab,$key);
	}

And making sure our constructor has all the hooks in place:

	/**
	* __construct
	* class constructor will set the needed filter and action hooks
	*/
	function __construct(){
		if (is_admin()){
			//add settings tab
			add_filter( 'woocommerce_settings_tabs_array', array($this,'woocommerce_settings_tabs_array'), 50 );
			//show settings tab
			add_action( 'woocommerce_settings_tabs_'.$this->id, array($this,'show_settings_tab' ));
			//save settings tab
			add_action( 'woocommerce_update_options_'.$this->id, array($this,'update_settings_tab' ));

        		//add tabs select field
			add_action('woocommerce_admin_field_'.$this->post_type,array($this,'show_'.$this->post_type.'_field' ),10);
			//save tabs select field
			add_action( 'woocommerce_update_option_'.$this->post_type,array($this,'save_'.$this->post_type.'_field' ),10);

			//add product tab link in admin
			add_action( 'woocommerce_product_write_panel_tabs', array($this,'woocommerce_product_write_panel_tabs' ));
			//add product tab content in admin
			add_action('woocommerce_product_write_panels', array($this,'woocommerce_product_write_panels'));
			//save product selected tabs
			add_action('woocommerce_process_product_meta', array($this,'woocommerce_process_product_meta'), 10, 2);
		}else{
			//add tabs to product page
			add_filter( 'woocommerce_product_tabs', array($this,'woocommerce_product_tabs') );
		}
		//ajax search handler
		add_action('wp_ajax_woocommerce_json_custom_tabs', array($this,'woocommerce_json_custom_tabs'));
		//register_post_type
		add_action( 'init', array($this,'custom_product_tabs_post_type'), 0 );
	}

And our plugin is ready:
Product view with custom tabs added

The whole plugin file:

<?php
/*
Plugin Name: GWP Custom Product Tabs
Plugin URI:
Description: A plugin to add Custom product tabs for WooCommerce
Version: 1.0
Author: Ohad Raz
Author URI: https://generatewp.com
*/
/**
* GWP_Custom_Product_Tabs
*/
class GWP_Custom_Product_Tabs{
	/**
	 * $post_type
	 * holds custo post type name
	 * @var string
	 */
	public $post_type = 'c_p_tab';
	/**
	 * $id
	 * holds settings tab id
	 * @var string
	 */
	public $id = 'gwp_custom_tabs';

	/**
	* __construct
	* class constructor will set the needed filter and action hooks
	*/
	function __construct(){
		if (is_admin()){
			//add settings tab
			add_filter( 'woocommerce_settings_tabs_array', array($this,'woocommerce_settings_tabs_array'), 50 );
			//show settings tab
			add_action( 'woocommerce_settings_tabs_'.$this->id, array($this,'show_settings_tab' ));
			//save settings tab
			add_action( 'woocommerce_update_options_'.$this->id, array($this,'update_settings_tab' ));

        		//add tabs select field
			add_action('woocommerce_admin_field_'.$this->post_type,array($this,'show_'.$this->post_type.'_field' ),10);
			//save tabs select field
			add_action( 'woocommerce_update_option_'.$this->post_type,array($this,'save_'.$this->post_type.'_field' ),10);

			//add product tab link in admin
			add_action( 'woocommerce_product_write_panel_tabs', array($this,'woocommerce_product_write_panel_tabs' ));
			//add product tab content in admin
			add_action('woocommerce_product_write_panels', array($this,'woocommerce_product_write_panels'));
			//save product selected tabs
			add_action('woocommerce_process_product_meta', array($this,'woocommerce_process_product_meta'), 10, 2);
		}else{
			//add tabs to product page
			add_filter( 'woocommerce_product_tabs', array($this,'woocommerce_product_tabs') );
		}
		//ajax search handler
		add_action('wp_ajax_woocommerce_json_custom_tabs', array($this,'woocommerce_json_custom_tabs'));
		//register_post_type
		add_action( 'init', array($this,'custom_product_tabs_post_type'), 0 );
	}

	/**
	 * woocommerce_settings_tabs_array
	 * Used to add a WooCommerce settings tab
	 * @param  array $settings_tabs
	 * @return array
	 */
	function woocommerce_settings_tabs_array( $settings_tabs ) {
		$settings_tabs[$this->id] = __('GWP Custom Tabs','GWP');
		return $settings_tabs;
	}

	/**
	 * show_settings_tab
	 * Used to display the WooCommerce settings tab content
	 * @return void
	 */
	function show_settings_tab(){
		woocommerce_admin_fields($this->get_settings());
	}

	/**
	 * update_settings_tab
	 * Used to save the WooCommerce settings tab values
	 * @return void
	 */
	function update_settings_tab(){
		woocommerce_update_options($this->get_settings());
	}

	/**
	 * get_settings
	 * Used to define the WooCommerce settings tab fields
	 * @return void
	 */
	function get_settings(){
		$settings = array(
			'section_title' => array(
				'name'     => __('GWP Custom Tabs','GWP'),
				'type'     => 'title',
				'desc'     => '',
				'id'       => 'wc_'.$this->id.'_section_title'
			),
			'title' => array(
				'name'     => __( 'Global Custom Tabs', 'GWP' ),
				'type'     => $this->post_type,
				'desc'     => __( 'Start typing the Custom Tab name, Used for including custom tabs on all products.', 'GWP' ),
				'desc_tip' => true,
				'default'  => '',
				'id'       => 'wc_'.$this->id.'_globals'
			),
			'section_end' => array(
				'type' => 'sectionend',
				'id'   => 'wc_'.$this->id.'_section_end'
			)
		);
		return apply_filters( 'wc_'.$this->id.'_settings', $settings );
	}

	/**
	 * show_c_p_tab_field
	 * Used to print the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function show_c_p_tab_field($field){
		global $woocommerce;
		?><tr valign="top">
			<th scope="row" class="titledesc">
				<label for="<?php echo esc_attr( $field['id'] ); ?>"><?php echo esc_html( $field['title'] ); ?></label>
				<?php echo '<img class="help_tip" data-tip="' . esc_attr( $field['desc'] ) . '" src="' . $woocommerce->plugin_url() . '/assets/images/help.png" height="16" width="16" />'; ?>
			</th>
			<td class="forminp forminp-<?php echo sanitize_title( $field['type'] ) ?>">
				<p class="form-field custom_product_tabs">
					<select id="custom_product_tabs" style="min-width: 350px;" name="<?php echo $field['id'];?>[]" class="ajax_chosen_select_tabs" multiple="multiple" data-placeholder="<?php _e( 'Search for a custom tab…', 'GWP' ); ?>">
						<?php
							$tabs_ids = get_option($field['id']);
							$_ids = ! empty( $tabs_ids ) ? array_map( 'absint',  $tabs_ids ) : null;
							if ( $_ids ) {
								foreach ( $_ids as $id ) {
									$tab_name = get_the_title($id);
									echo '<option value="' . esc_attr( $id ) . '" selected="selected">' . esc_html( $tab_name ) . '</option>';
								}
							}
						?>
					</select>
				</p>
			</td>
		</tr><?php
		add_action('admin_footer',array($this,'ajax_footer_js'));
	}

	/**
	 * save_c_p_tab_field
	 * Used to save the settings field of the custom type c_p_tab
	 * @param  array $field
	 * @return void
	 */
	function save_c_p_tab_field($field){
		if (isset($_POST[$field['id']])){
			$option_value =   $_POST[$field['id']];
			update_option($field['id'],$option_value);
		}else{
			delete_option($field['id']);
		}
	}

	/**
	 * ajax_footer_js
	 * Used to add needed javascript to product edit screen and custom settings tab
	 * @return void
	 */
	function ajax_footer_js(){
		?>
		<script type="text/javascript">
		jQuery(document).ready(function($){
			// Ajax Chosen Product Selectors
			jQuery("select.ajax_chosen_select_tabs").ajaxChosen({
				method: 'GET',
				url: ajaxurl,
				dataType: 'json',
				afterTypeDelay: 100,
				data: {
					action  : 'woocommerce_json_custom_tabs',
					security: '<?php echo wp_create_nonce( "search-products-tabs" ); ?>'
				}
			}, function (data) {
				var terms = {};
				$.each(data, function (i, val) {
					terms[i] = val;
				});
				return terms;
			});
		});
		</script>
		<?php
	}

	/**
	 * woocommerce_product_write_panel_tabs
	 * Used to add a product custom tab to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panel_tabs(){
		?>
		<li class="custom_tab">
			<a href="#custom_tab_data_ctabs">
				<?php _e('Custom Tabs', 'GWP'); ?>
			</a>
		</li>
		<?php
	}

	/**
	 * woocommerce_product_write_panels
	 * Used to display a product custom tab content (fields) to product edit screen
	 * @return void
	 */
	function woocommerce_product_write_panels() {
		global $post,$woocommerce;
		$fields = array(
			array(
				'key'   => 'custom_tabs_ids',
				'label' => __( 'Select Custom Tabs', 'GWP' ),
				'desc'  => __( 'Start typing the Custom Tab name, Used for including custom tabs.', 'GWP' )
			),
			array(
				'key'   => 'exclude_custom_tabs_ids',
				'label' => __( 'Select Global Tabs to exclude', 'GWP' ),
				'desc'  => __( 'Start typing the Custom Tab name. used for excluding global tabs.', 'GWP' )
			)
		);
		?>
		<div id="custom_tab_data_ctabs" class="panel woocommerce_options_panel">
			<?php
			foreach ($fields as $f) {
				$tabs_ids = get_post_meta( $post->ID, $f['key'], true );
				$_ids = ! empty( $tabs_ids ) ? array_map( 'absint',  $tabs_ids ) : null;
				?>
				<div class="options_group">
					<p class="form-field custom_product_tabs">
						<label for="custom_product_tabs"><?php echo $f['label']; ?></label>
						<select id="<?php echo $f['key']; ?>" name="<?php echo $f['key']; ?>[]" class="ajax_chosen_select_tabs" multiple="multiple" data-placeholder="<?php _e( 'Search for a custom tab…', 'GWP' ); ?>">
							<?php
								if ( $_ids ) {
									foreach ( $_ids as $id ) {
										$tab_name = get_the_title($id);
										echo '<option value="' . esc_attr( $id ) . '" selected="selected">' . esc_html( $tab_name ) . '</option>';
									}
								}
							?>
						</select> <img class="help_tip" data-tip="<?php echo esc_attr($f['desc']); ?>" src="<?php echo $woocommerce->plugin_url(); ?>/assets/images/help.png" height="16" width="16" />
					</p>
				</div>
				<?php
			}
			?>
		</div>
		<?php
		add_action('admin_footer',array($this,'ajax_footer_js'));
	}

	/**
	 * woocommerce_process_product_meta
	 * used to save product custom tabs meta
	 * @param  int $post_id
	 * @return void
	 */
	function woocommerce_process_product_meta( $post_id ) {
		foreach (array('exclude_custom_tabs_ids','custom_tabs_ids') as $key) {
			if (isset($_POST[$key]))
				update_post_meta( $post_id, $key, $_POST[$key]);
			else
				delete_post_meta( $post_id, $key);
		}
	}

	/**
	 * woocommerce_json_custom_tabs
	 * An AJAX handler to list tabs for tabs field
	 * prints out json of {tab_id: tab_name}
	 * @return void
	 */
	function woocommerce_json_custom_tabs(){
		check_ajax_referer( 'search-products-tabs', 'security' );
		header( 'Content-Type: application/json; charset=utf-8' );
		$term = (string) urldecode(stripslashes(strip_tags($_GET['term'])));
		if (empty($term)) die();
		$post_types = array($this->post_type);
		if ( is_numeric( $term ) ) {
			//by tab id
			$args = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'post__in'       => array(0, $term),
				'fields'         => 'ids'
			);

			$args2 = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'post_parent'    => $term,
				'fields'         => 'ids'
			);

			$posts = array_unique(array_merge( get_posts( $args ), get_posts( $args2 )));

		} else {
			//by name
			$args = array(
				'post_type'      => $post_types,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				's'              => $term,
				'fields'         => 'ids'
			);
			$posts = array_unique( get_posts( $args ) );
		}

		$found_tabs = array();

		if ( $posts ) foreach ( $posts as $post_id ) {

			$found_tabs[ $post_id ] = get_the_title($post_id);
		}

		$found_tabs = apply_filters( 'woocommerce_json_search_found_tabs', $found_tabs );
		echo json_encode( $found_tabs );

		die();
	}

	/**
	 * woocommerce_product_tabs
	 * Used to add tabs to product view page
	 * @param  array $tabs
	 * @return array
	 */
	function woocommerce_product_tabs($tabs){
		global $post;
		//get global tabs
		$global_tabs = get_option('wc_'.$this->id.'_globals');
		$global_tabs_ids = ! empty( $global_tabs ) ? array_map( 'absint',  $global_tabs ) : array();

		//get tabs to exclude from this product
		$exclude_tabs = get_post_meta( $post->ID, 'exclude_custom_tabs_ids', true );
		$exclude_tabs_ids = ! empty($exclude_tabs  ) ? array_map( 'absint',  $exclude_tabs ) : array();

		//get tabs to include with current product
		$product_tabs = get_post_meta( $post->ID, 'custom_tabs_ids', true );
		$_ids = ! empty($product_tabs  ) ? array_map( 'absint',  $product_tabs ) : null;

		//combine global and product specific tabs and remove excluded tabs
		$_ids = array_merge((array)$_ids,(array)array_diff((array)$global_tabs_ids, (array)$exclude_tabs_ids));

		if ($_ids){
			//fix order
			$_ids = array_reverse($_ids);
			//loop over tabs and add them
			foreach ($_ids as $id) {
				$tabs['customtab_'.$id] = array(
					'title'    => get_the_title($id),
					'priority' => 50,
					'callback' => array($this,'render_tab'),
					'content'  => apply_filters('the_content',get_post_field( 'post_content', $id)) //this allows shortcodes in custom tabs
				);
			}
		}
		return $tabs;
	}

	/**
	 * render_tab
	 * Used to render tabs on product view page
	 * @param  string $key
	 * @param  array  $tab
	 * @return void
	 */
	function render_tab($key,$tab){
		global $post;
		echo '<h2>'.apply_filters('GWP_custom_tab_title',$tab['title'],$tab,$key).'</h2>';
		echo apply_filters('GWP_custom_tab_content',$tab['content'],$tab,$key);
	}

	/**
	 * custom_product_tabs_post_type
	 * Register custom tabs Post Type
	 * @return void
	 */
	function custom_product_tabs_post_type() {
		$labels = array(
			'name'                => _x( 'Product Tabs', 'Post Type General Name', 'GWP' ),
			'singular_name'       => _x( 'Product Tab', 'Post Type Singular Name', 'GWP' ),
			'menu_name'           => __( 'product Tabs', 'GWP' ),
			'parent_item_colon'   => __( '', 'GWP' ),
			'all_items'           => __( 'Product Tabs', 'GWP' ),
			'view_item'           => __( '', 'GWP' ),
			'add_new_item'        => __( 'Add Product Tab', 'GWP' ),
			'add_new'             => __( 'Add New', 'GWP' ),
			'edit_item'           => __( 'Edit Product Tab', 'GWP' ),
			'update_item'         => __( 'Update Product Tab', 'GWP' ),
			'search_items'        => __( 'Search Product Tab', 'GWP' ),
			'not_found'           => __( 'Not found', 'GWP' ),
			'not_found_in_trash'  => __( 'Not found in Trash', 'GWP' ),
		);
		$args = array(
			'label'               => __( 'Product Tabs', 'GWP' ),
			'description'         => __( 'Custom Product Tabs', 'GWP' ),
			'labels'              => $labels,
			'supports'            => array( 'title', 'editor', ),
			'hierarchical'        => false,
			'public'              => true,
			'show_ui'             => true,
			'show_in_menu'        => 'edit.php?post_type=product',
			'show_in_nav_menus'   => false,
			'show_in_admin_bar'   => true,
			'menu_position'       => 5,
			'menu_icon'           => 'dashicons-feedback',
			'can_export'          => true,
			'has_archive'         => false,
			'exclude_from_search' => true,
			'publicly_queryable'  => false,
			'capability_type'     => 'post',
		);
		register_post_type( 'c_p_tab', $args );
	}
}//end GWP_Custom_Product_Tabs class.
new GWP_Custom_Product_Tabs();

Making it better

Well for those of you brave souls who made it this far, don’t worry we are not going to add anything but I do have some ideas on how we can improve this plugin by adding more features like the ability to remove default native WooCommerce tabs globally and on per product bases, reordering the tabs globally and on per product bases. Maybe feature like configure tabs based on product category, product tags ,based on sells count or on product stock count. The possibilities are endless.

Conclusion

Was this the simplest best way to create this kind of plugin with these features? No, I could have simply created a simple custom field in the custom tab post type and set it as global from there, but I did want to cover adding a custom WooCommerce settings tab and field type. I could have used a simple select drop-down field with the names of the tabs but I wanted to cover creating a custom product data meta-box tab with ajaxChosen field and some minor ajax handling. If you look back we covered:

  • Adding WooCommerce Custom product tab.
  • Custom WooCommerce product data meta-box tab.
  • Custom WooCommerce settings tab.
  • Custom WooCommerce Settings field type.
  • ajaxChosen JavaScript trigger and server side ajax handler.
  • And some other gems if you know how to read between the lines 🙂

As always, feedback is welcome.

Update:

The github repo has been updated with a quick bug fix for not showing empty tabs after delete and a new option to change the display name of the tab and priority using custom fields.
Share on facebook
Share on twitter
Share on linkedin
Share on reddit
Share on whatsapp
Share on email
Ohad Raz

Ohad Raz

A WordPress Consultant, a plugins and themes developer,WordPress Development moderator and sometimes a WordPress core contributor.

61 Comments:

  • Mantis

    Really nice plugins..Nice interface to use Global tab features. Checking how shortcodes working.
    thanks

    Reply

  • Mantis

    I used Accordion from Light Shortcodes. It behave improperly.

    Reply

  • Ryan

    I’m looking or a solution for an apparel store. However sizing for infants, mens, women, shoe’s, etc… are all different. I would like the custom tab name that displays to just read Size Chart. How can I accomplish this while being able to create sizing pages for all those listed above and them having a unique identifying name on the product page for me to chose. As it is right now, whatever name I put when I create the product tab is what displays to the customer.

    Reply

    • Ohad Raz

      I see what you mean, To solve it you can add use an extra custom field on the tab custom post type with the name to display (in your case its “Size Chart”) and have the custom tab title be anything else you want.
      Then just modify the woocommerce_product_tabs method to use the custom field as the title instead of the post title if it exists in the foreach loop, something like this:

      foreach ($_ids as $id) {
      	$display_title = get_post_meta($id,'tab_display_title',true);
      	$tabs['customtab_'.$id] = array(
      		'title'    => ( !empty($display_title)? $display_title : get_the_title($id) ),
      		'priority' => 50,
      		'callback' => array($this,'render_tab'),
      		'content'  => apply_filters('the_content',get_post_field( 'post_content', $id)) //this allows shortcodes in custom tabs
      	);
      }
      

      just make sure the custom field is named tab_display_title

      Reply

      • Ohad Raz

        Download the latest version from github and you are set.

        Reply

      • blenki

        Hi – thank you for this help – it has been very useful. However – I have tried doing the adjustment you detail above – and it doesn’t seem to override the original custom tab name. I am trying to achieve the same as Ryan – where by I have a delivery tab – which I just want to say Delivery – but admin can create many delivery tabs for small light items, large heavy ones etc – but the tab displayed to the user needs to just read Delivery – regardless. I have tried creating a custom post type (sorry – not too familiar with this) called tab_display_title and I’ve added your code above – but it doesn’t seem to pick it up. Any help appriciated.

        Reply

  • Andres Valle

    When i delete a custom tab, the tab still appearing in the product page, but empty and without the title.

    Reply

  • blenki

    I apologize – I have just seen your latest post – and downloaded the version from GIT hub – works like a dream… Thank you very much.

    Reply

  • bob

    How do i change the priority of a tab. Can you explain how to use the custom fields and what can be entered.

    Thank you

    Reply

    • Ohad Raz

      To change the title create a custom field on the custom tab post type edit page named tab_display_title and set the value to the desired title,
      to change the priority of the tab create a custom field on the custom tab post type edit page named tab_priority and set the value to the desired priority which defaults to 50, the lower value comes first.

      Reply

      • bob

        Thank you for your quick response.

        Sorry, but one more question. When I try to view the page that I set up for a TAB, I get a 404 but the tab works perfectly on the product pages. Is that normal?

        Reply

  • Irfan

    Thank you for the plugin Ohad Raz.
    Plugin was installed successfully but when I created a new tab and tried to
    enter tab’s name in search box it does not show any option to select just show’s
    “Looking for my_tab”

    Reply

    • Ohad Raz

      I can only guess that you have some kind of php notice or error on the ajax request.

      look at the response using your browsers console and search for the error.

      Reply

  • kumbhanimehulhul Kumbhani

    I am not able to display product specific description, specification. Can you please guide me how i can do this.

    Reply

  • Edgr

    Hi Raz, I wanted to add a Specification tab for different products. Is there a way to have a text box to key in my specification for different products? Really appreciate your help

    Reply

  • Brady

    I added your plugin as quick way to get custom tabs in woocommerce. Works great so far.
    ——
    Unfortunately, I do not see a way to edit the created custom tab per single product entry?
    Are the tab contents static when creating them (global or not)? And what would be best way to accomplish getting dynamic content per tab per single product?
    So basically, I need _custom content_ on each custom tab for each single product.

    For instance I have a tab called “Community Overview” which hold detailed information on a hospital (which is the product). This differs for each product.. so custom tab content needs to change for every single one.

    ——
    2nd question – not as important:
    I reordered my tabs by using functions.php – and used the internal name created “customtab_3073” in this case:

    add_filter( ‘woocommerce_product_tabs’, ‘woo_reorder_tabs’, 98 );
    function woo_reorder_tabs( $tabs ) {

    $tabs[‘description’][‘priority’] = 5; // description 1st
    $tabs[‘customtab_3073’][‘priority’] = 10; // Custom Tab1 2nd (practice_setting) (GWP-custom woo tabs)

    (……removed some code for brevity…..)

    return $tabs;
    }

    Is there a better way of doing this instead of using the internal assigned tab names? Or is this okay way of doing it? Seems like it will mess up easily if your plugin changes something on the way it defines created tabs/etc?

    thanks for your time and work.

    Reply

    • Ohad Raz

      Its simpler then you think,
      Lets say you have a product named item1 then you create a custom tab named “item1 content” and you include it only on the item1 edit screen. If you want the tab title to be something else just create a custom field named tab_display_title and set the value of the title you want.

      Reply

  • Brady

    Never mind priority of tab question – I see your answer above re: custom field. That worked..

    Reply

  • Tommy Mærsk

    Is it possible, to make a tab with featured products, just like related products, already made one with related products, displaying 6 related products, however I cant find the codes for doing a tab with featured products? Anybody got info on that? Thanks

    Reply

  • Brady

    Maybe you misunderstand (or me you?)

    If I add a custom tab and then edit a single product(s) under products, how do I add _unique content_ to each product for the _single_ custom tab I made?

    There is no content pane for the tab(s) I made when I edit it the product..

    For instance, on one of woo built-in tabs called “Description” you can add your description content when you edit that product. This of course differs for each product but remains same “named” tab.

    I see no pane for content on tabs I create under edit product.
    Only the content I added to tab when creating under Products/Product Tabs. And that remains the same no matter the product I add it to.

    Static for all?I do no want to add a million custom tabs for each single product, either. This defeats most of the purpose as the name of tab would remain the same… (like ‘Description’ tab/content).

    Reply

    • Ohad Raz

      There is no pane per each product. But if you want you can create a custom field in the product edit screen and make a shortcode which will pull that custom field in the tab.
      I made something like this for a client, in less then 5 minutes.

      Reply

  • eherman24

    Nice implementation. It would be nice to generate the tabs on the fly , from within the product creation screen.

    Reply

  • jolajoola

    Thanks aloooooooot for the plugin , why dont you upload it on wordpress.org , it is the best plugin ever

    Reply

  • luis

    Great plugin!

    Regarding your solution for dynamic content per product:

    “There is no pane per each product. But you can create a custom field in the product edit screen and make a shortcode which will pull that custom field in the tab.”

    Can you show us how to do it. ?

    Reply

    • Ohad Raz

      I can, but that is outside the scope of this (already long and informative) tutorial 🙂

      Reply

      • mikethecow

        Hi Ohad
        Fantastic work, many thanks for making your plugin available for free.

        PLEASE explain briefly how to generate dynamic content via custom fields / shortcodes.

        IE for each product I want to generate some shortcode driven content on a tab from an ID number pulled from a custom field…

        I know this is outside the scope of this tutorial 😉

        I am able to do this manually as there are only about 30 products but for larger product ranges would be a really useful element to explain for us.

        Thanks again

        Reply

  • Danilo Casati

    If I upload PHP file to /plugins/ directory, I can’t activate the plugin.
    It doesn’t appear in plugin list.

    How can I upload it to WordPress?

    Reply

  • saunakauf24

    It seems to be very basic at the first look, but it does what it should. It also works with shortcodes from other plug-ins. So you can generate a product inquiry form with e.g. Fast Secure Contact Form.

    Thank you for this great plugin!

    Reply

  • Christian Hristov

    After Woo 2.3.3 update this plugin is incompatible and broke some ajax related to products features. Disabling the plugin solve the misbehaving of WC.

    Reply

  • saunakauf24

    Since WooCommerce 2.3.3 it is no longer possible to add custum tabs to products. There is still the tab “Custom Tabs”, but no fields to enter anymore.

    Could you check that, please?

    Reply

  • Stoyan Nakov

    Thank you, I strongly recommend. You saved my time. I wish you Good luck

    Reply

  • Valeriy Maksymiv

    I got error in settings tab of the plugin: Warning: in_array() expects parameter 2 to be array, null given in path\wp-content\plugins\Simple_Custom_Product_Tabs.php on line 139

    Reply

    • QueenEve

      be sure you are copying the right code , when you copy and paste there is extra code copied from the source , be sure your code starts with <?php
      /*
      Plugin Name: GWP Custom Product Tabs
      Plugin URI:
      Description: A plugin to add Custom product tabs for WooCommerce
      Version: 1.2
      Author: Ohad Raz

      and ends with
      }//end GWP_Custom_Product_Tabs class.
      new GWP_Custom_Product_Tabs();

      and remove the codes before and after

      wish this helps you

      Reply

  • Jason Lee Neri

    Do the fields end up in the database serialized? I am trying to find a solution to adding custom tabs and fields to JUST the back end. and i have one, but the fields are put into the database serialized so currently the tab is the meta_key and then the fields are an array for the meta_value. i would like to find a way to make my custom fields be their own meta keys so that importing and exporting will be easier using existing plugins.

    Reply

  • ludovic

    hello,
    i have Warning: in_array() expects parameter 2 to be array, null given in C:\wamp\www\wp-content\plugins\GWP-Custom-Product-Tabs-master\Simple_Custom_Product_Tabs.php on line 139
    ???
    I just copy the code from github

    Reply

  • saunakauf24

    Hello,
    I just updated the plugin with the newest version from GitHub. Now my site is broken. If I try to invoke the login page, for example, the GitHub site of the plugin is displayed.
    This leaves me with many ???
    I have to deactivate the plugin by changing the filename extension via ftp
    Now all my tabs aren’t working anymore 🙁

    I hope you get this fixed very soon!

    Reply

  • Juha

    Hi, This is exactly what I need but for some reason I can’t get it installed. Sorry for beeing a rookie, but I need this! Could somebody please slowly walk with me the installation process, so I will see what is missing. Many thanks in advance.

    Reply

  • saunakauf24

    Last time I downloaded the .php file it was a copy of the whole github page. I don’t know why and how this happened. Now I d’loaded the file once more, and everything works fine again.
    Thanks for your immediate support!

    Reply

  • QueenEve

    I am trying to use the plugin with a newly installed woocommerce , but created tabs donot appear on the product page, is the plugin still working?

    Reply

  • Ravi142

    Hello Ohad,

    Thank you for share wonderful plugin.

    I have add addition code in above plugin structure.

    When i add add_filter in _construct() then given Warning: call_user_func_array() expects parameter 1 to be a valid callback,

    add_filter(‘woocommerce_stock_html’,’woocommerce_stock_html_mod’, 10, 1);

    function woocommerce_stock_html_mod($availability_html){
    global $product;

    // Change Out of Stock Text
    if ( $product->stock_status == ‘outofstock’ ) {
    $availability_html = “Sorry, sold out! Email us for availability”;
    }

    return $availability_html;
    }

    Suggest me.

    Thanks.

    Reply

  • louie171

    great plugin, does exactly what I need (global) tabs for all products

    Reply

  • Jigar Shah

    That’s Amazing… It’s looking extremely powerful WooCommerce Plugin. I like your way of idea. It’s unique and effective. Thanks for that.

    Reply

  • Francisco Quiros

    HI, that’s amazing! But i have a question.. i want to create a plugin to when i create a product with variations like colors, etc, i want to put accessories too, and u can select or don’t select the accessories when u have to buy it. What i have to do to make that? thanks!!

    Reply

  • mechint

    Hi Ohad Raz,
    Thanks for this customiztion. Hats off!!

    I have installed this plugin and custom tab is appearing, but not showing typing window for:
    Select Custom Tabs

    Select Global Tabs to exclude

    as shown after ajax_footer_js code.

    Thanks in advance!

    Reply

  • daniel

    oh no, i accidently found that snipped. Seems really good and lightweight. Only option, to customize content for each product is missing. Content of tabs are static.

    Reply

Leave a Reply

Subscribe to our Blog

Receive email notifications when new blog posts are published.