how to customize wordpress internal functions, like adjacent_post_link() - wordpress

I need to customize the previous_post_link() and next_post_link() on WordPress.
Per example, I want to shrink permalinks like "Top 5 programming languages that you need to learn" to "Top 5 programing...".
The function responsible for creating the link isadjacent_post_link() located at wp-includes/link-template.php

To create a custom adjacent link for posts I can make use of the filter hook next_post_link and previos_post_link;
In the functions.php:
function shrink_previous_post_link($format, $link){
$in_same_cat = false;
$excluded_categories = '';
$previous = true;
$link='%title';
$format='« %link';
if ( $previous && is_attachment() )
$post = & get_post($GLOBALS['post']->post_parent);
else
$post = get_adjacent_post($in_same_cat, $excluded_categories, $previous);
if ( !$post )
return;
$title = $post->post_title;
if ( empty($post->post_title) )
$title = $previous ? __('Previous Post') : __('Next Post');
$rel = $previous ? 'prev' : 'next';
//Save the original title
$original_title = $title;
//create short title, if needed
if (strlen($title)>40){
$first_part = substr($title, 0, 23);
$last_part = substr($title, -17);
$title = $first_part."...".$last_part;
}
$string = '<a href="'.get_permalink($post).'" rel="'.$rel.'" title="'.$original_title.'">';
$link = str_replace('%title', $title, $link);
$link = $string . $link . '</a>';
$format = str_replace('%link', $link, $format);
echo $format;
}
function shrink_next_post_link($format, $link){
$in_same_cat = false;
$excluded_categories = '';
$previous = false;
$link='%title';
$format='%link »';
if ( $previous && is_attachment() )
$post = & get_post($GLOBALS['post']->post_parent);
else
$post = get_adjacent_post($in_same_cat, $excluded_categories, $previous);
if ( !$post )
return;
$title = $post->post_title;
if ( empty($post->post_title) )
$title = $previous ? __('Previous Post') : __('Next Post');
$rel = $previous ? 'prev' : 'next';
//Save the original title
$original_title = $title;
//create short title, if needed
if (strlen($title)>40){
$first_part = substr($title, 0, 23);
$last_part = substr($title, -17);
$title = $first_part."...".$last_part;
}
$string = '<a href="'.get_permalink($post).'" rel="'.$rel.'" title="'.$original_title.'">';
$link = str_replace('%title', $title, $link);
$link = $string . $link . '</a>';
$format = str_replace('%link', $link, $format);
echo $format;
}
add_filter('next_post_link', 'shrink_next_post_link',10,2);
add_filter('previous_post_link', 'shrink_previous_post_link',10,2);
That all I needed to do. Thanks!

the next_post_link() and previous_post_link() function both come with customization options. http://codex.wordpress.org/Function_Reference/next_post_link. After reading that and learning what the acceptable arguments to the function are, Test to see if it is possible to pass a php function to the option, like substr().
<?php next_post_link('%link',substr('%title',20),FALSE);?>
'%link' and '%title' are shortcodes to the post link and title.
Let us know if it works out.

Related

Functions.php to set all external links with rel:="nofollow"

