Add blocks to custom location on shop page? - wordpress

On my woocommerce shop page I want to remove the description/blocks from the all-in-one woocommerce product grid block. Then add them back above it separately for layout reasons.
So I've removed the description like this:
remove_action( 'woocommerce_archive_description', 'woocommerce_product_archive_description', 10 );
Then I thought I could just add it back in with a shortcode block in the archive-product.html template.
add_shortcode('shop_description', 'woocommerce_product_archive_description');
The problem is, this adds it to the very top of document even before the <html> tag, instead of where I put the shortcode block...

I realised the woocommerce_product_archive_description function uses echo and to make it work with a shortcode it needs to instead use return.
So I made a new function for the shortcode and boiled it down to be:
$shop_page = get_post( wc_get_page_id( 'shop' ) );
$allowed_html = wp_kses_allowed_html( 'post' );
$description = wc_format_content( wp_kses( $shop_page->post_content, $allowed_html ) );
if ( $description ) {
return $description;
}

Related

Output certain product page on homepage / WooCommerce shortcode not working properly

I need to output a certain product page on the homepage. add_rewrite_rule doesn't work for homepage for any reason (
there are actually no rewrite rules for the homepage in the database, WordPress seems to use some other functions to
query the homepage):
//works fine
add_rewrite_rule( 'certainproductpage/?$',
'index.php?post_type=product&name=certainproduct',
'top'
);
//does not work
add_rewrite_rule( '', //tried everything like "/", "/?$" etc
'index.php?post_type=product&name=certainproduct',
'top'
);
After spending way too much time looking through wp / wc core code and stackoverflow I came across an alternative. I can
simply add a shortcode in the content of the page I need to be the homepage and a product page at the same
time: [product_page id=815]. Indeed it works great, but only if the shortcode is added in the admin editor or is
stored in the database (post_content). If I try to call the shortcode manually on the page template (
page-certainproductpage.php) then it outputs the product page without some necessary stuff (PayPal, PhotoSwipe and
Gallery js). Weirdly enough, if I keep the shortcode in the content (via Gutenberg / Code Editor) but don't
call the_content and only echo the shortcode then everything works fine:
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
get_header( 'shop' );
//works fine only if the same shortcode is within the certainproductpage's content
echo do_shortcode("[product_page id='815']");
//the_content();
get_footer( 'shop' );
Also when I try to add the shortcode via the_content filter hook before the do_shortcode function is applied in core's
default-filters.php ($priority < 11), then I get only the error:
NOTICE: PHP message: PHP Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/html/wp-includes/functions.php on line 5106
Unfortunately there is no stack trace logged. And the function around line 5107 is wp_ob_end_flush_all which is called on shutdown from default-filters.php
echo do_shortcode(apply_filters('the_content', "[product_page id=815]")); did not help either (same incomplete output as
with echo do_shortcode("[product_page id=815]");)
Also totally weird:
When I compare the string of the content from the editor and the string of the shortcode added programmatically it is
equal!:
add_filter( "the_content", function ( $content ){
$wtf = "<!-- wp:paragraph -->
<p>[product_page id=815]</p>
<!-- /wp:paragraph -->";
$result = $wtf === $content;
?><pre><?php var_dump($result)?></pre><?php
return $content;
}, 1 );
But if I replace return $content with return $wtf - I get the maximimum exucution time exceeded error.
So how can I properly output a product page on the homepage ("/") or how can I get the same result with the shortcode
when applied within the the_content filter as when just adding the shortcode in the (Gutenberg) editor?
Update
Tested it with a simple custom shortcode outputting only a heading tag and it works fine with the_content filter. Also tried it on an absolutely clean site with only WooCommerce and PayPal installed - with the same results. Seems to be a bug on the WooCommerce side. Gonna run it through xDebug some day this week.
Ok, found a bit of a hacky solution. I just check on every page load whether the homepage is currently queried or not. Then I get the page content and check if it already contains the shortcode. If not then the page content gets updated in the database with the shortcode appended.
//it has to be a hook which loads everything needed for the wp_update_post function
//but at the same time has not global $post set yet
//if global $post is already set, the "certainproductpage" will load content not modified by the following code
add_action( "wp_loaded", function () {
//check if homepage
//there seems to be no other simple method to check which page is currently queried at this point
if ( $_SERVER["REQUEST_URI"] === "/" ) {
$page = get_post(get_option('page_on_front'));
$product = get_page_by_path( "certainproduct", OBJECT, "product" );
if ( $page && $product ) {
$page_content = $page->post_content;
$product_id = $product->ID;
$shortcode = "[product_page id=$product_id]";
//add shortcode to the database's post_content if not already done
$contains_shortcode = strpos( $page_content, $shortcode ) > - 1;
if ( ! $contains_shortcode ) {
$shortcode_block = <<<EOT
<!-- wp:shortcode -->
{$shortcode}
<!-- /wp:shortcode -->
EOT;
$new_content = $page_content . $shortcode_block;
wp_update_post( array(
'ID' => $page->ID,
'post_content' => $new_content,
'post_status' => "publish"
) );
}
}
}
} );
I'd recommend one step at a time. First of all, does this work?
add_filter( "the_content", function ( $content ) {
$content .= do_shortcode( '[product_page id=815]' );
return $content;
}, 1 );
This should append a product page to every WordPress page/post.
If it works, then you need to limit it to the homepage only, by using is_front_page() conditional in case it's a static page:
add_filter( "the_content", function ( $content ) {
if ( is_front_page() ) {
$content .= do_shortcode( '[product_page id=815]' );
}
return $content;
}, 1 );
If this works too, then we'll see how to return a Gutenberg paragraph block, but not sure why you'd need that, so maybe give us more context

