Hey guys is there a way to keep encoded link after order is completed in wordpress?
It looks good in cart and checkout:
link
But not so good in order review and admin order review
link
I use encodeURIComponent to encode every url param:
My example link that i generate
EDIT
Product creation page: https://isodos.lt/produktas/pinigine/ If you want to see order review page you can add it to the cart and test buy it with random name etc.
Btw, i took php code from somewhere, not my code, im not good with php, especially in wordpress
//small example
const graviravimasFinished = document.querySelector("#iconic-engraving-finished");
if(tf1text === "nerNieko" || tf1text === ""){
tf1TxtFinal = "";
}else{
let txt1TxtEncoded = encodeURIComponent(tf1text);
tf1TxtFinal = `&tf1inner=${txt1TxtEncoded}`;
}
tf1TxtFinal = `${tf1TxtFinal}${tf1TopFinal}${tf1LeftFinal}${tf1SizeFinal}${tf1FontFinal}`;
tf2TxtFinal = `${tf2TxtFinal}${tf2TopFinal}${tf2LeftFinal}${tf2SizeFinal}${tf2FontFinal}`;
tf3TxtFinal = `${tf3TxtFinal}${tf3TopFinal}${tf3LeftFinal}${tf3SizeFinal}${tf3FontFinal}`;
graviravimasFinished.value = `Stilius`;
<?php
function iconic_output_engraving_field() {
global $product;
if ( $product->get_id() !== 5296 ) {
return;
}
?>
<!-- Graviravimo tekstas -->
<div style="padding-bottom: 10px" class="iconic-engraving-field">
<!-- Info -->
<input style="display: none;" type="text" id="iconic-engraving-finished" name="iconic-engraving-finished">
</div>
<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'iconic_output_engraving_field', 10 );
/**
* Add engraving text to cart item.
*
* #param array $cart_item_data
* #param int $product_id
* #param int $variation_id
*
* #return array
*/
function iconic_add_engraving_text_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
$engraving_text = filter_input( INPUT_POST, 'iconic-engraving-finished' );
if ( empty( $engraving_text ) ) {
return $cart_item_data;
}
$cart_item_data['iconic-engraving-finished'] = $engraving_text;
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'iconic_add_engraving_text_to_cart_item', 10, 3 );
/**
* Display engraving text in the cart.
*
* #param array $item_data
* #param array $cart_item
*
* #return array
*/
function iconic_display_engraving_text_cart( $item_data, $cart_item ) {
if ( empty( $cart_item['iconic-engraving-finished'] ) ) {
return $item_data;
}
$item_data[] = array(
'key' => __( 'Graviravimas', 'iconic-finished' ),
'value' => $cart_item['iconic-engraving-finished'] ,
'display' => '',
);
return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'iconic_display_engraving_text_cart', 10, 2 );
/**
* Add engraving text to order.
*
* #param WC_Order_Item_Product $item
* #param string $cart_item_key
* #param array $values
* #param WC_Order $order
*/
function iconic_add_engraving_text_to_order_items( $item, $cart_item_key, $values, $order ) {
if ( empty( $values['iconic-engraving-finished'] ) ) {
return;
}
$item->add_meta_data( __( 'Graviravimas', 'iconic-finished' ), $values['iconic-engraving-finished'] );
}
add_action( 'woocommerce_checkout_create_order_line_item', 'iconic_add_engraving_text_to_order_items', 10, 4 );
Related
My request is to provide a separate copyright field for images and to display the copyright information for each image. For this purpose I have inserted a separate field in the media library based on this source (https://bavotasan.com/2012/add-a-copyright-field-to-the-media-uploader-in-wordpress/)
/**
* Adding a "Copyright" field to the media uploader $form_fields array
*
* #param array $form_fields
* #param object $post
*
* #return array
*/
function add_copyright_field_to_media_uploader( $form_fields, $post ) {
$form_fields['copyright_field'] = array(
'label' => __('Copyright'),
'value' => get_post_meta( $post->ID, '_custom_copyright', true ),
'helps' => 'Set a copyright credit for the attachment'
);
return $form_fields;
}
add_filter( 'attachment_fields_to_edit', 'add_copyright_field_to_media_uploader', null, 2 );
/**
* Save our new "Copyright" field
*
* #param object $post
* #param object $attachment
*
* #return array
*/
function add_copyright_field_to_media_uploader_save( $post, $attachment ) {
if ( ! empty( $attachment['copyright_field'] ) )
update_post_meta( $post['ID'], '_custom_copyright', $attachment['copyright_field'] );
else
delete_post_meta( $post['ID'], '_custom_copyright' );
return $post;
}
add_filter( 'attachment_fields_to_save', 'add_copyright_field_to_media_uploader_save', null, 2 );
/**
* Display our new "Copyright" field
*
* #param int $attachment_id
*
* #return array
*/
function get_featured_image_copyright( $attachment_id = null ) {
$attachment_id = ( empty( $attachment_id ) ) ? get_post_thumbnail_id() : (int) $attachment_id;
if ( $attachment_id )
return get_post_meta( $attachment_id, '_custom_copyright', true );
}
With the above code in place, you can now use the following snippet to display your attachment’s new “Copyright” field within one of your page templates.
<?php echo get_featured_image_copyright(); ?>
Unfortunately, this snippet is only available for featured-images.
How can I use this snippet for images and galleries within articles? Would that work with the codebase? If not, how would you solve this problem without plugins? I would be very grateful for your help.
It seems that you have a parameter in the function get_featured_image_copyright($attachment_id);.
So you can call the function with an image id :
get_featured_image_copyright($image_id);.
Example with a gallery:
while ( have_posts() ) : the_post();
if ( get_post_gallery() ) :
$gallery = get_post_gallery( get_the_ID(), false );
/* Loop through all the image and output copyright one by one */
foreach( $gallery['ids'] as $id ) :
echo get_featured_image_copyright($id);
endforeach;
endif;
endwhile;
No tested but you get the idea and i'm pretty sure i didn't made any typo
'header_menu',
'menu_class' =>'navbar-nav' ,
'container' =>'div' ,
'container_class' => 'navbar-collapse collapse justify-content-center',
'container_id' => 'navbarDefault',
'depth' => 2,
)
);
?>
No, you will need to create your own menu class here how to do this;
https://code.tutsplus.com/tutorials/understanding-the-walker-class--wp-25401
https://wordpress.stackexchange.com/questions/248369/how-to-create-this-custom-menu-walker
https://developer.wordpress.org/reference/classes/walker/
This way you can edit and create menus according to what you want.
Note: You can add class through the menu in the wordpress panel, but you will need to manually add to all.
You can do that via the 'nav_menu_css_class' filter:
function wpdocs_special_nav_class( $classes, $item ) {
// Apply specific class: .special-class
$classes[] = "special-class";
return $classes;
}
add_filter( 'nav_menu_css_class' , 'wpdocs_special_nav_class' , 10, 2 );
References:
Add custom CSS classes to wp_nav_menu’s HTML output using WordPress filters
Codex
/**
* Add class to the li element on a menu.
*
* #param $classes
* #param $item
* #param $args
*
* #return array
*/
function add_classes_on_li( $classes, $item, $args ) {
if( 'primary' === $args->theme_location ) {
$classes[] = 'nav-item';
}
if( 'secondary' === $args->theme_location ) {
$classes[] = 'nav-item';
}
return $classes;
}
add_filter( 'nav_menu_css_class', 'add_classes_on_li', 1, 3 );
/**
* Add class to primary menu anchors.
*
* #param $atts
* #param $item
* #param $args
* #param $depth
*
* #return array
*/
function add_class_to_primary_menu_anchors( $atts, $item, $args, $depth ) {
if( 'primary' === $args->theme_location ) {
$atts['class'] = 'nav-link';
}
return $atts;
}
add_filter( 'nav_menu_link_attributes', 'add_class_to_primary_menu_anchors', 10, 4 );
How can i create breadcrumb home->page->post name when we click on main menu any page, open the list of post that time breadcrumb create home ->page name its ok but now when we click on any post that time breadcrumb create home->post category name->post name is is that when we click on post category name on breadcrumb layout shown different we want to its goes on page link, not category link. so we need to when we open any post we need to create the breadcrumb like this home->page name->post name so when we click on page name open the post list page, not category page.
WordPress doesn't provide builtin breadcrumbs functionality. Thus you'll have to either use a plugin or else code it yourself (or copy from the reference below).
As a matter of fact, the plugin or custom code, if providing similar functionality, make not much of a difference. Thus use the one which is more convenient for you.
If you would like to add a custom code, here are few resources which I could look up on search:
https://www.techpulsetoday.com/wordpress-breadcrumbs-without-plugin/
https://www.thewebtaylor.com/articles/wordpress-creating-breadcrumbs-without-a-plugin
https://www.codexworld.com/wordpress-how-to-display-breadcrumb-without-plugin/
https://gist.github.com/tinotriste/5387124
You can look into them and modify them as you wish!
I hope it helps!
I can't understand how an answer with only pasted links can get to that many upvote. The regular WordPress breadcrumb approach is painfully unoptimized, most of the one out there do not suit custom themes. I decided to built a URL based breadcrumb which is, from my point of view, far more efficient and adaptable. I wanted something generic, SEO friendly, without any default styling. It needed also to properly handle posts and pages title.
Version
Requires at least WordPress:
5.0.0
Requires at least PHP:
7.0.0
Tested up to WordPress:
6.0.2
The latest version is available on my GitHub as an unofficial WordPress plugin.
<?php
/**
* Checks if a string ends with a given substring.
* Backward compatibility for PHP < 8.0.0.
*
* #since 1.2.0
* #param String $haystack The string to search in.
* #param String $needle The substring to search for in the haystack.
* #return Boolean
*/
if ( ! function_exists( 'backward_compatibility_str_ends_with' ) ) {
function backward_compatibility_str_ends_with( $haystack, $needle ) {
$length = strlen( $needle );
if ( ! $length ) {
return true;
};
return substr( $haystack, -$length ) === $needle;
};
};
/**
* Determine if a string contains a given substring.
* Backward compatibility for PHP < 8.0.0.
*
* #since 1.2.0
* #param String $haystack The string to search in.
* #param String $needle The substring to search for in the haystack.
* #return Boolean
*/
if ( ! function_exists( 'backward_compatibility_str_contains' ) ) {
function backward_compatibility_str_contains( $haystack, $needle ) {
if ( strpos( $haystack, $needle ) !== false ) {
return true;
};
};
};
/**
* Retrieve the crumbs.
*
* #since 1.0.0
* #return Array Crumbs array.
*/
if ( ! function_exists( 'get_the_crumbs' ) ) {
function get_the_crumbs() {
/**
* $_SERVER["REQUEST_SCHEME"] seems to be UNRELIABLE.
*
* Article "Is $_SERVER['REQUEST_SCHEME'] reliable?".
* #see https://stackoverflow.com/a/18008178/3645650
*
* $_SERVER['REQUEST_SCHEME'] is a native variable of Apache web server since its version 2.4.
* Naturally, if a variable is not set by the server, PHP will not include it in its global array $_SERVER.
*
* An alternative to $_SERVER['REQUEST_SCHEME'] is $_SERVER['HTTPS'] which set to a non-empty value if the script was queried through the HTTPS protocol.
*
* Article "How to find out if you're using HTTPS without $_SERVER['HTTPS']".
* #see https://stackoverflow.com/a/16076965/3645650
*/
if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) {
$server_scheme = 'https';
} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || ! empty( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on' ) {
$server_scheme = 'https';
} else {
$server_scheme = 'http';
};
/**
* $_SERVER["REQUEST_URI"] seems to be RELIABLE.
* $_SERVER['REQUEST_URI'] will not be empty in WordPress, because it is filled in wp_fix_server_vars() (file wp-includes/load.php).
*
* Article "Is it safe to use $_SERVER['REQUEST_URI']?".
* #see https://wordpress.stackexchange.com/a/110541/190376
*/
$server_uri = $_SERVER['REQUEST_URI'];
/**
* $_SERVER["HTTP_HOST"] seems to be RELIABLE.
*
* Article "How reliable is HTTP_HOST?".
* #see https://stackoverflow.com/a/4096246/3645650
*/
$server_host = $_SERVER["HTTP_HOST"];
if ( backward_compatibility_str_contains( $server_uri, '?' ) ) {
$server_uri = substr( $server_uri, 0, strpos( $server_uri, '?' ) );
};
if ( backward_compatibility_str_ends_with( $server_uri, '/' ) ) {
$server_uri = explode( '/', substr( $server_uri, 1, -1 ) );
} else {
$server_uri = explode( '/', substr( $server_uri, 1 ) );
};
$crumbs = array();
foreach ( $server_uri as $crumb ) {
$slug = esc_html( urldecode( $crumb ) );
$url = esc_url( $server_scheme . '://' . $server_host . '/' . substr( implode( '/', $server_uri ), 0, strpos( implode( '/', $server_uri ), $crumb ) ) . $crumb. '/' );
array_push( $crumbs,
array(
'slug' => $slug,
'url' => $url,
)
);
};
/**
* WordPress, by default, doesn't generate a taxonomy index, meaning https://.../taxonomy will redirect to a 404.
* Any request needs to be made against a term. eg: https://.../taxonomy/term will redirect to taxonomy.php.
* Therefore we need to remove the taxonomy slug from the crumbs array to avoid displaying a link to a 404.
*
* We round up all taxonomies through get_taxonomies().
* #see https://developer.wordpress.org/reference/functions/get_taxonomies/
*
* Through array_filter we filter-out any matching crumbs.
* #see https://www.php.net/manual/en/function.array-filter.php
*/
$banned_slugs = array();
$taxonomies = get_taxonomies(
array(
'public' => true,
),
'objects'
);
foreach ( $taxonomies as $taxonomy ) {
array_push( $banned_slugs, $taxonomy->name );
if ( isset( $taxonomy->rewrite['slug'] ) ) {
array_push( $banned_slugs, $taxonomy->rewrite['slug'] );
};
};
$banned_crumbs = array();
foreach ( $banned_slugs as $banned_slug ) {
$slug = esc_html( $banned_slug );
$url = esc_url( $server_scheme . '://' . $server_host . '/' . substr( implode( '/', $server_uri ), 0, strpos( implode( '/', $server_uri ), $banned_slug ) ) . $banned_slug. '/' );
array_push( $banned_crumbs,
array(
'slug' => $slug,
'url' => $url,
)
);
};
$crumbs = array_filter( $crumbs, function( $crumb ) use ( $banned_slugs ) {
if ( ! in_array( $crumb['slug'], $banned_slugs ) && ! in_array( $crumb['url'], $banned_slugs ) ) {
return ! in_array( $crumb['slug'], $banned_slugs );
};
} );
return $crumbs;
};
};
/**
* Display the bread, a formatted crumbs list.
*
* #since 1.0.0
* #param Array $ingredients The bread arguments.
* #param Array $ingredients['crumbs'] The crumbs array. Default to get_the_crumbs().
* #param Array $ingredients['root'] Root crumb. Default to null.
* #param String $ingredients['root']['slug'] Root crumb slug.
* #param String $ingredients['root']['url'] Root crumb url.
* #param String $ingredients['separator'] The crumb's separator.
* #param Integer $ingredients['offset'] Crumbs offset. Accept positive/negative Integer. Default to "0". Refer to array_slice, https://www.php.net/manual/en/function.array-slice.php.
* #param Integer $ingredients['length'] Crumbs length. Accept positive/negative Integer. Default to "null". Refer to array_slice, https://www.php.net/manual/en/function.array-slice.php.
* #return Array The formatted crumbs list.
*/
if ( ! function_exists( 'the_bread' ) ) {
function the_bread( $ingredients = array() ) {
if ( empty( $ingredients['crumbs'] ) ) {
$crumbs = get_the_crumbs();
} else {
$crumbs = $ingredients['crumbs'];
};
if ( empty( $ingredients['root'] ) ) {
$root = null;
} else {
$root = $ingredients['root'];
};
if ( empty( $ingredients['offset'] ) ) {
$offset = 0;
} else {
$offset = $ingredients['offset'];
};
if ( empty( $ingredients['length'] ) ) {
$length = null;
} else {
$length = $ingredients['length'];
};
/**
* Handling the root crumb case.
* Prepend one or more elements to the beginning of an array.
* #see https://www.php.net/manual/en/function.array-unshift.php
*/
if ( ! empty( $root ) ) {
array_unshift( $crumbs, $ingredients['root'] );
};
/**
* Handling the length case.
* Extract a slice of the array.
* #see https://www.php.net/manual/en/function.array-slice.php
*/
$crumbs = array_slice( $crumbs, $offset, $length );
if ( ! empty( $crumbs ) ) {
echo '<ol class="🍞 bread" itemscope itemtype="https://schema.org/BreadcrumbList">';
$i = 0;
foreach ( $crumbs as $crumb ) {
$i++;
/**
* Unparsing the slug.
*/
if ( url_to_postid( $crumb['url'] ) ) {
$title = get_the_title( url_to_postid( $crumb['url'] ) );
} elseif ( get_page_by_path( $crumb['slug'] ) ) {
$title = get_the_title( get_page_by_path( $crumb['slug'] ) );
} else {
$title = ucfirst( str_replace( '-', ' ', $crumb['slug'] ) );
};
echo '<li class="crumb" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="' . $crumb['url'] . '">
<span itemprop="name">' . $title . '</span>
</a>
<meta itemprop="position" content="' . $i . '">
</li>';
if ( $i !== sizeof( $crumbs ) && ! empty( $ingredients['separator'] ) ) {
echo $ingredients['separator'];
};
};
echo '</ol>';
};
};
};
Displaying the bread, a formatted crumbs list.
<?php
the_bread( $ingredients = array() );
Parameters
Parameter
Description
$ingredients
(Optional) Array The bread arguments.
$ingredients['crumbs']
Array The crumbs array. Default to get_the_crumbs().
$ingredients['root']
Array Root crumb. Default to null.
$ingredients['root']['slug']
(Required if $ingredients['root']). Root crumb slug.
$ingredients['root']['url']
(Required if $ingredients['root']). Root crumb url.
$ingredients['separator']
The crumb's separator.
$ingredients['offset']
Crumbs offset. Accept positive/negative Integer. Default to 0. Refer to array_slice.
$ingredients['length']
Crumbs length. Accept positive/negative Integer. Default to null. Refer to array_slice.
Example: The bread with a custom separator
<?php
$ingredients = array(
'separator' => '→',
);
the_bread( $ingredients );
Example: Displaying the last 3 crumbs
<?php
$ingredients = array(
'offset' => -3,
'length' => 3,
);
the_bread( $ingredients );
Example: The bread with a root crumb
<?php
$ingredients = array(
'root' => array(
'slug' => 'home',
'url' => get_home_url(),
),
);
the_bread( $ingredients );
Example: Intercepting the crumbs array
<?php
//Intercept the crumbs array...
$crumbs = get_the_crumbs();
//... Do something with it:
//In our case we're appending a new crumb to the crumbs array.
array_push( $crumbs, array(
'slug' => 'search',
'url' => 'https://.../search/',
) );
$ingredients = array(
'crumbs' => $crumbs,
);
the_bread( $ingredients );
HTML5 structure output
<ol class="🍞 bread" itemscope="" itemtype="https://schema.org/BreadcrumbList">
<li class="crumb" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="http://example.com/where/">
<span itemprop="name">Where</span>
</a>
<meta itemprop="position" content="1">
</li>
>
<li class="crumb" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="http://example.com/where/is/">
<span itemprop="name">Is</span>
</a>
<meta itemprop="position" content="2">
</li>
>
<li class="crumb" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="http://example.com/where/is/my/">
<span itemprop="name">My</span>
</a>
<meta itemprop="position" content="3">
</li>
>
<li class="crumb" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="http://example.com/where/is/my/bread/">
<span itemprop="name">Bread</span>
</a>
<meta itemprop="position" content="4">
</li>
</ol>
Minimal css boilerplate (Optional)
.🍞,
.bread {
list-style-type: none;
margin:0;
padding:0;
}
.🍞 li,
.bread li {
display:inline-block;
}
.🍞 li.crumb:last-child a,
.bread li.crumb:last-child a {
text-decoration: none;
pointer-events: none;
color: inherit;
}
Retrieving the crumbs
Even tho we recommend you to use the_bread() function to display and build your own breadcrumb, you can use get_the_crumbs() to retrieve the crumbs object.
Example: Outputting the crumbs object
<?php
var_dump( get_the_crumbs() );
A minimalistic breadcrumbs generator in a few lines as a filter:
add_filter( 'my_get_breadcrumbs', [ $this, 'the_breadcrumbs' ], 10, 1 );
/**
* Returns a list of all the breadcrumbs for the current page.
* usage: apply_filters( 'my_get_breadcrumbs', false )
*
* #param $max_depth int
*
* #return string
*/
function the_breadcrumbs() {
$crumbs = '';
$current_page_id = get_the_ID();
$parent = wp_get_post_parent_id( $current_page_id );
$index = 0;
while ( $parent ) {
$index ++;
$crumbs = '<li>' . get_the_title( $parent ) . '</li>' . $crumbs;
$parent = wp_get_post_parent_id( $parent );
if ( $index > 10 ) {
break;
}
}
return $crumbs . '<li><a>' . get_the_title( $current_page_id ) . '</a></li>';
}
Sounds bizarre for me to ask this, in woocommerce is there anyway to have a specific product, which could be controlled via the product id, to only have one in the cart, so in our scenario they select one or the other, if they try to re-add to cart it simply replaces or swaps that item in the cart.
Our main products would not then be affected by this rule.
I know that I've already posted this somewhere, but I can't find it so I will just repost it. This is an entire plugin (so add it as a file to wp-content/plugins) that will 1. add a checkbox to the product information meta box and 2. limit the cart to only that item if that checkbox is checked.
<?php
/*
Plugin Name: WooCommerce Restrict Items in Cart
Description: Forces cart to remove certain items if other items are added
Version: 1.0
Author: Kathy Darling
Author URI: http://kathyisawesome.com
Requires at least: 4.1.0
Tested up to: 4.1.0
Copyright: © 2015 Kathy Darling.
License: GNU General Public License v3.0
License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
/**
* The Main WC_Restrict_Item_in_Cart class
**/
if ( ! class_exists( 'WC_Restrict_Item_in_Cart' ) ) :
class WC_Restrict_Item_in_Cart {
/**
* WC_Restrict_Item_in_Cart init
*
* #access public
* #since 1.0
*/
public static function init() {
// product meta
add_action( 'woocommerce_product_options_general_product_data', array( __CLASS__, 'add_to_wc_metabox' ) );
add_action( 'woocommerce_process_product_meta', array( __CLASS__, 'process_wc_meta_box' ), 1, 2 );
// validation - ensure product is never in the cart with other products
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'maybe_remove_items' ), 10, 3 );
}
/*-----------------------------------------------------------------------------------*/
/* Product Write Panels */
/*-----------------------------------------------------------------------------------*/
/*
* Add text inputs to product metabox
* #since 1.0
*/
public static function add_to_wc_metabox(){
global $post;
echo '<div class="options_group">';
echo woocommerce_wp_checkbox( array(
'id' => '_only_item_in_cart',
'label' => __( 'Only Item In Cart' ) ,
'description' => __( 'For special items that need to be purchased individually.' )
)
);
echo '</div>';
}
/*
* Save extra meta info
* #since 1.0
*/
public static function process_wc_meta_box( $post_id, $post ) {
if ( isset( $_POST['_only_item_in_cart'] ) ) {
update_post_meta( $post_id, '_only_item_in_cart', 'yes' );
} else {
update_post_meta( $post_id, '_only_item_in_cart', 'no' );
}
}
/*-----------------------------------------------------------------------------------*/
/* Check Cart for presence of certain items */
/*-----------------------------------------------------------------------------------*/
/**
* When an item is added to the cart, remove other products
* based on WooCommerce Subscriptions code
*/
public static function maybe_remove_items( $valid, $product_id, $quantity ) {
if ( self::is_item_special( $product_id ) && WC()->cart->get_cart_contents_count() > 0 ){
self::remove_specials_from_cart();
}
return $valid;
}
/*-----------------------------------------------------------------------------------*/
/* Helper methods */
/*-----------------------------------------------------------------------------------*/
/*
* I've added a custom field 'only_item_in_cart' on items on 'special' products
* check for this field similar to how Subscriptions checks cart for subscription items
*/
public static function check_cart_for_specials() {
$contains_special = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( self::is_item_special( $cart_item['product_id'] ) ) {
$contains_special = true;
break;
}
}
return $contains_special;
}
/**
* Removes all special products from the shopping cart.
*/
public static function remove_specials_from_cart(){
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ){
if ( self::is_item_special( $cart_item['product_id'] ) ){
WC()->cart->set_quantity( $cart_item_key, 0 );
$product_title = $cart_item['data']->get_title();
wc_add_notice( sprintf( __( '"%s" has been removed from your cart. Due to shipping calculations, it cannot be purchased in conjunction with other products.', 'wc_Restrict_Item_in_cart' ), $product_title ), 'error' );
}
}
}
/*
* check if an item has custom field
*/
public static function is_item_special( $product_id ){
if ( 'yes' == get_post_meta( $product_id, '_only_item_in_cart', true ) ){
return TRUE;
} else {
return false;
}
}
} //end class: do not remove or there will be no more guacamole for you
endif; // end class_exists check
// Launch the whole plugin
WC_Restrict_Item_in_Cart::init();
I have been looking around for quite awhile now and all I have found are tutorials from 3-4 years ago that explain how to do a pointer tour. All I want to do is add a pointer that pops up when someone activates my plugin so that I can notify them of a new menu option where they will go to view my plugin settings. Any help would be greatly appreciated!
Pointers in WP need 3 components:
1: wp-pointer css file
2: wp-pointer JS file
3: A JavaScript snippet
To 1 and 2
include them simply with:
wp_enqueue_style( 'wp-pointer' );
and
wp_enqueue_script( 'wp-pointer' );
The JS code:
<script type="text/javascript">
(function($){
var options = {"content":"<h3>Personal Data and Privacy<\/h3><h4>Personal Data Export and Erasure<\/h4><p>New <strong>Tools<\/strong> have been added to help you with personal data export and erasure requests.<\/p><h4>Privacy Policy<\/h4><p>Create or select your site’s privacy policy page under <strong>Settings > Privacy<\/strong> to keep your users informed and aware.<\/p>","position":{"edge":"left","align":"bottom"},"pointerClass":"wp-pointer arrow-bottom","pointerWidth":420}, setup;
if ( ! options )
return;
options = $.extend( options, {
close: function() {
$.post( ajaxurl, {
pointer: 'wp500_isrc_pointer',
action: 'dismiss-wp-pointer'
});
}
});
setup = function() {
$('#menu-settings').first().pointer( options ).pointer('open');
};
if ( options.position && options.position.defer_loading )
$(window).bind( 'load.wp-pointers', setup );
else
$(document).ready( setup );
})( jQuery );
</script>
Of Course you need to wrap all them in a php file to check the user capabilities and check the dismiss from the users meta.
I have copied the WP pointer class in wp-admin/includes/class-wp-internal-pointers and made a custom one from it.
Here the complete code which i can call it with an action hook like:
add_action( 'admin_enqueue_scripts', array( 'isrc_Internal_Pointers', 'enqueue_scripts') );
add_action( 'user_register',array( 'isrc_Internal_Pointers', 'dismiss_pointers_for_new_users' ) );
The Full PHP file (include it in your code and call the 2 actions):
<?php
/**
* Administration API: WP_Internal_Pointers class
*
* #package WordPress
* #subpackage Administration
* #since 4.4.0
*/
/**
* Core class used to implement an internal admin pointers API.
*
* #since 3.3.0
*/
final class isrc_Internal_Pointers {
/**
* Initializes the new feature pointers.
*
* #since 3.3.0
*
* All pointers can be disabled using the following:
* remove_action( 'admin_enqueue_scripts', array( 'WP_Internal_Pointers', 'enqueue_scripts' ) );
*
* Individual pointers (e.g. wp390_widgets) can be disabled using the following:
* remove_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_wp390_widgets' ) );
*
* #static
*
* #param string $hook_suffix The current admin page.
*/
public static function enqueue_scripts( $hook_suffix ) {
/*
* Register feature pointers
*
* Format:
* array(
* hook_suffix => pointer callback
* )
*
* Example:
* array(
* 'themes.php' => 'wp390_widgets'
* )
*/
$registered_pointers = array(
'index.php' => 'wp500_isrc_pointer',
);
// Check if screen related pointer is registered
if ( empty( $registered_pointers[ $hook_suffix ] ) )
return;
$pointers = (array) $registered_pointers[ $hook_suffix ];
/*
* Specify required capabilities for feature pointers
*
* Format:
* array(
* pointer callback => Array of required capabilities
* )
*
* Example:
* array(
* 'wp390_widgets' => array( 'edit_theme_options' )
* )
*/
$caps_required = array(
'wp500_isrc_pointer' => array(
'install_plugins'
),
);
// Get dismissed pointers
$dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
$got_pointers = false;
foreach ( array_diff( $pointers, $dismissed ) as $pointer ) {
if ( isset( $caps_required[ $pointer ] ) ) {
foreach ( $caps_required[ $pointer ] as $cap ) {
if ( ! current_user_can( $cap ) )
continue 2;
}
}
// Bind pointer print function
add_action( 'admin_print_footer_scripts', array( 'isrc_Internal_Pointers', 'pointer_'.$pointer ) );
$got_pointers = true;
}
if ( ! $got_pointers )
return;
// Add pointers script and style to queue
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
}
/**
* Print the pointer JavaScript data.
*
* #since 3.3.0
*
* #static
*
* #param string $pointer_id The pointer ID.
* #param string $selector The HTML elements, on which the pointer should be attached.
* #param array $args Arguments to be passed to the pointer JS (see wp-pointer.js).
*/
private static function print_js( $pointer_id, $selector, $args ) {
if ( empty( $pointer_id ) || empty( $selector ) || empty( $args ) || empty( $args['content'] ) )
return;
?>
<script type="text/javascript">
(function($){
var options = <?php echo wp_json_encode( $args ); ?>, setup;
if ( ! options )
return;
options = $.extend( options, {
close: function() {
$.post( ajaxurl, {
pointer: '<?php echo $pointer_id; ?>',
action: 'dismiss-wp-pointer'
});
}
});
setup = function() {
$('<?php echo $selector; ?>').first().pointer( options ).pointer('open');
};
if ( options.position && options.position.defer_loading )
$(window).bind( 'load.wp-pointers', setup );
else
$(document).ready( setup );
})( jQuery );
</script>
<?php
}
/**
* Display a pointer for wp500_isrc_pointer
*
* #since 4.9.6
*/
public static function pointer_wp500_isrc_pointer() {
$content = '<h3>' . __( 'Personal Data and Privacy' ) . '</h3>';
$content .= '<h4>' . __( 'Personal Data Export and Erasure' ) . '</h4>';
$content .= '<p>' . __( 'New <strong>Tools</strong> have been added to help you with personal data export and erasure requests.' ) . '</p>';
$content .= '<h4>' . __( 'Privacy Policy' ) . '</h4>';
$content .= '<p>' . __( 'Create or select your site’s privacy policy page under <strong>Settings > Privacy</strong> to keep your users informed and aware.' ) . '</p>';
if ( is_rtl() ) {
$position = array(
'edge' => 'right',
'align' => 'bottom',
);
} else {
$position = array(
'edge' => 'left',
'align' => 'bottom',
);
}
$js_args = array(
'content' => $content,
'position' => $position,
'pointerClass' => 'wp-pointer arrow-bottom',
'pointerWidth' => 420,
);
self::print_js( 'wp500_isrc_pointer', '#menu-settings', $js_args );
}
/**
* Prevents new users from seeing existing 'new feature' pointers.
*
* #since 3.3.0
*
* #static
*
* #param int $user_id User ID.
*/
public static function dismiss_pointers_for_new_users( $user_id ) {
add_user_meta( $user_id, 'dismissed_wp_pointers', 'wp500_isrc_pointer' );
}
}
What you are looking for is WordPress Activation / Deactivation Hooks. For example:
register_activation_hook( __FILE__, 'pluginprefix_function_to_run' );
And on pluginprefix_function_to_run, display a nice message to let users know that you've added a menu using admin_notices:
function my_admin_notice() {
?>
<div class="updated">
<p><?php _e( 'Your message goes here!', 'my-text-domain' ); ?></p>
</div>
<?php
}
function pluginprefix_function_to_run() {
add_action( 'admin_notices', 'my_admin_notice' );
}