WooCommerce Blocks - Add short product description to product grid - php

So I'm using the new WooCommerce Blocks for the first time. On the homepage of my website I've included the "ProductBestSellers" block and the "ProductOnSale" block. Both of these blocks show a grid styled layout for either best selling products or products on sale.
For the design I needed I had to add some wrappers to the html and therefore I cloned the repository from here; WooCommerce Gutenberg Blocks
The added html does work but now I need to include the product short description within these blocks. I've eddit the AbstractProductGrid.php as follows;
AbstractProductGrid.php
/**
* Render a single products.
* Edited: 24/02/2020
*
* Added wrappers to display content with padding borders and other styling
*
* #param int $id Product ID.
* #return string Rendered product output.
*/
public function render_product( $id ) {
$product = wc_get_product( $id );
if ( ! $product ) {
return '';
}
$data = (object) array(
'permalink' => esc_url( $product->get_permalink() ),
'description' => $this->get_description_html( $product ), <--- Add product short description
'image' => $this->get_image_html( $product ),
'title' => $this->get_title_html( $product ),
'rating' => $this->get_rating_html( $product ),
'price' => $this->get_price_html( $product ),
'badge' => $this->get_sale_badge_html( $product ),
'button' => $this->get_button_html( $product ),
);
return apply_filters(
'woocommerce_blocks_product_grid_item_html',
"<li class=\"wc-block-grid__product\">
<div class=\"wc-block-grid__product__wrapper\">
<div class=\"wc-block-grid__product__items\">
<a href=\"{$data->permalink}\" class=\"wc-block-grid__product-link\">
{$data->image}
{$data->title}
</a>
{$data->badge}
{$data->rating}
{$data->description}
<div class=\"wc-block-grid__product__price-wrapper\">
{$data->price}
{$data->button}
</div>
</div>
</div>
</li>",
$data,
$product
);
}
/**
* Get the product short description.
*
* #param \WC_Product $product Product.
* #return string
*/
protected function get_description_html( $product ) {
if ( empty( $this->attributes['contentVisibility']['description'] ) ) {
return '<p class="purple">The short description is empty</p>';
}
return '<div class="wc-block-grid__description">' . $product->get_short_description() ? $product->get_short_description() : wc_trim_string( $product->get_description(), 400 ) . '</div>';
}
The code above returns an empty attribute, how can I include the short description for the new WooCommerce Blocks?

I found this post when looking for an answer to this, I guess you might have solved it already but here is a suggested solution for anyone coming here.
First, It´s not recommended to change the core files of WooCommerce or the Blocks-plugin, since if you update the plugin your changes will be overwritten.
A better way is utilizing that the plugin is exposing a filter for the rendered output called "woocommerce_blocks_product_grid_item_html".
So if you put this in your functions.php you will get the short description after the product title:
/**
* Add short description to WooCommerce product blocks
*/
add_filter('woocommerce_blocks_product_grid_item_html', 'add_short_desc_woocommerce_blocks_product_grid_item_html', 10 , 3);
function add_short_desc_woocommerce_blocks_product_grid_item_html($content, $data, $product) {
$short_description = '<div class="wc-block-grid__product-short-description">' . $product->get_short_description() . '</div>';
// This will inject the short description after the first link (assumed: the default product link).
$after_link_pos = strpos($content, "</a>");
$before = substr($content, 0, $after_link_pos + 4);
$after = substr($content, $after_link_pos + 4);
$content = $before . $short_description . $after;
return $content;
}

This is very interesting, I tried it on my site, and it works - however my first is after the image, which also is a link.
So for me, the code from #Pierre, ended up outputtin:
Image
Description
Title
Anyone have any suggestions on how to make it output
Image
Title
Description
(Even when images are linked to the product page)
?

Related

Parse XML into Wordpress WYSIWYG editor ONLY for Posts

