Setting Cookie for Custom URL Parameter - wordpress/woocommerce - wordpress

I've created a shortcode that always me to use a URL parameter to dynamically swap the featured product on the home page which works well:
// Shortcode to display specific product page via URL - ?myppid=46
// =============================================================================
function ppid_from_query( $atts ) {
$atts = shortcode_atts( array(
'default' => 46
), $atts, 'myppid' );
$pp_id = $atts['default'];
if ( isset( $_REQUEST['ppid'] ) && $_REQUEST['ppid'] != '' ) {
$pp_id = intval( $_REQUEST['ppid'] );
}
return do_shortcode( "[product_page id='".$pp_id."']" );
}
add_shortcode( 'myppid', 'ppid_from_query' );
The challenge I'm having, is that if the user goes to any other page on my site and returns, the URL parameter is gone, and the default product shows up.
I've tried several methods for passing the parameter, but have not succeeded... Even if I had, it would have worked for that session only. In other words, if user leaves site and comes back to primary domain without ?ppid=X, then they won't see that product. So to that end, I've tried setting a cookie per this thread I found (granted it's a bit dated): https://wordpress.stackexchange.com/questions/188749/i-am-looking-to-append-url-parameter-to-all-urls
So in my header, I have:
<script>
if(!isset($_SESSION['ppid']) and $_GET['ppid']){
$cookie_expire = time()+60*60*24*30;
$_SESSION['ppid'] = $_GET['ppid'];
setcookie('ppid', $_SESSION['ppid'], $cookie_expire, '/', '.'.$_SERVER['HTTP_HOST']);
</script>
and in functions:
function wprdcv_param_redirect(){
if(isset($_COOKIE['ppid']) and !$_GET['ppid']){
$location = esc_url(add_query_arg('ppid', $_COOKIE['ppid']));
wp_redirect($location);
}
}
add_action('template_redirect', 'wprdcv_param_redirect');
Still no luck. What am I doing wrong?

The PHP code is processed by the web server and not by the browser. You don't have to set the cookie between the <script> tags. Instead, run it via the Wordpress init hook.
Based on #MattMelton's comment I added the checkout page exclusion in the wprdcv_param_redirect function.
You can optimize your functions like this:
add_shortcode( 'myppid', 'ppid_from_query' );
function ppid_from_query( $atts ) {
$atts = shortcode_atts( array(
'default' => 46
), $atts, 'myppid' );
$pp_id = $atts['default'];
if ( isset( $_REQUEST['ppid'] ) && $_REQUEST['ppid'] != '' ) {
$pp_id = intval( $_REQUEST['ppid'] );
}
return do_shortcode( "[product_page id='".$pp_id."']" );
}
// set the cookie based on the url parameter "ppid"
add_action( 'init', 'set_cookie_based_on_url_parameter_ppid' );
function set_cookie_based_on_url_parameter_ppid() {
if ( ! isset($_SESSION['ppid'] ) && $_GET['ppid'] ) {
$cookie_expire = time()+60*60*24*30;
$_SESSION['ppid'] = $_GET['ppid'];
setcookie('ppid', $_SESSION['ppid'], $cookie_expire, '/', '.'.$_SERVER['HTTP_HOST']);
}
}
add_action('template_redirect', 'wprdcv_param_redirect');
function wprdcv_param_redirect(){
// if it's the checkout page, don't redirect
if ( is_checkout() ) {
return;
}
/*
// if it is the order confirmation page in checkout, please do not redirect
if ( is_checkout() && is_wc_endpoint_url( 'order-received' ) ) {
return;
}
*/
if ( isset($_COOKIE['ppid']) && ! $_GET['ppid'] ) {
$location = esc_url( add_query_arg('ppid', $_COOKIE['ppid']) );
wp_redirect($location);
}
}
The code has been tested and works for me. Add it to your active theme's functions.php.

Related

WPML Save choosed language by cookie

I have this code:
add_filter( 'icl_ls_languages', function( $languages ){
global $sitepress;
foreach( $languages as $lang_code => $language ){
$languages[$lang_code]['url'] = add_query_arg( array( 'switch_language' => $lang_code ), $languages[$lang_code]['url'] );
}
return $languages;
});
add_action( 'init', function (){
$switch_language = filter_input(INPUT_GET, 'switch_language', FILTER_SANITIZE_STRING);
if( $switch_language ) {
$languages = icl_get_languages();
if( array_key_exists( $switch_language, $languages ) ) {
setcookie( 'wp-wpml_user_selected_language', $switch_language, time() + (10 * 365 * 24 * 60 * 60), '/' );
wp_redirect( strtok( $_SERVER['REQUEST_URI'], '?' ) );
exit;
}
}
}, 1);
add_action( 'pre_get_posts', function ( $query ) {
$switch_language = filter_input( INPUT_GET, 'switch_language', FILTER_VALIDATE_BOOLEAN );
$user_selected_language = filter_input( INPUT_COOKIE, 'wp-wpml_user_selected_language', FILTER_SANITIZE_STRING );
if ( $user_selected_language && !$switch_language ) {
do_action( 'wpml_switch_language', $user_selected_language );
}
}, 1);
It works great. But there is one problem. The site has WP Rocket + connected cloudflaer.
When the user selects a language for the first time, this code writes a cookie and then always redirects to the selected language for each page.
But if the cookie is still there and the user selects a different language, the error ERR_TOO_MANY_REDIRECTS appears. And after 4 seconds, the site loads in a new language with a new cookie value.
As I understand it, WP Rocket caches cookies.
I put the cookie ID in the restriction of this plugin. I even deleted it before choosing the language, but there is still an error.
I need help with this.

WooCommerce: Populate [product_page id="X"] where X is a URL parameter

I have written the following code with the goal of dynamically populating the woocommerce shortcode [product_page id="X"] with a url parameter where X is the product id, with a fallback default (product ID=46) if no parameter exists in the url.
For example:
example.com/somepage?ppid=60 would change the shortcode to [product_page id="60"]
whereas
example.com/somepage would change the shortcode to [product_page id="46"] where 46 is the "default"
However, it is currently not returning the correct product page. Oddly, no matter what I try, it defaults to product ID 440 (which is the highest product ID in my db).
function ppid_from_query($atts) {
$atts = shortcode_atts(array('default' => 46), $atts, 'myppid');
$pp_id = $atts['default'];
if (isset($_REQUEST['ppid']) && !empty($_REQUEST['ppid'])) {
$pp_id = intval($_REQUEST['ppid']);
}
if (!empty($pp_id) && 46 != $pp_id) {
return do_shortcode("[product_page id='{$ppid}']");
}
}
add_shortcode('myppid', 'ppid_from_query');
And shortcode is added to page like [myppid default=46"]
What am I doing wrong?
You pass the wrong variable $ppid instead of $pp_id. try below code.
function ppid_from_query( $atts ) {
$atts = shortcode_atts( array(
'default' => 46
), $atts, 'myppid' );
$pp_id = $atts['default'];
if ( isset( $_REQUEST['ppid'] ) && $_REQUEST['ppid'] != '' ) {
$pp_id = intval( $_REQUEST['ppid'] );
}
return do_shortcode( "[product_page id='".$pp_id."']" );
}
add_shortcode( 'myppid', 'ppid_from_query' );

Custom permalink with dynamic rewrite tags on a Wordpress custom post type

I have a custom post type named location and I'm trying to set posts of this type as children of existing pages, to achieve a permalink structure like this:
example.com/alabama <-- Page with generic content
example.com/alabama/locations <-- Another page, child of a state page
example.com/alabama/locations/location-name <- Custom type, child of alabama/locations seen above and titled `Location Name`
The closest I've got to create hierarchical relationships between two distinct post types was through a meta box where I can assign a post ID as the post_parent of my custom type posts when saving them. However despite the page ID being indeed saved to the CPT post_parent field, it has no effect on the permalinks. They are coming as defined in the rewrite CPT option. But I don't know how to make the ['rewrite']['slug'] option dynamic, or if it's even possible.
This is how my post type is being defined:
add_action( 'init', function() {
register_post_type( 'location', [
'label' => 'Location',
'menu_icon' => 'dashicons-location-alt',
'supports' => [ 'title', 'editor', 'custom-fields' ],
'public' => true,
'hierarchical' => false,
'has_archive' => false,
'rewrite' => false,
] );
} );
How can I configure the rewrite rules for locations to get the permalinks I need?
I'm assuming that all the location posts will have a permalink structure that looks like this:
example.com/{STATE NAME}/locations/{CPT SLUG}
Sample URL addresses:
http://example.com/alabama/locations/location-1
http://example.com/alabama/locations/location-2
http://example.com/new-york/locations/location-3
So if that is correct, then:
• Use the add_rewrite_rule() function to add a custom rewrite rule for those permalinks.
• You don't need the /locations/ Page.
add_action( 'init', function(){
// Handles requests to `your-site-domain.com/{STATE NAME}/locations/{CPT SLUG}`
add_rewrite_rule(
'([^/]+)/locations/([^/]+)(?:/([0-9]+))?/?$',
'index.php?location=$matches[2]&page=$matches[3]&state_name=$matches[1]',
'top'
);
// Allows you to retrieve the `state_name`; for example using `get_query_var()`.
add_rewrite_tag( '%state_name%', '([\w\-]+)' );
} );
(You can change state_name to another name; it's up to you. And don't forget to flush the rewrite rules — go to the Permalink Settings page and click on the Save Changes button without having to make any changes.)
Next, when you create or edit a location post, set the value of the post_parent custom field to the ID of the 'state Page' — e.g. the /alabama/ Page.
And this code will filter the get_permalink() output, and returns the appropriate permalink for a location post:
add_filter( 'post_type_link', 'so51217355_post_type_link', 10, 2 );
function so51217355_post_type_link( $permalink, $post ) {
if ( 'location' === $post->post_type ) {
$page_id = get_post_meta( $post->ID, 'post_parent', true );
$state_name = ( is_numeric( $page_id ) && $page_id ) ?
get_post_field( 'post_name', $page_id ) : null;
// Make sure the post is associated to a valid 'state Page'.
if ( $state_name ) {
$permalink = $state_name . '/locations/' . $post->post_name;
$permalink = home_url( user_trailingslashit( $permalink ) );
}
}
return $permalink;
}
So for example, get_permalink( 123 ) would return http://example.com/alabama/locations/location-1, if the location post's slug is location-1, and its 'state Page' is /alabama/.
UPDATE
When the permalink is requested (i.e. users visit example.com/{STATE NAME}/locations/{CPT SLUG}), and you want to make sure the 'state Page' and location post both exist, and that the 'state Page' was indeed associated to the location post, then this code can help you:
// Validates the `state_name` of the current page/URL.
add_action( 'parse_request', 'so51217355_parse_request' );
function so51217355_parse_request( $wp ) {
if ( ! empty( $wp->query_vars['state_name'] ) &&
! empty( $wp->query_vars['location'] ) ) {
global $wpdb;
$page_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['state_name']
) );
if ( ! is_numeric( $page_id ) || ! $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
return;
}
$post_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['location']
) );
$page_id2 = get_post_meta( $post_id, 'post_parent', true );
if ( (int) $page_id2 !== (int) $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
}
}
}
UPDATE #2
Refer to the // Comment in the code below this image — see the _so51217355_admin_ajax_js() function.
add_action( 'wp_ajax_so51217355_admin_ajax', '_so51217355_admin_ajax_php' );
function _so51217355_admin_ajax_php() {
$post_id = filter_input( INPUT_POST, 'post_id' );
echo get_sample_permalink_html( $post_id );
wp_die();
}
add_action( 'admin_print_footer_scripts', '_so51217355_admin_ajax_js', 11 );
function _so51217355_admin_ajax_js() {
$screen = get_current_screen();
if ( 'location' === $screen->id ) :
?>
<script>
// This script will sync the Permalink under the big/main post title box on
// the Edit Post page; but only if and when editing or deleting the custom
// field as in `meta_key` below. Make sure to change it, if necessary.
jQuery( function( $ ){
var meta_key = 'post_parent';
function ajax() {
$.post( ajaxurl, {
action: 'so51217355_admin_ajax',
post_id: $( '#post_ID' ).val()
}, function( s ){
$( '#edit-slug-box' ).html( s );
} );
}
function _go( e, a ) {
var $input = $( a.target ),
mid, mkey;
if ( /^meta\-(\d+)\-submit$/.test( $input.attr( 'name' ) ) ||
/^deletemeta\[(\d+)\]$/.test( $input.attr( 'name' ) ) ) {
mid = RegExp.$1;
mkey = $( 'input[name="meta[' + mid + '][key]"]' ).val();
if ( meta_key === mkey ) {
ajax();
}
}
}
$( '#the-list' )
.on( 'wpListAddEnd', _go )
.on( 'wpListDelEnd', _go );
} );
</script>
<?php
endif;
}