when i use the code:
add_filter('the_content', 'my_nofollow');
add_filter('the_excerpt', 'my_nofollow');
function my_nofollow($content) {
return preg_replace_callback('/<a[^>]+/', 'my_nofollow_callback', $content);
}
function my_nofollow_callback($matches) {
$link = $matches[0];
$site_link = get_bloginfo('url');
if (strpos($link, 'rel') === false) {
$link = preg_replace("%(href=\S(?!$site_link))%i", 'rel="nofollow" $1', $link);
} elseif (preg_match("%href=\S(?!$site_link)%i", $link)) {
$link = preg_replace('/rel=\S(?!nofollow)\S*/i', 'rel="nofollow"', $link);
}
return $link;
}
only in the_content and the_excerpt the links get a nofollow attribute. How i can edit the code, that the whole wordpress site use this functions (footer, sidebar...)
Thank you
a good script which allows to add nofollow automatically and to keep the other attributes
function nofollow(string $html, string $baseUrl = null) {
return preg_replace_callback(
'#<a([^>]*)>(.+)</a>#isU', function ($mach) use ($baseUrl) {
list ($a, $attr, $text) = $mach;
if (preg_match('#href=["\']([^"\']*)["\']#', $attr, $url)) {
$url = $url[1];
if (is_null($baseUrl) || !str_starts_with($url, $baseUrl)) {
if (preg_match('#rel=["\']([^"\']*)["\']#', $attr, $rel)) {
$relAttr = $rel[0];
$rel = $rel[1];
}
$rel = 'rel="' . ($rel ? (strpos($rel, 'nofollow') ? $rel : $rel . ' nofollow') : 'nofollow') . '"';
$attr = isset($relAttr) ? str_replace($relAttr, $rel, $attr) : $attr . ' ' . $rel;
$a = '<a ' . $attr . '>' . $text . '</a>';
}
}
return $a;
},
$html
);
}
The idea is to add your action in the full html buffer, once everything is loaded and filtered, not only in the 'content' area.
BE VERRY CAREFULL WITH THIS !!!! This action is verry RAW and low-level... You can really mess up everything with theses buffer actions. But it's also very nice to know ;-)
It works like this :
add_action( 'wp_loaded', 'buffer_start' ); function buffer_start() { ob_start( "textdomain_nofollow" ); }
add_action( 'shutdown', 'buffer_end' ); function buffer_end() { ob_end_flush(); }
Then do the replace action and setup the callback :
Juste imagine the $buffer variable as your full site in HTML, as it will load.
function textdomain_nofollow( $buffer ) {
return preg_replace_callback( '/<a[^>]+/', 'textdomain_nofollow_callback', $buffer );
}
Here is how I do the href checking (read my comments) :
function textdomain_nofollow_callback( $matches ) {
$link = $matches[0];
// if you need some specific external domains to exclude, just use :
//$exclude = '('. home_url() .'|(http|https)://([^.]+\.)?(domain-to-exclude.org|other-domain-to-exclude.com)|/)';
// By default, just exclude your own domain, and your relative urls that starts by a '/' (if you use relatives urls)
$exclude = '('. home_url() .'|/)';
if ( preg_match( '#href=\S('. $exclude .')#i', $link ) )
return $link;
if ( strpos( $link, 'rel=' ) === false ) {
$link = preg_replace( '/(?<=<a\s)/', 'rel="nofollow" ', $link );
} elseif ( preg_match( '#rel=\S(?!nofollow)#i', $link ) ) {
$link = preg_replace( '#(?<=rel=.)#', 'nofollow ', $link );
}
return $link;
}
It works well in my experience...
Q

Show the custom attribute "variable product" in the list of related products

