I have the following code which works and changes the title of the 'order received' page:
add_filter( 'the_title', 'woo_title_order_received', 10, 2 );
function woo_title_order_received( $title, $id ) {
if ( function_exists( 'is_order_received_page' ) &&
is_order_received_page() && get_the_ID() === $id ) {
$title = "Thank you, good luck!";
}
return $title;
}
However it causes a fatal error on the shop page due to Too few arguments to function woo_title_order_received(). After checking online I found that the_title isn't correct and it should be get_the_title. If I change it to that the fatal error goes away, but it no longer changes the title on the order received page.
None of the other snippets I've found online have worked, and I can't see why the above stops the shop pages from working. Any ideas?
Try to set $id argument to null (useful when it's not defined):
add_filter( 'the_title', 'woo_title_order_received', 10, 2 );
function woo_title_order_received( $title, $id = null ) {
if ( function_exists( 'is_order_received_page' ) &&
is_order_received_page() && get_the_ID() === $id ) {
$title = "Thank you, good luck!";
}
return $title;
}
It could work…
No idea why you should use the_title WordPress hook with multiple if conditions, while WooCommerce has specific hooks for that.
The 'woocommerce_endpoint_' . $endpoint . '_title' filter hook allows to change the Order received title.
So you get:
/**
* #param string $title Default title.
* #param string $endpoint Endpoint key.
* #param string $action Optional action or variation within the endpoint.
*/
function filter_woocommerce_endpoint_order_received_title( $title, $endpoint, $action ) {
$title = __( 'Thank you, good luck!', 'woocommerce' );
return $title;
}
add_filter( 'woocommerce_endpoint_order-received_title', 'filter_woocommerce_endpoint_order_received_title', 10, 3 );
Related
I'm developing a plugin for custom product type. Here's my class that is being registered on plugins_loaded hook:
class WC_Product_Subscription extends WC_Product {
public function __construct( $product ) {
$this->product_type = 'subscription';
$this->purchasable = true;
$this->downloadable = false;
$this->virtual = true;
$this->sold_individually = true;
$this->manage_stock = false;
$this->supports[] = 'ajax_add_to_cart';
parent::__construct( $product );
}
public function is_purchasable() {
return true;
}
}
The problem is that I cannot see "Add to Cart" button on the product page which means my product cannot be purchased. I tried adding
public function add_to_cart_url() {
return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this );
}
public function add_to_cart_text() {
$text = $this->is_purchasable() && $this->is_in_stock() ? __( 'Add to cart', 'woocommerce' ) : __( 'Read more', 'woocommerce' );
return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this );
}
to the class but without success. I'm stuck.
It appears there are some missing steps to make your custom product type work.
Try the steps below:
#1. Make sure that your plugin is active.
#2. Make sure the product is in stock and has a price set. WooCommerce checks both of these conditions before displaying the Add to Cart button.
#3. Check if the custom product type is registered correctly. Use the following code to check:
add_action( 'init', 'check_registered_product_types' );
function check_registered_product_types() {
$product_types = wc_get_product_types();
var_dump( $product_types );
}
#4. Make sure that the WooCommerce product type is supported. Use the following code to check:
add_filter( 'product_type_selector', 'custom_product_type_selector' );
function custom_product_type_selector( $product_types ) {
var_dump( $product_types );
return $product_types;
}
#5. Make sure that the product class is correctly loaded. Use the following code to check:
add_action( 'plugins_loaded', 'check_product_class' );
function check_product_class() {
$product_class = 'WC_Product_Subscription';
var_dump( class_exists( $product_class ) );
}
#6. Ensure you have a product template for your custom product type in your theme's WooCommerce folder (e.g. single-product-subscription.php).
#7. If everything else seems to be working, you might have to override the WooCommerce templates to display the Add to Cart button.
Edit:
You can create a template in your plugin directory by using the following code:
add_filter( 'woocommerce_locate_template', 'wc_subscription_template', 10, 3 );
function wc_subscription_template( $template, $template_name, $template_path ) {
if ( 'single-product-subscription.php' === $template_name ) {
$template = untrailingslashit( plugin_dir_path( __FILE__ ) ) . '/templates/single-product-subscription.php';
}
return $template;
}
This will tell WooCommerce to use your custom template in the templates folder within your plugin directory. Make sure you put the code in a file that is included in your plugin, so it will run when the plugin is activated.
In the order email templates (for example email-order-items.php), WooCommerce uses the function wc_display_item_meta to display product details in the order table. The function code is present in the wc-template-functions.php file (line number 3011). I am copying the function code below for reference
function wc_display_item_meta( $item, $args = array() ) {
$strings = array();
$html = '';
$args = wp_parse_args( $args, array(
'before' => '<ul class="wc-item-meta"><li>',
'after' => '</li></ul>',
'separator' => '</li><li>',
'echo' => true,
'autop' => false,
) );
foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) {
$value = $args['autop'] ? wp_kses_post( $meta->display_value ) : wp_kses_post( make_clickable( trim( $meta->display_value ) ) );
$strings[] = '<strong class="wc-item-meta-label">' . wp_kses_post( $meta->display_key ) . ':</strong> ' . $value;
}
if ( $strings ) {
$html = $args['before'] . implode( $args['separator'], $strings ) . $args['after'];
}
$html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args );
if ( $args['echo'] ) {
echo $html; // WPCS: XSS ok.
} else {
return $html;
}
}
The problem is: it doesn't take any arguments that can help me filter out item data that I don't want to show in the order email. I don't want to change this function in the wc-template-functions.php as it's a core file. So, I want to know if there's a piece of code that I can add to functions.php that'll somehow modify this wc_display_item_meta function to filter out specific item meta.
Note: I know someone might suggest why not just remove that particular item data from the product details, but that data is essential to internal order processing. I just don't want it to show to the customers.
Update #1: What meta data I don't want to show in the order email? Below is a screenshot of an order email. I have highlighted three item data.."Qty Selector", "Qty" and "Total". I want all these three to not show in the order email.
Try the following without any guarantee (as I don't really have the real necessary keys):
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
if( is_admin() || is_wc_endpoint_url() )
return $formatted_meta;
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
Code goes in function.php file of your active child theme (active theme). Tested with other meta data than yours and works. I hope it will work for you too.
Now, the hook used with this code is the right filter hook. It's located in the WC_Order_Item method get_formatted_meta_data() and allows to filter the order item meta data.
There is a bug with the accepted answer, and all of the other snippets that I've found around the internet, so I'm posting my own answer here in the hopes that stores around the world don't accidentally leak information.
The problem is that when you use the Order actions meta box to resend the email, the filter check fails because is_admin() === true.
The order actions is a meta box down the side of the Orders page:
So the first time, when the order is created, it filters the email like you want, but then if an admin resends the email to a customer then it will be broken and show all of the meta fields to the user in the resent email.
The code that fixes this scenario is this:
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
So if you look at the linked snippet then you will see the meta box adds that field to the $_POST. It has to be cleaned up like that as well or it won't match.
The full example integrated into the accepted solution's answer is:
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
I hear, that you want to show the Order Item Meta Data in the admin backend only. That's actually a tricky one. I have played around for some hours but no solution I have found, guarentee that the Order Itema Meta Data doesn't show up in e-mails to the customer.
The thing is that there are several ways these e-mails are fired (eg. through the resend meta box (which #rtpHarry mentions) or by changing order status either at the order overview, the single order view or an automatic/programmatically order status change). That gives many cases where it's neccessary to unset the Order Item Meta Data - you need to find all cases except the admin backend.
Therefore, my suggestion is to first completely remove the Order Item Meta Data using the above mentioned woocommerce_order_item_get_formatted_meta_data filter and then add them again using an action like woocommerce_before_order_itemmeta which ONLY fires in the admin backend. Because the Order Item Meta Data is unset you cannot use the get_formatted_meta_data method to get the data. Instead you can use the function wc_get_order_item_meta.
Complete code (tested and works):
//Hide 'Qty Selector', 'Qty' and 'Total' completely
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data');
function unset_specific_order_item_meta_data($formatted_meta){
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
//Add 'Qty Selector', 'Qty' and 'Total' in the admin backend only
add_action('woocommerce_before_order_itemmeta', 'add_specific_order_item_meta_data_in_backend', 10, 2);
function add_specific_order_item_meta_data_in_backend( $item_id, $item ) {
//Only applies for line items
if( $item->get_type() !== 'line_item' ) return;
$qty_sel_lines = wc_get_order_item_meta($item_id, 'Qty Selector', false);
$qty_lines = wc_get_order_item_meta($item_id, 'Qty', false);
$total_lines = wc_get_order_item_meta($item_id, 'Total', false);
foreach ($qty_sel_lines as $qty_sel_line){
echo $qty_sel_line . '<br>';
}
foreach ($qty_lines as $qty_line){
echo $qty_line . '<br>';
}
foreach ($total_lines as $total_line){
echo $total_line. '<br>';
}
}
Note:
If you need to add the Order Item Meta Data to the admin e-mails, you need to do that seperately. I have not examined the options on that.
I kind of agreed with #pstidsen argument. So I was thinking about how to solve this without to re-add all the metadata, since it kind of disturbed me not to handle it in the same way as it was added before. I have additional filters to add css classes and so on to the metadata. So there would've been a need to take care of.
So here is my approach which gives you the opportunity to use it for emails, custom emails, pdf invoices or similar scenarios. I also uses a fallback to filter for our frontend or any situation we didn't consider.
Please keep the order of if else in mind. I checked the admin filter the last, to make sure any other filter gets fired before. The situation for an email at example is: It's sent from the admin interface, so the admin filter is true but also is the email filter.
Functionality for a different filter for admin emails is given as well.
/**
* This function filters all unwanted item metadata, if the specific filter are hooked in
* we also use a fallback filter, if none of the hooks are fired
*
* #params array() $metadata
*
*/
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'custom_filter_item_meta_data', 50, 1);
function custom_filter_item_meta_data( $metadata ){
if ( empty( $metadata ) ) return $metadata;
$filter_array = array();
if ( apply_filters( 'custom_filter_item_meta_email', false ) ){
// email filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'email' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin_email', false ) ){
// admin email filter goes here
// pass
elseif ( apply_filters( 'custom_filter_item_meta_invoice', false ) ){
// invoice filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'invoices' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin', false ) ){
// general admin filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'admin_backend' );
}else{
// fallback filter
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'fallback' );
}
foreach ( $metadata as $key => $meta ){
if ( in_array( $meta->key, $filter_array ) ){
unset ( $metadata[ $key ] );
}
}
return $metadata;
}
/**
* Is used to enable our item meta filter for our admin backend
* Hooked:
* #admin_init
*/
add_action( 'admin_init', 'custom_init_item_meta_filter_admin', 50, 1 );
function custom_init_item_meta_filter_admin(){
add_filter( 'custom_filter_item_meta_admin', function(){ return true; });
}
/**
* Is used to enable our item meta filter for emails
* Hooked:
* #woocommerce_email_order_details
*/
add_action( 'woocommerce_email_order_details', 'custom_init_item_meta_filter_email' ), 10, 2);
function custom_init_item_meta_filter_email( $order, $sent_to_admin ){
if ( $sent_to_admin ){
add_filter('custom_filter_item_meta_admin_email', function(){ return true; } );
}else{
add_filter('custom_filter_item_meta_email', function(){ return true; } );
}
}
/**
* Is used to enable our item meta filter for invoices
* Hooked:
* #wpo_wcpdf_before_order_details
*/
add_filter( 'wpo_wcpdf_before_order_details', 'custom_init_item_meta_filter_invoice', 10, 1);
function custom_init_item_meta_filter_invoice(){
add_filter( 'custom_filter_item_meta_invoice', function(){ return true; });
}
I didn't test it in that "flattened" format. I used it within different classes of my oop coded plugin and edited it to post it here.
Structure of the html code of the metadata tags
If I want to delete meta_data 2 and meta_data 3:
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
function filter_woocommerce_display_item_meta( $html, $item, $args ) {
$arrayPortionsTags = explode("<li", $html);
unset($arrayPortionsTags[2],$arrayPortionsTags[3]);
$firstLi = array( '<li' );
$lastUl = array( '</ul>' );
array_splice( $arrayPortionsTags, 1, 0, $firstLi );
array_splice( $arrayPortionsTags, 3, 0, $lastUl );
$html= implode('',$arrayPortionsTags);
return $html;
};
I'm new on plugin development.
I'm try to create a custom Printable form page in wp-admin to create Customer Postal Address.
very Similar This plugin
when administrator click on "print Address" link , pop-up template.php page with customer address and information for print address
The Problem is :
I get fatal Error when click on print order anchor tag and i can't run any wordpress action on template.php:
Fatal error: Call to undefined function add_action() in C:\xampp\htdocs\wp-content\plugins\address generator\template.php on line 4
<?php
/**
* Plugin Name: Address Generator
* Plugin URI: http://CGTV.ir
* Description:Generate Postal Label for Parcel
* Version: 1.0 or
* Author: Hamed Mayahian
* Author URI: CGTV.ir
* License: A "Slug" license name e.g. GPL12
*/
// ADDING COLUMN TITLES (Here 2 columns)
/*define( 'MY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
include( MY_PLUGIN_PATH . 'template.php');
*/
require_once(ADDRESS__PLUGIN_DIR .'template.php');
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column',11);
function custom_shop_order_column($columns)
{
//add columns
$columns['my-column1'] = __( 'چاپ آدرس','theme_slug');
return $columns;
}
// adding the data for each orders by column (example)
add_action( 'manage_shop_order_posts_custom_column' , 'cbsp_credit_details', 10, 2 );
function cbsp_credit_details( $column )
{
global $post, $woocommerce, $the_order;
$order_id = $the_order->id;
switch ( $column )
{
case 'my-column1' :
$myVarOne = wc_get_order_item_meta( $order_id, '_the_meta_key1', true );
echo $myVarOne;
echo "<a target='_blank' href='".plugins_url( 'template.php' , __FILE__ )."?order=$order_id'>Print Address</a>";
break;
}
}
Template.php
<?php
add_action('init', 'my_init', 1);
function my_init(){
global $post, $woocommerce, $the_order;
$id = $_GET['order'];
$order = new WC_Order($id);
$address = $order->get_billing_address();
$customer_id = get_current_user_id();
if($_GET['order'] == "") {
// no username entered
echo "آدرس پیدا نشد";
} else {
echo "Hello, " . $address;
}
}
?>
Since I don't know what you are trying to accomplish, I can only suggest the following as an improvement in how you are launching your plugin and how you are displaying the custom column.
/**
* Plugin Name: Custom Shop Column Link
* Plugin URI: http://stackoverflow.com/a/39280792/383847
* Description: Link for shop column to display billing address
* Version: 1.0.0
* Author: helgatheviking
* Author URI: http://kathyisawesome.com/
* Text Domain: your-plugin
* Domain Path: /languages
*
* Copyright: © 2015 Kathy Darling and Manos Psychogyiopoulos
* License: GNU General Public License v3.0
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
// add all your hooks only when woocommerce has fully loaded it's files
add_action( 'woocommerce_loaded', 'custom_address_generator_init' );
function custom_address_generator_init(){
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column',11);
add_action( 'manage_shop_order_posts_custom_column', 'cbsp_credit_details',11);
}
// add your custom column
function custom_shop_order_column($columns)
{
//add columns
$columns['my-column1'] = __( 'چاپ آدرس', 'your-plugin');
return $columns;
}
// adding the data for each orders by column (example)
function cbsp_credit_details( $column )
{
global $the_order;
$order_id = $the_order->id;
switch ( $column )
{
case 'my-column1' :
$myVarOne = get_post_meta( $order_id, '_the_meta_key1', true );
echo $myVarOne;
$url = add_query_arg( array( 'order_id' => $order_id, 'my-action' => 'do-something-cool', ), wp_nonce_url( admin_url(), 'my_order_nonce', 'my_nonce' ) );
printf( '<a class="custom-class" href="%s" data-order_id="%s">%s</a>', $url, $order_id, __( 'Print Address', 'your-plugin' ) );
break;
}
}
EDIT 2 We're going to create a link to the front-end so we can load a custom template via template_include. It should have enough security on it to keep it limited to only the appropriate users.
// load a custom template when special link is clicked
add_action( 'template_include', 'my_template', 1 );
function my_template(){
if( isset( $_GET['my-action'] ) && $_GET['my-action'] == 'do-something-cool' && isset( $_GET['order_id'] ) && current_user_can( 'edit_shop_order', $_GET['order_id'] ) && wp_verify_nonce( $_GET['my_nonce'], 'my_order_nonce' ) ){
return untrailingslashit( plugin_dir_path( __FILE__ ) ) . '/templates/my-template.php';
}
}
Then a /templates/my-plugin.php file in your plugin folder:
<?php
$order_id = intval( $_GET['order_id'] );
$order = wc_get_order($order_id);
if( is_a( $order, 'WC_Order' ) ){
$address = $order->get_formatted_billing_address ();
if( $address ){
printf( '%s, %s', __( 'Hello', 'your-plugin' ), $address );
} else {
_e( 'No billing address', 'your-plugin' );
}
} else {
_e( 'Not a valid order ID', 'your-plugin' );
}
I've dropped the my_init() function in favor of my_template() which will now load a custom template (/templates/my-template.php) via the template_include filter. This template is loaded by WordPress and has all the WordPress functions available for you to use.
template.php file is out of Wordpress then we are not access Wordpress core functions. Include this file in main plugin is works properly but when we access by url directly this file then we can't access Wordpress core functions because we are not follow Wordpress stranded. Order list table have button called url generate like something is http://localhost/wp-content/plugins/address%20generator/template.php?order=5147. When we access this get following error "Fatal error: Call to undefined function add_action() in.."
First comment this line in your main plugin file.
// require_once('template.php');
Changes in template.php file.
<?php
require('../../../wp-load.php');
$id = $_GET['order'];
$order = new WC_Order($id);
$address = $order->get_billing_address();
$customer_id = get_current_user_id();
if($_GET['order'] == "") {
// no username entered
echo "آدرس پیدا نشد";
} else {
echo "Hello, " . $address;
}
But this is not a Wordpress stranded solution. User #helgatheviking is provide best solution for this.
I have created below function to hide page title. But when I execute this code, it also hides the menu name.
function wsits_post_page_title( $title ) {
if( is_admin())
return $title;
$selected_type = get_option('wsits_page_show_hide');
if(!is_array($selected_type)) return $title;
if ( ( in_array(get_post_type(), $selected_type ) ) && get_option('wsits_page_show_hide') )
{
$title = '';
}
return $title;
}
add_filter( 'the_title', array($this, 'wsits_post_page_title') );
Nikola is correct:
Because menu items also have titles and they need to be filtered :).
To make this only call in the posts, and not in menus, you can add a check for in_the_loop() - if it is true, you're in a post.
So change the first line in the function to:
if( is_admin() || !in_the_loop() )
and all should be well.
It's a bit of a hack but you can solve this by adding your action to loop_start.
function make_custom_title( $title, $id ) {
// Your Code Here
}
function set_custom_title() {
add_filter( 'the_title', 'make_custom_title', 10, 2 );
}
add_action( 'loop_start', 'set_custom_title' );
By embedding the_title filter inside of a loop_start action, we avoid overwriting the menu title attributes.
You can do something like that :
In your function.php :
add_filter( 'the_title', 'ze_title');
function ze_title($a) {
global $dontTouch;
if(!$dontTouch && !is_admin())
$a = someChange($a);
return $a;
}
In your template :
$dontTouch = 1;
wp_nav_menu( array('menu' => 'MyMenu') );
$dontTouch = 0;
Posting this answer because it was the search result I ended up clicking on while searching about targeting the filter hook the_title while ignoring the filter effect for navigation items.
I was working on a section in a theme which I wanted to add buttons to the page title within the heading one tag.
It looked similar to this:
<?php echo '<h1>' . apply_filters( 'the_title', $post->post_title ) . '</h1>'.PHP_EOL; ?>
I was then "hooking in" like this:
add_filter( 'the_title', 'my_callback_function' );
However, the above targets literally everything which calls the_title filter hook, and this includes navigation items.
I changed the filter hook definition like this:
<?php echo '<h1>' . apply_filters( 'the_title', $post->post_title, $post->ID, true ) . '</h1>'.PHP_EOL; ?>
Pretty much every call to the_title filter passes parameter 1 as the $post->post_title and parameter 2 as the $post->ID. Search the WordPress core code for apply_filters( 'the_title'* and you'll see for yourself.
So I decided to add a third parameter for situations where I want to target specific items which call the_title filter. This way, I can still receive the benefit of all callbacks which apply to the_title filter hook by default, while also having the ability to semi-uniquely target items that use the_title filter hook with the third parameter.
It's a simple boolean parameter:
/**
* #param String $title
* #param Int $object_id
* #param bool $theme
*
* #return mixed
*/
function filter_the_title( String $title = null, Int $object_id = null, Bool $theme = false ) {
if( ! $object_id ){
return $title;
}
if( ! $theme ){
return $title;
}
// your code here...
return $title;
}
add_filter( 'the_title', 'filter_the_title', 10, 3 );
Label the variables however you want. This is what worked for me, and it does exactly what I need it to do. This answer may not be 100% relevant to the question asked, but this is where I arrived while searching to solve this problem. Hope this helps someone in a similar situation.
The global $dontTouch; solution didn't work for me for some reason. So I simply removed the filter around the menu thus in header.php:
remove_filter( 'the_title', 'change_title' );
get_template_part( 'template-parts/navigation/navigation', 'top' );
add_filter( 'the_title', 'change_title' );
And all is well.
I think you're looking for this:
function change_title($title) {
if( in_the_loop() && !is_archive() ) { // This will skip the menu items and the archive titles
return $new_title;
}
return $title;
}
add_filter('the_title', array($this, 'change_title'), 10, 2);
When we add new post in wordpress, after supplying the post title, the slug is generated automatically. I need to edit that auto generation module so that i can add some arbitrary number in the end of the slug automatically. How to do it?
Don't use the hard-coded version that the OP used here. When he did that, there was not a filter available. More recently, since 3.3, a filter was added.
add_filter( 'wp_unique_post_slug', 'custom_unique_post_slug', 10, 4 );
function custom_unique_post_slug( $slug, $post_ID, $post_status, $post_type ) {
if ( $custom_post_type == $post_type ) {
$slug = md5( time() );
}
return $slug;
}
However this method will change the slug every time you save the post... Which was what I was hoping for...
EDIT:
This kind of works for limiting the generation to just once. The only drawback is that it creates one version when ajax runs after creating the title, then it creates another, permanent slug when the post is saved.
function custom_unique_post_slug( $slug, $post_ID, $post_status, $post_type ) {
if ( $custom_post_type == $post_type ) {
$post = get_post($post_ID);
if ( empty($post->post_name) || $slug != $post->post_name ) {
$slug = md5( time() );
}
}
return $slug;
}
Write a plugin to hook into the wp_insert_post_data filter so you can update the slug before the post is sent for insertion into the database:
function append_slug($data) {
global $post_ID;
if (empty($data['post_name'])) {
$data['post_name'] = sanitize_title($data['post_title'], $post_ID);
$data['post_name'] .= '-' . generate_arbitrary_number_here();
}
return $data;
}
add_filter('wp_insert_post_data', 'append_slug', 10);
Note that this function requires that you allow WordPress to auto-generate the slug first, meaning you must not enter your own slug before generating, and it cannot update existing posts with the number.
Test this : (paste it into functions.php)
function append_slug($data) {
global $post_ID;
if (!empty($data['post_name']) && $data['post_status'] == "publish" && $data['post_type'] == "post") {
if( !is_numeric(substr($data['post_name'], -4)) ) {
$random = rand(1111,9999);
$data['post_name'] = sanitize_title($data['post_title'], $post_ID);
$data['post_name'] .= '-' . $random;
}
}
return $data; } add_filter('wp_insert_post_data', 'append_slug', 10);
add_filter('post_link','postLinkFilter', 10, 3);
/**
* Manipulates the permalink
*
* #param string $permalink
* #param stdClass $post
* #return string
*/
function postLinkFilter($permalink,stdClass $post){
return $permalink.'?12345';
}
Untested in that scenario, but I have already used it, should work with a minimum of changes, but try and test it REALLY Carefully .
In any Case, don't use rand() here or something alike, since the function must return the same link for the same post every time, otherwise you will have some serious problems.
Have fun!
You should operate with wp_ajax_sample-permalink action and name_save_pre filter.
More examples here: https://wordpress.stackexchange.com/a/190314/42702