Can I turn a WordPress Draft into a published post with a direct link if I know the post ID?

For example, using a function like this:
http://website.com/wp-admin/post.php?post=%%post_id%%&action=publish
P.S. I checked and this does not work, but I'm wondering if there is something similar in spirit that works?
You can paste this code in your theme functions.php file. This will do the trick, now if you change action parameter to draft and sent a get request , it will make that post draft.
add_action( 'admin_init', 'draft_post_status_221' );
function draft_post_status_221(){
// Get current page , so this action will only fire in post.php page.
global $pagenow;
if ( $pagenow != 'post.php' ){
return;
}
$post_id = false;
$action = false;
// get post id
if ( isset($_GET['post']) && !empty($_GET['post']) ){
$post_id = $_GET['post'];
}
// get action
if ( isset($_GET['action']) && !empty($_GET['action']) ){
$action = $_GET['action'];
// for security we only allow draft action
if ( $action != 'draft' ){
$action = false;
}
}
// if $post_id and $action has data than post will be updated.
if ( !empty($post_id) && !empty($action) ){
$args = array(
'ID' => $post_id,
'post_status' => $action
);
wp_update_post( $args );
}
}

WordPress SEO plugin only visible to specific user

I am using the Yoast SEO plugin in WordPress and wanted to know if there was a way to make it only visible to one specific user in the db or in the functions.php file? Not a role, an actual user.
I tried an universal solution to simply add "plugin-name" and disable it, but failed.
But, to show WPSEO only to a specific user (ID equals 2), the following works:
add_action( 'plugins_loaded', 'seo_so_25654837' );
function seo_so_25654837()
{
if ( '2' == get_current_user_id() )
return;
remove_action( 'plugins_loaded', 'wpseo_admin_init', 15 );
}
Don't add the code to functions.php, use it as a normal plugin.
The following is also needed to remove the SEO menu from the admin bar:
add_action( 'wp_before_admin_bar_render', 'bar_so_25654837' );
function bar_so_25654837()
{
if ( '2' == get_current_user_id() )
return;
global $wp_admin_bar;
$nodes = $wp_admin_bar->get_nodes();
foreach( $nodes as $node )
{
if( !$node->parent )
{
if( 'wpseo-menu' === $node->id )
$wp_admin_bar->remove_menu( $node->id );
}
}
}
You can hook to pre_current_active_plugins to remove elements from the table before it is displayed. Using get_current_user_id() within the function will let you selectively hide a plugin.
function hide_plugins_by_user( $all_plugins=false ) {
global $wp_list_table;
// if the current user ID is not 1, hide it.
if ( 1 != get_current_user_id() ){
// the active plugins from the table
$plugins = $wp_list_table->items;
// loop through them
foreach ( $plugins as $key => $val ) {
// use the dir + filename of the plugin to hide
if ( $key == 'plugindir/plugin.php' ) {
unset( $wp_list_table->items[$key] );
}
}
}
}
add_action( 'pre_current_active_plugins', 'hide_plugins_by_user' );
This code is working fine (Credits goes to Hislop):
// Returns true if user has specific role
function check_user_role( $role, $user_id = null ) {
if ( is_numeric( $user_id ) )
$user = get_userdata( $user_id );
else
$user = wp_get_current_user();
if ( empty( $user ) )
return false;
return in_array( $role, (array) $user->roles );
}
// Disable WordPress SEO meta box for all roles other than administrator and seo
function wpse_init(){
if( !(check_user_role('seo') || check_user_role('administrator')) ){
// Remove page analysis columns from post lists, also SEO status on post editor
add_filter('wpseo_use_page_analysis', '__return_false');
// Remove Yoast meta boxes
add_action('add_meta_boxes', 'disable_seo_metabox', 100000);
}
}
add_action('init', 'wpse_init');
function disable_seo_metabox(){
remove_meta_box('wpseo_meta', 'post', 'normal');
remove_meta_box('wpseo_meta', 'page', 'normal');
}
Just place it in the functions.php file.
To disable the Yoast for all the users and enable it for just for few or specific, just add the following piece of code to your function.php file.
function remove_wpseo(){
/* if you want to keep it enabled for user with id 2 */
if ( '2' == get_current_user_id() ) {
return;
}
global $wpseo_front;
if(defined($wpseo_front)){
remove_action('wp_head',array($wpseo_front,'head'),1);
}
else {
$wp_thing = WPSEO_Frontend::get_instance();
remove_action('wp_head',array($wp_thing,'head'),1);
}
}
add_action('template_redirect','remove_wpseo');
Reference: https://makersbyte.com/disable-yoast-seo-plugin-specific-page/

Resources