Based on "How to add variation stock status to Woocommerce product variation dropdown" answer, I have made some code that shows the custom attribute of a variable product and its presence in the store:
// Function that will check the stock status and display the corresponding additional text
function get_stock_status_text($product, $name, $term_slug) {
foreach($product - > get_available_variations() as $variation) {
if ($variation['attributes'][$name] == $term_slug)
$stock = $variation['is_in_stock'];
}
return $stock == 1 ? '' : ' - out of stock';
}
// The hooked function that will add the stock status to the dropdown options elements.
add_filter('woocommerce_dropdown_variation_attribute_options_html', 'show_stock_status_in_dropdown', 10, 2);
function show_stock_status_in_dropdown($html, $args) {
// Only if there is a unique variation attribute (one dropdown)
if (sizeof($args['product'] - > get_variation_attributes()) == 1):
$options = $args['options'];
$product = $args['product'];
$attribute = $args['attribute']; // The product attribute taxonomy
$name = $args['name'] ? $args['name'] : 'attribute_'.sanitize_title($attribute);
$id = $args['id'] ? $args['id'] : sanitize_title($attribute);
$class = $args['class'];
$show_option_none = $args['show_option_none'] ? true : false;
$show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __('Select size', 'woocommerce');
if (empty($options) && !empty($product) && !empty($attribute)) {
$attributes = $product - > get_variation_attributes();
$options = $attributes[$attribute];
}
$html = '<select id="'.esc_attr($id).
'" class="'.esc_attr($class).
'" name="'.esc_attr($name).
'" data-attribute_name="attribute_'.esc_attr(sanitize_title($attribute)).
'" data-show_option_none="'.($show_option_none ? 'yes' : 'no').
'">';
$html. = '<option value="">'.esc_html($show_option_none_text).
'</option>';
if (!empty($options)) {
if ($product && taxonomy_exists($attribute)) {
$terms = wc_get_product_terms($product - > get_id(), $attribute, array('fields' => 'all'));
foreach($terms as $term) {
if (in_array($term - > slug, $options)) {
// HERE Added the function to get the text status
$stock_status = get_stock_status_text($product, $name, $term - > slug);
$html. = '<option value="'.esc_attr($term - > slug).
'" '.selected(sanitize_title($args['selected']), $term - > slug, false).
'>'.esc_html(apply_filters('woocommerce_variation_option_name', $term - > name).$stock_status).
'</option>';
}
}
} else {
foreach($options as $option) {
$selected = sanitize_title($args['selected']) === $args['selected'] ? selected($args['selected'], sanitize_title($option), false) : selected($args['selected'], $option, false);
// HERE Added the function to get the text status
$stock_status = get_the_stock_status($product, $name, $option);
$html. = '<option value="'.esc_attr($option).
'" '.$selected.
'>'.esc_html(apply_filters('woocommerce_variation_option_name', $option).$stock_status).
'</option>';
}
}
}
$html. = '</select>';
endif;
return $html;
}
On a single product page, I show related products. How to show this attribute (its presence in the store) in the form of a label in related products?
For example:
The product's name
Price - $25
Sizes available: L, XL
Try this code, I hooked into the woocommerce_after_shop_loop_item and displayed the product attributes. Right now it only works for the size attribute. It is showing the Large, Medium, Small. If you want to show L, XL, M, etc. just edit the size attribute.
add_action( 'woocommerce_after_shop_loop_item', 'mamham_woocommerce_after_shop_loop_item', 10);
function mamham_woocommerce_after_shop_loop_item() {
global $product;
// Bail if not in the single product page.
if ( ! is_product() ) return;
// Prduct attributes which you want to show.
// Attributes to show in the slug.
// 'attribute slug' => 'Text shown';
$show_attributes = array(
'size' => 'Sizes available:',
);
// Get the slugs.
$show_slugs = array_keys( $show_attributes );
// Get product type.
$product_type = $product->get_type();
if( 'simple' === $product_type ) {
$product_attributes = $product->get_attributes();
foreach( $product_attributes as $key => $product_attribute ) {
$slug = substr( $key, 3 );
if ( in_array( $slug, $show_slugs, TRUE ) ) {
$terms = $product_attribute->get_terms();
$terms_output = wp_list_pluck( $terms, 'name');
$terms_output = implode(', ', $terms_output );
echo esc_html( "${show_attributes[$slug]} ${terms_output}" );
}
}
} elseif ( 'variable' === $product_type ) {
$product_variations = $product->get_available_variations();
foreach ( $product_variations as $product_variation ) {
if ( $product_variation['is_in_stock'] && isset( $product_variation['attributes']['attribute_pa_size'] )) {
$terms_output[] = ucwords( $product_variation['attributes']['attribute_pa_size'] );
}
}
if ( ! empty( $terms_output) ) {
$terms_output = implode(', ', $terms_output );
echo esc_html( "Sizes available: ${terms_output}" );
}
}
}
Here is the screenshot, I have storefront theme and WooCommerce installed.