I've run into a little situation that is hopefully possible to solve. My goal is to take an existing XML file from the server, parse it, and then inject it as a list into Wordpress's original WYSIWYG editor so that the site owner has the list readily available when he writes a new post. Right now, I have this code in my wp-admin/edit-form-advanced.php file:
/**
* Fires after the title field.
*
* #since 3.5.0
*
* #param WP_Post $post Post object.
*/
do_action( 'edit_form_after_title', $post );
if ( post_type_supports($post_type, 'editor') ) {
?>
<div id="postdivrich" class="postarea<?php if ( $_wp_editor_expand ) { echo ' wp-editor-expand'; } ?>">
<?php
/** LOAD XML FROM SERVER AND PARSE AS UL INTO EACH NEW WP POST **/
$xml = simplexml_load_file('../my-folder/file.xml');
$product = "<br/><br/><h2 style='text-align:center; color:#003300;'><u>Products Available Now</u></h2><br/><ul style='text-align:center; list-style:none; color:#003300;'>";
foreach( $xml as $value ) {
$product .= "<li>";
$product .= $value->Description .= " $";
$product .= $value->Price .= " / ";
$product .= $value->QtyUnit .= "\n";
$product .= "</li>";
};
?>
<?php wp_editor($product, 'content', array(
'_content_editor_dfw' => $_content_editor_dfw,
'drag_drop_upload' => true,
'tabfocus_elements' => 'content-html,save-post',
'editor_height' => 300,
'tinymce' => array(
'resize' => false,
'wp_autoresize_on' => $_wp_editor_expand,
'add_unload_trigger' => false,
),
) ); ?>
Although it works, this causes a couple issues.
1) It injects the data into every WYSIWYG editor, including pages, which I would like to avoid. The content should only appear in post editors if possible.
2) It causes a pretty serious bug that erases anything but the list whenever that particular admin page is reloaded. I can't save any drafts, or edit posts or pages unless I keep that session open in the browser during the editing process.
Not sure if these issues can be solved, but any and all help is sincerely appreciated!!
You should never modify WP core files. It's advisable that you update or restore the original files.
What you need can be achieved with this little plugin:
<?php
/**
* Plugin Name: Default Post Content
*/
add_action( 'load-post-new.php', 'new_post_so_44123076' );
function new_post_so_44123076() {
# Only load if post type not defined (only occurs for Posts)
if( isset($_GET['post_type']) )
return;
add_filter( 'default_content', 'default_content_so_44123076' );
}
function default_content_so_44123076( $content ) {
# Build your own custom content
$content = "My html content.";
return $content;
}
Make a folder for the plugin, put the code inside a file (custom-content.php) and put the XML on the same folder.
It can be retrieved like this:
$xml = plugins_url( '/file.xml', __FILE__ );

How to change the title on a WordPress page?