Override WooCommerce Shop Site Breadcrumb with Yoast SEO Breadcrumb

I tried to remove the WooCommerce Breadcrumbs with this code:
add_action( 'init', 'remove_wc_breadcrumbs' );
function remove_wc_breadcrumbs() {
remove_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb', 20, 0 );
}
...which works.
After that I activated the Yoast Seo Plugin Breadcrumbs an placed this code in my theme header.php
<?php if ( function_exists('yoast_breadcrumb') ) {
yoast_breadcrumb('<p id="breadcrumbs">','</p>');
} ?>
...which also works.
The problem appears when I navigate to my "shop-site" which I defined in the WooCommerce settings. The full breadcrumb looks like this
Home > Product
Note: The Page name in WordPress for the "shop-site" is "Test" (not "Product").
I want to override the "Product" in something different. I tried to override it by defining a custom breadcrumb title in the advanced Yoast settings on the wordpress "shop-site" page but it didn't seems to work. Overriding works if the page isn't defined as the "shop-site" in the WooCommerce settings. So I guess some function overwrites the Yoast Breadcrumb override but I can't figure out which function does this.
The same problem appears when I navigate to a single product. Breadcrumb looks like this
Home > Product > Product Name
Again I want to rename the "Product" (only the second crumb here) into something different.
Can someone solve this mysticism?
I fixed it with this code:
add_filter( 'wpseo_breadcrumb_output', 'custom_wpseo_breadcrumb_output' );
function custom_wpseo_breadcrumb_output( $output ){
if( is_product() ){
$from = 'rel="v:url" property="v:title">Product</a>';
$to = 'rel="v:url" property="v:title">New Title</a>';
$output = str_replace( $from, $to, $output );
}
elseif ( is_shop() ) {
$from = '<span class="breadcrumb_last">Products</span>';
$to = '<span class="breadcrumb_last">New Title</span>';
$output = str_replace( $from, $to, $output );
}
return $output;
}
I wonder if there wouldn't be an easier way to change the first "product" into sth like "product category"...
Have you tried in wp-admin:
Go to "Yoast"
Then "SEO settings"
Then "Breadcrumbs" and adapt taxonomies (eg: product > category).

Wordpress: Custom <title> for archive pages

I'm having a little trouble modifying the page title for my archive pages. I have a custom post type for movies, and I can't seem to alter it. Right now it reads "Movies archive". I'd like to change it altogether to something like "Programme".
Any ideas?
Thanks!
It depends on the function your template is using to output the title of your custom post type archive.
I guess it's using either wp_title or get_the_archive_title so you can try adding a filter (inside functions.php of your theme):
function movies_archive_title( $title ) {
if(is_post_type_archive('movie'))
return 'Programme';
return $title;
}
add_filter( 'wp_title', 'movies_archive_title' );
add_filter( 'get_the_archive_title', 'movies_archive_title' );
If the page viewed is the movies archive (be sure to replace 'movie' with the exact name of your custom post type name) then it replace the title with Programme.
You can add a filter in functions.php like this to override the title, yet preserve the site name and title separator:
function movies_archive_title( $title ) {
$site_name = get_bloginfo();
$sep = apply_filters( 'document_title_separator', '|' );
$sep = str_pad( $sep, 30, " ", STR_PAD_BOTH );
if(is_post_type_archive('movie'))
return 'Programme'.$sep.$site_name;
return $title;
}
// Raise priority above other plugins/themes that
// have effect on the title with 900 (the default is 10).
add_filter( 'wp_title', 'movies_archive_title', 900 );
add_filter( 'get_the_archive_title', 'movies_archive_title', 900 );
Replace movie with your custom post-type name and Programme with the desired title in the above example.
With some plugins and themes you need to raise the priority with the priority parameter of the add_filter method or you won't see a change.