inserting wp_nav_menu_items (or similar solution) without saving to database

So I coded some custom menu items as drop downs that are dynamically generated. It worked fine, but then I realised they were also saved to the database. i want to create drop downs that hook in to the main menu but do NOT save in the database as they are different for every user depending on user meta, any ideas?
Also this is a child theme so has to hook into the main menu, no option of generating a new menu etc as its part of a horrible theme builder.
function _custom_nav_menu_item( $title, $url, $order, $parent = 0 ){
$item = new stdClass();
$item->ID = 1000000 + $order + parent;
$item->db_id = $item->ID;
$item->title = $title;
$item->url = $url;
$item->menu_order = $order;
$item->menu_item_parent = $parent;
$item->type = '';
$item->object = '';
$item->object_id = '';
$item->classes = array();
$item->target = '';
$item->attr_title = '';
$item->description = '';
$item->xfn = '';
$item->status = '';
return $item;
}
// Custom nav menu. Adding dropdowns etc according to user permissions.
function custom_nav_menu_items( $items, $menu ){
$user_permissions = user_is_loggedin_with_permissions();
if( $menu->slug == 'main-menu-english' ){
// Announcements dropdown, users allowed regions.
$top = _custom_nav_menu_item( 'Announcements', site_url( '/announcements' ), 2 );
$items[] = $top;
$i = 1;
foreach( $user_permissions['Region'] as $region ){
$for_humans = ucfirst( str_replace( '_', ' ', $region ) );
$items[] = _custom_nav_menu_item( $for_humans, site_url( '?post_type=announcements&regions='.$region ), '10'.$i, $top->ID );
$i++;
}
}
return $items;
}
add_filter( 'wp_get_nav_menu_items', 'custom_nav_menu_items', 20, 2 );

woocommerce display last purchase and cross sells current user