Context: WordPress 5.4.5, Yoast 3.7.1
I'm a plugin developer who has access to the client's site. The site has Yoast 3.7.1 installed and I'm wondering if that is significant because no matter what I do I can't change the 404 page's title.
Now on other pages on StackOverflow where similar questions have been posed (here, here and here for example), those answering have asked if the header.php is correctly embedding a call to wp_title(). Here's what's in the current theme's header.php at that point:
<title><?php wp_title( '|', true, 'right' ); ?></title>
Interestingly, in my 404.php page, wp_get_document_title() tells me that the document title is Page not found - XXXX even though the wp_title call above specifies the separator as |. Yoast's rewriting of titles has been disabled so I'm not at all sure where that dash is coming from.
My plugin does a REST call and pulls in content from off-site for inclusion in the page. Part of that content is the text to be used in the title.
On previous client sites, I've been able to do the following:
add_filter('wp_title', 'change_404_title');
function change_404_title($title) {
if (is_404())
{
global $plugin_title;
if (!empty($plugin_title))
{
$title = $plugin_title;
}
}
return $title;
}
However, on this site, that's not working.
I have tried, based on the version of WordPress being used, hooking the pre_get_document_title filter, viz
add_filter('pre_get_document_title', 'change_404_title');
but again to no avail. I am currently reading up on Yoast ...
wp_title deprecated since version 4.4. So we should use the new filter pre_get_document_title. Your code looks fine but I am confused about global $plugin_title. I would rather ask you to Try this first
add_filter('pre_get_document_title', 'change_404_title');
function change_404_title($title) {
if (is_404()) {
return 'My Custom Title';
}
return $title;
}
If it doesn't work then try changing the priority to execute your function lately.
add_filter('pre_get_document_title', 'change_404_title', 50);
How document title is generated has changed since Wordpress v4.4.0. Now wp_get_document_title dictates how title is generated:
/**
* Displays title tag with content.
*
* #ignore
* #since 4.1.0
* #since 4.4.0 Improved title output replaced `wp_title()`.
* #access private
*/
function _wp_render_title_tag() {
if ( ! current_theme_supports( 'title-tag' ) ) {
return;
}
echo '<title>' . wp_get_document_title() . '</title>' . "\n";
}
Here is the code from v5.4.2. These are the filters you can use to manipulate title tag:
function wp_get_document_title() {
/**
* Filters the document title before it is generated.
*
* Passing a non-empty value will short-circuit wp_get_document_title(),
* returning that value instead.
*
* #since 4.4.0
*
* #param string $title The document title. Default empty string.
*/
$title = apply_filters( 'pre_get_document_title', '' );
if ( ! empty( $title ) ) {
return $title;
}
// --- snipped ---
/**
* Filters the separator for the document title.
*
* #since 4.4.0
*
* #param string $sep Document title separator. Default '-'.
*/
$sep = apply_filters( 'document_title_separator', '-' );
/**
* Filters the parts of the document title.
*
* #since 4.4.0
*
* #param array $title {
* The document title parts.
*
* #type string $title Title of the viewed page.
* #type string $page Optional. Page number if paginated.
* #type string $tagline Optional. Site description when on home page.
* #type string $site Optional. Site title when not on home page.
* }
*/
$title = apply_filters( 'document_title_parts', $title );
// --- snipped ---
return $title;
}
So here are two ways you can do it.
First one uses pre_get_document_title filter which short-circuits the title generation and hence more performant if you are not going make changes on current title:
function custom_document_title( $title ) {
return 'Here is the new title';
}
add_filter( 'pre_get_document_title', 'custom_document_title', 10 );
Second way uses document_title_separator and document_title_parts hooks for the title and the title seperator that are executed later in the function, after title is generated using functions like single_term_title or post_type_archive_title depending on the page, and just before the title tags is about to be outputted:
// Custom function should return a string
function custom_seperator( $sep ) {
return '>';
}
add_filter( 'document_title_separator', 'custom_seperator', 10 );
// Custom function should return an array
function custom_html_title( $title ) {
return array(
'title' => 'Custom Title',
'site' => 'Custom Site'
);
}
add_filter( 'document_title_parts', 'custom_html_title', 10 );
Add this to your functions.php
function custom_wp_title($title) {
if ( is_404() ) {
$title = 'Custom 404 Title';
}
return $title;
}
add_filter( 'wp_title', 'custom_wp_title', 10, 2 );
10 - is priority change to overwrite other plugins like SEO

WordPress how to add TinyMCE format row function to Post->ID Excerpt filter