Disable WooCommerce SKU on Product Page

I have a WooCommerce store and I don't want to display the SKU on any single product page. Looking at their code, I found this filter:
/**
* Returns whether or not SKUS are enabled.
* #return bool
*/
function wc_product_sku_enabled() {
return apply_filters( 'wc_product_sku_enabled', true );
}
and I attempted to override it with this line of code I placed in a custom plugin:
apply_filters( 'wc_product_sku_enabled', false );
I also tried placing the apply_filter inside an action function for woocommerce_product_meta_start which fires right before but it still renders the SKU on the product page. Any ideas?
I think you shoul try with this:
add_filter( 'wc_product_sku_enabled', '__return_false' );
That will remove sku from all woo, back and front end. You can always hide it just by CSS if need it on admin.
The easiest way is with CSS:
.sku_wrapper {
display:none;
}
A more robust approach is to recreate the woocommerce template woocommerce/templates/single-product/meta.php in your own theme and simply comment out the line:
<span class="sku_wrapper"><?php _e( 'SKU:', 'woocommerce' ); ?> <span class="sku" itemprop="sku"><?php echo ( $sku = $product->get_sku() ) ? $sku : __( 'N/A', 'woocommerce' ); ?></span>.</span>
To recreate a woocommerce template in your own theme, see:
http://docs.woothemes.com/document/template-structure/
Hiding the SKU/UGS by using cSS is not an efficient solution because it will be still part of the HTML code.
In order to hide it from the product single page and keep it in the admin page, you have to add this code in the child (or parent if you don’t have the child) functions.php :
// Remove the Product SKU from Product Single Page
add_filter( 'wc_product_sku_enabled', 'woocustomizer_remove_product_sku' );
function woocustomizer_remove_product_sku( $sku ) {
// Remove only if NOT admin and is product single page
if ( ! is_admin() && is_product() ) {
return false;
}
return $sku;
}
Make sure also in the product php page (it can have a different name depending on the theme you use) to have this condition to show the SKU in the product single page:
if (wc_product_sku_enabled() && $product->get_sku()) { // HTML code that shows the SKU in the product single page}
Make Sure to remove it from the frontend only by using this code on function.php usually you can edit the function file on theme editor
add_filter( 'wc_product_sku_enabled', 'my_remove_sku', 10 );
function my_remove_sku( $return, $product ) {
if ( !is_admin() && is_product() ) {
return false;
} else {
return true;
}
}
If you don’t need to use SKUs at all in your shop, you can disable them completely by using this plugin. simply install this plugin. https://wordpress.org/plugins/woocommerce-remove-sku/

Only show images/attachments within the gallery?

When i create a page, add a gallery, and browse this gallery on the front-end it will browse all the attachments that are associated with that page, instead of just the images within that gallery. Is there a way to filter all the other attachments and only show the images within a certain gallery? So that, for instance, when I delete the gallery and add a new gallery on the same page > only the new gallery is shown?
Any ideas?
This might not be the most elegant way, but i've found it very usefull.
Passing a post ID to the function below will load a gallery from the post_content of that post. So you would create a gallery and insert it into your post content, then in the template you run this function and will be returned with an array of attachments in that gallery which you are free to to whatever with, i.e slideshows and the likes.
function wp_load_gallery($post_id) {
$post = get_post( $post_id );
$regx = '/' . get_shortcode_regex() . '/';
preg_match( $regx, $post->post_content, $matches );
$ids = shortcode_parse_atts( $matches[3] );
$gallery = array( );
foreach( explode( ',', $ids['ids'] ) as $id ) {
if($id) {
$gallery[] = get_post( $id );
}
}
return $gallery;
}
Note that the shortcode is not cut from the content, so when you display the content you should run it through the strip_shortcodes function, i.e:
echo strip_shortcodes( get_the_content() );
This allows you to update the gallery whenever you want with whatever you want.
EDIT:
To simply display all images:
$gallery = wp_load_gallery($YOUR_POST_ID);
foreach($gallery as $image) {
echo wp_get_attachment_image($image->ID);
}

Resources