I use specific thank you pages depend of product
I want to display the last purchase (product's name) of the current user on this page and fetch cross sell of the specific product too.
Any help would be apreciate
OF course i made some research and found tutorial like this one
https://www.skyverge.com/blog/get-all-woocommerce-orders-for-a-customer/#comment-703884
But i am not enaugh good at php to modify it for my needs
Thank you
If you want to display 50 last purchased product for example, go to function.php in Appearance>Editor>function.php then past the code below:
// Recently Sold Products shortcode for WooCommerce
add_shortcode('sold_products', 'sold_products');
function sold_products($attributes) {
global $wpdb, $woocommerce;
$defaults = array('max_products' => 50, 'title' => "Last purchased");
$parameters = shortcode_atts( $defaults, $attributes);
$max = absint($parameters['max_products']); // number of products to show
$title = sanitize_text_field($parameters['title']);
$html = '<div class="sold_products">'.PHP_EOL;
if ($title) {
$html .= '<h3>Last purchased:</h3>'.PHP_EOL;
}
$table = $wpdb->prefix.'woocommerce_order_items';
$my_query = $wpdb->prepare("SELECT * FROM $table WHERE `order_item_type`='line_item' ORDER BY `order_id` DESC LIMIT %d", $max);
$nr_rows = $wpdb->query($my_query);
if (!$nr_rows) {
$html .= '<p>Aucun produit n\'est acheté encore!</p>';
} else {
$html .= '<ul>'.PHP_EOL;
for ($offset = 0; $offset < $nr_rows; $offset++) {
if ( is_user_logged_in() ) {
$user_info = wp_get_current_user();
}
$row = $wpdb->get_row($my_query, OBJECT, $offset);
$product_name = $row->order_item_name;
$product = get_page_by_title($product_name, OBJECT, 'product');
$url = get_permalink($product->ID);
$order_id = $row->order_id;
$order = new WC_Order($order_id);
$user = $order->get_user();
if ($user) {
$user_id = $user->ID;
} else {
$user_id = 'Guest';
}
if($user_info->ID == $user_id){
$unix_date = strtotime($order->order_date);
$date = date('d/m/y', $unix_date);
$html .= '<li>'.$product_name.' acheté le '.$date.'</li>'.PHP_EOL;
}
}
$html .= '</ul>'.PHP_EOL;
}
$html .= '</div>'.PHP_EOL;
return $html;
} // end function
you can dislay this result anywhere in your pages just past this shotcode where you want it to be displayed:
[sold_products max_products="50" title="Last purchased"]

How to get current post_id in function.php file

This is my function in function.php file
function getcity(){
global $wpdb;
if($_POST['state'])
{
$id=$_POST['state'];
$district = get_post_meta(get_the_ID() , 'district', true);
var_dump($district);
$result=$wpdb->get_results("SELECT * FROM districts WHERE state_id='$id'");
foreach($result as $row) {
$district_name = $row-
>district_name;
$district_id = $row->district_id;
echo '<option value="'.$district_id.'">'.$district_name.'</option>';
}
}
}
add_action("wp_ajax_nopriv_getcity", "getcity");
add_action("wp_ajax_getcity", "getcity");
I want to get current post id in this function to display selected dropdown value..
Note that $post or get_queried_object_id() do not work until the first query was fired. So these options are available only at the hook template_redirect and later. But functions.php is included much earlier (before the hook after_setup_theme) so this isn't a solution.
A function that should work pretty much anywhere would be:
$url = 'http://' . $_SERVER[ 'HTTP_HOST' ] . $_SERVER[ 'REQUEST_URI' ];
$current_post_id = url_to_postid( $url );
Here is a overview over hook execution order.
As mentioned in the comments url_to_postid may be heavy in some setups, so if your code is executed after template_redirect you better use one of the solutions below as they are most likely much faster than the snippet above:
global $post;
$id = $post->id;
or
$id = get_queried_object_id();
Here is one my function what never fail.
I don't know why WordPress not made something like this before.
/**
* Get current page ID
* #autor Ivijan-Stefan Stipic
* #version 2.0.0
**/
function get_current_page_ID(){
static $current_page_ID;
if(!$current_page_ID) return $current_page_ID;
global $post, $wp_query, $wpdb;
if(!is_null($wp_query) && isset($wp_query->post) && isset($wp_query->post->ID) && !empty($wp_query->post->ID)){
$current_page_ID = $wp_query->post->ID;
} else if(function_exists('get_the_id') && !empty(get_the_id())){
$current_page_ID = get_the_id();
}else if(!is_null($post) && isset($post->ID) && !empty($post->ID))
$current_page_ID = $post->ID;
else if( (isset($_GET['action']) && sanitize_text_field($_GET['action']) == 'edit') && $post = ((isset($_GET['post']) && is_numeric($_GET['post'])) ? absint($_GET['post']) : false))
$current_page_ID = $post;
else if($p = ((isset($_GET['p']) && is_numeric($_GET['p'])) ? absint($_GET['p']) : false))
$current_page_ID = $p;
else if($page_id = ((isset($_GET['page_id']) && is_numeric($_GET['page_id'])) ? absint($_GET['page_id']) : false))
$current_page_ID = $page_id;
else if(!is_admin() && $wpdb)
{
$actual_link = rtrim($_SERVER['REQUEST_URI'], '/');
$parts = explode('/', $actual_link);
if(!empty($parts))
{
$slug = end($parts);
if(!empty($slug))
{
if($post_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts}
WHERE
`post_status` = %s
AND
`post_name` = %s
AND
TRIM(`post_name`) <> ''
LIMIT 1",
'publish',
sanitize_title($slug)
)
))
{
$current_page_ID = absint($post_id);
}
}
}
}
else if(!is_admin() && 'page' == get_option( 'show_on_front' ) && !empty(get_option( 'page_for_posts' ))){
$current_page_ID = get_option( 'page_for_posts' );
}
return $current_page_ID;
}
This function check all possible cases and return ID of the current page or false on the fail.

Resources