I am using the forced excerpt code to automatically generate excerpt content without using the more tag or the excerpt editor box as follows in functions.php:
function mytheme_excerpt_read_more_link( $output ) {
global $post;
return $output . ' <div class="more-link">
<a href="' . get_permalink( $post->ID ) . '>Read More</a></div>';
}
add_filter( 'the_excerpt', 'mytheme_excerpt_read_more_link' );
However, I need to add the TinyMCE function so that if someone adds italic (for example) in the editor the <em> will be picked up in the excerpt code.
This is what I have to customize the TinyMCE:
add_filter( 'mce_buttons', 'mrw_mce_buttons_1' );
function mrw_mce_buttons_1( $buttons ) {
$buttons = array('styleselect', 'bold', 'italic',
'link', 'unlink', 'bullist', 'numlist', 'indent',
'outdent', 'pastetext', 'removeformat', 'charmap',
'undo', 'redo', 'wp_more','wp_help', );
return $buttons;
}
How do I add the above filter to the Post->ID? I tried this but it didn't work:
function my_theme_add_tinymce_styles() {
global $post;
$post_type = get_post_type( $post->ID );
$tinymce_style = 'mrw_mce_buttons_1';
add_editor_style( $tinymce_style );
}
add_action( 'pre_get_posts', 'my_theme_add_tinymce_styles' );
Thanks
Charles
This puts the same tinyMCE setup that's in the Editor window into the Excerpt window:
<?php
//adjust Excerpt box height
function admin_excerpt() { echo "<style>"; echo "textarea#excerpt {height:300px;}"; echo "</style>"; } add_action('admin_head', 'admin_excerpt');
/*from http://easywebdesigntutorials.com/customizing-the-excerpts-meta-box/
Code to adding the TinyMCE visual editor to an excerpt is from:
https://marcgratch.com/add-tinymce-to-excerpt/
Code 1.
To me this seems like the best code of the 3 to use. */
function lb_editor_remove_meta_box() {
global $post_type;
/**
* Check to see if the global $post_type variable exists
* and then check to see if the current post_type supports
* excerpts. If so, remove the default excerpt meta box
* provided by the WordPress core. If you would like to only
* change the excerpt meta box for certain post types replace
* $post_type with the post_type identifier.
*/
if (isset($post_type) && post_type_supports($post_type, 'excerpt')){
remove_meta_box('postexcerpt', $post_type, 'normal');
}
}
add_action('admin_menu', 'lb_editor_remove_meta_box');
function lb_editor_add_custom_meta_box() {
global $post_type;
/**
* Again, check to see if the global $post_type variable
* exists and then if the current post_type supports excerpts.
* If so, add the new custom excerpt meta box. If you would
* like to only change the excerpt meta box for certain post
* types replace $post_type with the post_type identifier.
*/
if (isset($post_type) && post_type_supports($post_type, 'excerpt')){
add_meta_box('postexcerpt', __('Excerpt'), 'lb_editor_custom_post_excerpt_meta_box', $post_type, 'normal', 'high');
}
}
add_action( 'add_meta_boxes', 'lb_editor_add_custom_meta_box' );
function lb_editor_custom_post_excerpt_meta_box( $post ) {
/**
* Adjust the settings for the new wp_editor. For all
* available settings view the wp_editor reference
* http://codex.wordpress.org/Function_Reference/wp_editor
*/
$settings = array( 'textarea_rows' => '12', 'quicktags' => true, 'tinymce' => true);
/**
* Create the new meta box editor and decode the current
* post_excerpt value so the TinyMCE editor can display
* the content as it is styled.
*/
wp_editor(html_entity_decode(stripcslashes($post->post_excerpt)), 'excerpt', $settings);
// The meta box description - adjust as necessary
// echo '<p><em>Excerpts are optional, hand-crafted, summaries of your content.</em></p>';
}
// add Text (HTML)

How to replace meta title and meta description in wordpress?

Sorry if my question was basic or stupid but please help me to solve this issue. I'm trying to change <title> and <meta name="description" > tags dynamically in wordpress. so this is what I tried in function.php file.
function changeMeta_2(){
global $wpdb;
$cur_url = $_SERVER['REQUEST_URI'];
$basename = pathinfo($cur_url);
$ebasename = $basename['filename'];
if(is_numeric($ebasename)) {
$url = explode('/', $basename['dirname']);
$basename = explode('.', $url[count($url)-2]);
$ebasename = $basename[0];
}
$pageName = $ebasename;
$arraylist_subcat = array("car","bike","boat","xxxx","yyyy","zzz","mmmm");
$arraylist_maincat = array("aus","ind","usa","uae");
$category_id = get_term_by('slug',$pageName, 'category');
$category_parentid = get_term_by('id', $category_id->parent, 'category');
$parent_slug = $category_parentid->slug;
if ( is_page()) {
if ( in_array($pageName,$arraylist_maincat) ) {
$metaTitle = 'Browse '.$pageName.' | Some txt title | mysite.com';
$metaDescription = 'some of custome blablaaaaa text description '.$pageName.' some of custome blablaaaaa text description ';
echo '<title>'.$metaTitle.'</title>';
echo '<meta name="description" content="'.$metaDescription.'"/>';
}
}
}
add_action( 'wp_head', 'changeMeta_2' );
In the above code I'm trying to change the title tag and meta description for term id which are matching with array values (in_array condition).
Everything works fine, but problem is instead of override(replace) <title> tag is appending in head. Its not changing it appending. please someone help me to solve this issue.
How document title is generated has changed since Wordpress v4.4.0. Now wp_get_document_title dictates how title is generated:
/**
* Displays title tag with content.
*
* #ignore
* #since 4.1.0
* #since 4.4.0 Improved title output replaced `wp_title()`.
* #access private
*/
function _wp_render_title_tag() {
if ( ! current_theme_supports( 'title-tag' ) ) {
return;
}
echo '<title>' . wp_get_document_title() . '</title>' . "\n";
}
Here is the code from v5.4.2. Here are the filters you can use to manipulate title tag:
function wp_get_document_title() {
/**
* Filters the document title before it is generated.
*
* Passing a non-empty value will short-circuit wp_get_document_title(),
* returning that value instead.
*
* #since 4.4.0
*
* #param string $title The document title. Default empty string.
*/
$title = apply_filters( 'pre_get_document_title', '' );
if ( ! empty( $title ) ) {
return $title;
}
// --- snipped ---
/**
* Filters the separator for the document title.
*
* #since 4.4.0
*
* #param string $sep Document title separator. Default '-'.
*/
$sep = apply_filters( 'document_title_separator', '-' );
/**
* Filters the parts of the document title.
*
* #since 4.4.0
*
* #param array $title {
* The document title parts.
*
* #type string $title Title of the viewed page.
* #type string $page Optional. Page number if paginated.
* #type string $tagline Optional. Site description when on home page.
* #type string $site Optional. Site title when not on home page.
* }
*/
$title = apply_filters( 'document_title_parts', $title );
// --- snipped ---
return $title;
}
So here are two ways you can do it.
First one uses pre_get_document_title filter which short-circuits the title generation and hence more performant if you are not going make changes on current title:
function custom_document_title( $title ) {
return 'Here is the new title';
}
add_filter( 'pre_get_document_title', 'custom_document_title', 10 );
Second way uses document_title_separator and document_title_parts hooks for the title and the title seperator that are executed later in the function, after title is generated using functions like single_term_title or post_type_archive_title depending on the page and about to be outputted:
// Custom function should return a string
function custom_seperator( $sep ) {
return '>';
}
add_filter( 'document_title_separator', 'custom_seperator', 10 );
// Custom function should return an array
function custom_html_title( $title ) {
return array(
'title' => 'Custom Title',
'site' => 'Custom Site'
);
}
add_filter( 'document_title_parts', 'custom_html_title', 10 );
For anybody coming to this question in the future: This functionality can be accomplished using the Yoast SEO plugin.
However, if you do want to still do this yourself....
In order to modify the title, rather than the wp_head hook, you need to be using the filters that actually allow you to modify the title: wp_title
And you can / should use the wp_head in order to add the meta description (see the docs here: http://codex.wordpress.org/Meta_Tags_in_WordPress)
Also note there's easier ways to get the page title, mentioned below...
For the title, your code would look something like so:
function changeTitle($title, $sep, $seplocation){
global $wpdb;
// NOTE: This is the HARD way to get the page title, and is unreliable...
$cur_url = $_SERVER['REQUEST_URI'];
$basename = pathinfo($cur_url);
$ebasename = $basename['filename'];
if(is_numeric($ebasename)) {
$url = explode('/', $basename['dirname']);
$basename = explode('.', $url[count($url)-2]);
$ebasename = $basename[0];
}
$pageName = $ebasename;
// NOTE: Why not get pagename this way?
global $post;
$pageName = $post->post_title;
// or if you need the slug...
$pageName = $post->post_slug;
$arraylist_subcat = array("car","bike","boat","xxxx","yyyy","zzz","mmmm");
$arraylist_maincat = array("aus","ind","usa","uae");
$category_id = get_term_by('slug',$pageName, 'category');
$category_parentid = get_term_by('id', $category_id->parent, 'category');
$parent_slug = $category_parentid->slug;
if ( is_page()) {
if ( in_array($pageName,$arraylist_maincat) ) {
$title = 'Browse '.$pageName.' | Some txt title | mysite.com';
}
}
return $title;
}
add_action( 'wp_title', 'changeTitle', 10, 3 );

Adding Additional Text To a Product Category Page in Woocommerce

Woocommerce has product category pages. They act just like wordpress regular category pages, but for products rather than posts.
In the product category editor there is a description box where you can add text. The text becomes displayed below the category page title when viewing that product category page.
I am trying to find a way to also add content below the products as well. Basically I want to have a 1,000 word article underneath my products on that product category page for SEO purpose.
However, I cannot figure out how to do this.
To display the text on the category page below the products, you need to add this code to the beginning of the file wp-includes/functions.php, before code "mysql2date ($ format, $ date, $ translate = true) {"
add_action( 'init', 'wpm_product_cat_register_meta' );
/**
* Register details product_cat meta.
*
* Register the details metabox for WooCommerce product categories.
*
*/
function wpm_product_cat_register_meta() {
register_meta( 'term', 'details', 'wpm_sanitize_details' );
}
/**
* Sanitize the details custom meta field.
*
* #param string $details The existing details field.
* #return string The sanitized details field
*/
function wpm_sanitize_details( $details ) {
return wp_kses_post( $details );
}
add_action( 'product_cat_add_form_fields', 'wpm_product_cat_add_details_meta' );
/**
* Add a details metabox to the Add New Product Category page.
*
* For adding a details metabox to the WordPress admin when
* creating new product categories in WooCommerce.
*
*/
function wpm_product_cat_add_details_meta() {
wp_nonce_field( basename( __FILE__ ), 'wpm_product_cat_details_nonce' );
?>
<div class="form-field">
<label for="wpm-product-cat-details"><?php esc_html_e( 'Details', 'wpm' ); ?></label>
<textarea name="wpm-product-cat-details" id="wpm-product-cat-details" rows="5" cols="40"></textarea>
<p class="description"><?php esc_html_e( 'Detailed category info to appear below the product list', 'wpm' ); ?></p>
</div>
<?php
}
add_action( 'product_cat_edit_form_fields', 'wpm_product_cat_edit_details_meta' );
/**
* Add a details metabox to the Edit Product Category page.
*
* For adding a details metabox to the WordPress admin when
* editing an existing product category in WooCommerce.
*
* #param object $term The existing term object.
*/
function wpm_product_cat_edit_details_meta( $term ) {
$product_cat_details = get_term_meta( $term->term_id, 'details', true );
if ( ! $product_cat_details ) {
$product_cat_details = '';
}
$settings = array( 'textarea_name' => 'wpm-product-cat-details' );
?>
<tr class="form-field">
<th scope="row" valign="top"><label for="wpm-product-cat-details"><?php esc_html_e( 'Details', 'wpm' ); ?></label></th>
<td>
<?php wp_nonce_field( basename( __FILE__ ), 'wpm_product_cat_details_nonce' ); ?>
<?php wp_editor( wpm_sanitize_details( $product_cat_details ), 'product_cat_details', $settings ); ?>
<p class="description"><?php esc_html_e( 'Detailed category info to appear below the product list','wpm' ); ?></p>
</td>
</tr>
<?php
}
add_action( 'create_product_cat', 'wpm_product_cat_details_meta_save' );
add_action( 'edit_product_cat', 'wpm_product_cat_details_meta_save' );
/**
* Save Product Category details meta.
*
* Save the product_cat details meta POSTed from the
* edit product_cat page or the add product_cat page.
*
* #param int $term_id The term ID of the term to update.
*/
function wpm_product_cat_details_meta_save( $term_id ) {
if ( ! isset( $_POST['wpm_product_cat_details_nonce'] ) || ! wp_verify_nonce( $_POST['wpm_product_cat_details_nonce'], basename( __FILE__ ) ) ) {
return;
}
$old_details = get_term_meta( $term_id, 'details', true );
$new_details = isset( $_POST['wpm-product-cat-details'] ) ? $_POST['wpm-product-cat-details'] : '';
if ( $old_details && '' === $new_details ) {
delete_term_meta( $term_id, 'details' );
} else if ( $old_details !== $new_details ) {
update_term_meta(
$term_id,
'details',
wpm_sanitize_details( $new_details )
);
}
}
add_action( 'woocommerce_after_shop_loop', 'wpm_product_cat_display_details_meta' );
/**
* Display details meta on Product Category archives.
*
*/
function wpm_product_cat_display_details_meta() {
if ( ! is_tax( 'product_cat' ) ) {
return;
}
$t_id = get_queried_object()->term_id;
$details = get_term_meta( $t_id, 'details', true );
if ( '' !== $details ) {
?>
<div class="product-cat-details">
<?php echo apply_filters( 'the_content', wp_kses_post( $details ) ); ?>
</div>
<?php
}
}
You will get an additional field in the category properties named "Details". Add your article here and you will get the text underneath the products list on category page. This will work for each category.
There are countless ways to achieve this. One way is to override the archive-product.php template, and include the content there.
The general steps are:
Create a /woocommerce/ directory in your theme root
Copy the archive-product.php template from /plugins/woocommerce/templates/ to your newly created folder.
Edit and save the copied file.
Read more about template overrides in the WooCommerce docs.

Categories