I need to be able to switch currency on WooCommerce site by adding specific link. On client's site is installed WooCommerce Multilingual & Multicurrency by OnTheGoSystems.
I have something like this at the moment:
add_filter( 'wcml_client_currency', 'abc_client_currency' );
function abc_client_currency( $currency ) {
if( !empty( $_GET['country'] ) ) {
switch ( $_GET['country']) {
case 'US':
$new_currency = 'USD';
break;
case 'PL':
$new_currency = 'PLN';
break;
default:
$new_currency = 'EUR';
break;
}
$settings = get_option( '_wcml_settings' );
$currencies = $settings['currency_options'];
$currency_codes = array_keys( $currencies );
if( in_array( $new_currency, $currency_codes ) ) {
return $new_currency;
}
}
return $currency;
}
It works on the very first pageload (as long as there is ?contry=XY in url). I know I could possibly save currency into a cookie/session and keep using this method, but that doesn't seam right. I would like to properly switch the currency.
Assuming you have some sort of dropdown list for selecting between currencies:
<ul id="curr_switcher">
<li class="country" data-country="US">USD</li>
<li class="country" data-country="PL">PLN</li>
<li class="country" data-country="FR">EUR</li>
</ul>
I'd use some simple jQuery hooked to the wp_footer action (NOT wp_head as jQuery will likely not be loaded yet) to redirect to the same page/location with correct variable on user click - just hook it to wp_footer action to load on all front-end pages :
<?php
add_action('wp_footer', function () { ?>
<script>
jQuery(document).ready(function($) {
// .country on click
$('.country').click(function(e) {
// prevent default behaviour
e.preventDefault();
// retrieve country code
var country = $(this).data('country');
// retrieve current location pathname
var current_loc_path = window.location.pathname;
// append currency argument to pathname
var new_loc_path = current_loc_path + '/?country=' + country;
// use location replace to redirect page with correctly appended currency as argument
window.location.replace(new_loc_path);
});
});
</script>
<?php }); ?>
So in a nutshell you're retrieving the current browser window location path, appending the selected country to that path on click, and then basically reloading the page (technically a full redirect, but let's not split hairs here).
Note that this is probably not the most "technically correct" way of doing it - there are probably other way more involved ways of achieving the same thing - but my tendency is always towards finding the simplest solution, as long as it works and your code is properly commented for future reference!
Note also: this will likely not work past the current page - probably a good idea to append the country argument to all links you wish to load the correct currency for.
Hopefully the above get you started in the right direction.
Related
I'm building a simple plugin that uses the geoservices web service and what I'm trying to do is dynamically change the content on a WordPress page based on their location. I have it working somewhat but my issue is that it's returning both the location-specific text AND the default. I know it's because i'm using the shortcode instance more than once but I don't know how to change it to ONLY show the location specific content and if the location is not set or does not match the shortcode params then fall back to the default one. I don't want to add "default" as a shortcode param because it could contain HTML or something else.
Here is an example of my shortcode:
[geo city="Orlando"]555-123-6349[/geo][geo city="Raleigh"]919-999-9999[/geo][geo city="Default"]Default text here[/geo]
So based on the above, the desired result would show Orlando's phone number if the user is from Orlando or it would show Raleigh number if they are from Raleigh. Otherwise, if they are not from either of those places, it would use the default.
Here is my shortcode:
function geo_services( $atts , $content = null ) {
// Attributes
extract(shortcode_atts(array(
'city' => '',
'state' => '',
), $atts));
require_once('geoplugin.class.php');
$geoplugin = new geoPlugin();
$geoplugin->locate();
if($city === $geoplugin->city){
return $content;
} elseif ($state === $geoplugin->region){
return $content;
} elseif ($city === 'Default') {
return $content;
}
}
add_shortcode( 'geo', 'geo_services' );
And here is what is happening when I use the example shortcode above:
I believe you may be misunderstanding how shortcodes work in WP. In your example, you have added 3 shortcodes to the content. Each of those shortcodes is going to run. Not one or the other. So doing,
[geo city="Orlando"]555-123-6349[/geo][geo city="Raleigh"]919-999-9999[/geo][geo city="Default"]Default text here[/geo]
means that each of those will be called and evaluated. $geoplugin->city is always going to return the city of the user, regardless of what attributes you supplied. And since you are returning $content in all cases, it will always spit out the content that you added inside the shortcode. This is why you are seeing all 3 responses.
Instead, I would try the approach below. If your goal is to spit out content based on the city of the user, you really don't need to supply an attribute to the shortcode. See the following example:
//in your post/page content, simply use the shortcode geo
[geo]
//your function should be
function geo_services( $atts , $content = null ) {
//
require_once('geoplugin.class.php');
//
$geoplugin = new geoPlugin();
$geoplugin->locate();
//
switch( $geoplugin->city ) {
case 'Orlando':
return '555-123-6349';
break;
case 'Raleigh':
return '919-999-9999';
break;
default:
return 'Default text here';
break;
}
}
add_shortcode( 'geo', 'geo_services' );
Providing another answer based on OP comments. If you really need to manage the content via the WYSIWYG, then you could supply the content for each city as an attribute.
//add shortcode to post/page content
[geo orlando="555-123-6349" raleigh="919-999-9999" default="Custom default text here"]
//your function should be
function geo_services( $atts , $content = null ) {
//don't use extract since we expect X number of atts now
$atts = shortcode_atts(array(
'default' => 'Default text here'
), $atts);
//
require_once('geoplugin.class.php');
//
$geoplugin = new geoPlugin();
$geoplugin->locate();
//was the city provided as an attribute?
if( isset($atts[ strtolower($geoplugin->city) ]) ) {
return $atts[ strtolower($geoplugin->city) ];
}else {
return $atts['default'];
}
}
add_shortcode( 'geo', 'geo_services' );
You may have to get creative with the HTML portion of the content, but now you can include X number of cities, with their custom content, in the shortcode itself. If the city is not supplied, or does not match, it will fallback to the default.
I'm coding a WordPress website in which I want all the external links to be "no-follow" by default.
It is quite long to go and edit each single link to add the "no-follow", is there a way to code it once for all?
Thank you
IMHO, the best way to do so is using a custom filter. Being generated dinamically by PHP, you can edit links on post and pages server-side even before a user visit them: WordPress used to add rel="nofollow" to each link by default in the past, but I see it doesn’t happen anymore. Then, I found a solution by Debjit Saha you may try on functions.php.
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;
}
Please, notice that some plugins could break this code and I’ve never tried this on newer WordPress versions.
One way you can do it is using JavaScript:
var hrefToCheck = "mysite.com" // change this string
noFollowExternalLinks(hrefToCheck); // call to run the function below
function noFollowExternalLinks(siteHref) {
document.querySelectorAll('a').forEach(function(link) {
if (!link.href.includes(siteHref)) {
link.setAttribute("rel", "nofollow");
}
})
}
So, you can change hrefToCheck variable to a piece of your site URL that will be checked inside each link inside the page.
The function will loop to check every link in the page and apply rel="nofollow" to all external links (that don't match the text in the variable).
Good Day,
I have been trying to add verification badge to WordPress users but I no success. I tried using administrator to do the testing by checking if user has role administrator and trying to enter code here update_user_meta (display_name) but I wasn't able to add icons to the display name.
I have as well tried creating a custom field in user profile called verify_user (text field) .... In which I entered the value "verified" and saved ... I have been searching for hooks to use but haven't seen one. I'm not sure which hook/filter to use here
Please is there any solution to adding this verification icon to author's display name in which when the word "verified" is entered into the custom field created in user profile or any other approach available (e.g if user has specific role). I don't mind if the little structure I wrote above would be changed.
Thanks
I was able to get a solution which worked exactly as i wanted. For anyone who might have same task to tackle;
function add_verification_bagdge_to_authors($display_name) {
global $authordata;
if (!is_object($authordata))
return $display_name;
$icon_roles = array(
'administrator',
'verified_author',
);
$found_role = false;
foreach ($authordata->roles as $role) {
if (in_array(strtolower($role), $icon_roles)) {
$found_role = true;
break;
}
}
if (!$found_role)
return $display_name;
$result = sprintf('%s <i title="%s" class="fa fa-check-circle"></i>',
$display_name,
__('This is a Verified Author', 'text-domain-here')
);
return $result;
}
add_filter( 'the_author', 'add_verification_bagdge_to_authors' );
The way i was able to tackle this was to create a user role called Verified Author (using add_role() function from Wordpress Codex ), which will be the role assigned to verified author across the website. All Users with Admin Role are automatically Verified while user role has to be switched from either contributor/Author role to Verified Author.
The above code was able to do 98% of the task but when a verified author / administrator comments, their display name doesn't show the verified badge. I was able to use the below code to fix that (combining the code with a shortcode).
function my_comment_author( $author = '' ) {
// Get the comment ID from WP_Query
$comment = get_comment( $comment_ID );
if ( ! empty($comment->comment_author) ) {
if (!empty($comment->user_id)){
$user=get_userdata($comment->user_id);
$author=$user->display_name.' '. do_shortcode('[this_is_verified-sc]'); // This is where i used the shortcode
} else {
$author = $comment->comment_author;
}
} else {
$author = $comment->comment_author;
}
return $author;
}
add_filter('get_comment_author', 'my_comment_author', 10, 1);
Shortcode to return the Verified Badge if user is admin or verified author
function add_verification_bagdge_to_authors_sc($display_name) {
global $authordata;
if (!is_object($authordata))
return $display_name;
$icon_roles = array(
'administrator',
'verified_author',
);
$found_role = false;
foreach ($authordata->roles as $role) {
if (in_array(strtolower($role), $icon_roles)) {
$found_role = true;
break;
}
}
if (!$found_role)
return $display_name;
$result = sprintf('%s <i title="%s" class="fa fa-check-circle"></i>', $display_name, __('This is a Verified Author', 'text-domain-here') );
return $result;
}
add_shortcode( 'this_is_verified-sc', 'add_verification_bagdge_to_authors_sc' );
Please Note: Not all Magazine theme are properly coded / have a hard coded Block and module elements, so the verified badge might not work on their block / module element. I experienced this during the whole website design process, so we had to change up theme and the only modification i had to make to the new theme was to remove the html escape added to their block/module code, so that the icon can be rendered. i.e. in their module/block code they had something like this esc_html( the_author() ) and i removed the esc_html to have just the_author() an it all worked.
Its not really a straight forward solution but that is how i was able to tackle the task without using a plugin. Just add the codes to your functions.php file and that's it. I hope it helps someone.
Thanks to Kero for pointing me in the right direction, providing the code i was able to work with and manipulate
I know we can add meta for woocommerce cart item using woocommerce_add_cart_item_data hook.
Is there any way to update existing cart item meta.?
Yes, but it seems, only via accessing the cart directly:
global $woocommerce;
$woocommerce->cart->cart_contents[$cart_item_key]['whatever_meta'] = 'testing';
$woocommerce->cart->set_session(); // when in ajax calls, saves it.
I would recommend to remove and re-add the product, as other meta data could be lost (as in Phong Tran's answer).
Based on #DanielSalcedos answer, only keeping the absolute minimum required to answer the question.
I know It's been a while, but as it's still not answered, and it took me lots of sweat and a pint of blood, I share my solution here.
First
I'll assume you know how to add metadata to a cart and into an order. If not, you can have a look at pwnewbie's solution but I recommend you the full article at Wisdm labs
Wisdm's method takes many steps. First you create a PHP's session variable trough Ajax. Second, you intercept woocommerce_add_cart_item_data filter to add your Session variable to woocommerce session.
The thing about woocommerce_add_cart_item_data filter is that it gets executed in the middle of the add to cart process, so, if you add your variable to the main $wooocmmerce object, at some point, it get's stored as the add-to-cart event. (Sort of)
The idea
What if I want to edit that metadata and not any of the standard cart properties. The ideal would be to get a filter or an action that runs in the middle of saving something. The problem is that as long as we don't change anything else, there's no hook to run (I tried with woocommerce_update_cart_action_cart_updated hook that runs after coupons, quantities and removals from cart had happened, but it never fired as I never passed the validations)
My approach
In a shell, rebuild as little as possible, as much as needed. I added a synchronous ajax event to the cart form OnSubmit event. (I want my UI to be updated with my changes, so the reload must happen after my update)
AJAX:
var myFlag33322805 = true;
$('form').submit(function(e){
if(myFlag33322805){
myFlag33322805 = false;
e.preventDefault(); // Flag and prevent default to syncronize submits
var kart = []; // Will retrieve every cart item's meta
$('.cartRow').each(function(){
//This object will store your meta data and be pushed into kart array
var kitm = {
'p' : $(this).data('product_id'),
'm' : $(this).find('select[name=myMetaData]').val(),
'k' : $(this).data('key')
};
kart.push(kitm);
});
var data = {
'action': 'Ajax_Update_My_MetaData_33322805',
'k': kart
};
$.post(VKVAjax.ajaxurl, data, function (response) {
// Might do something with the response here
});
$('form').submit(); // This time, the form will submit, but AJAX wont run because of myFlag33322805 = false
}
});
The magic:
The php ajax response is a function that will receive my meta data to update (actually, all of it, updated or not), and will take the global $woocommerceobject to insert the meta data AND save it to the session:
PHP:
function fn_Update_My_MetaData_33322805(){
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$updt = Array();
foreach ($_POST['k'] AS $item){
$product = new stdClass();
$updtCL = new stdClass();
$product->{'id'} = $item['p']; //This is product id
$product->{'mymeta'} = $item['m']; //This is metadata
$updtCL->{'krtkey'} = $item['k']; //This is product key in cart
$updtCL->{'meta'} = $product;
$updt[] = $updtCL;
}
// cycle the cart replace the meta of the correspondant key
foreach ($cart as $key => $item) {
foreach($updt as $updtitem){
if($key == $updtitem->krtkey){ // if this kart item corresponds with the received, the meta data is updated
// Update the content of the kart
$woocommerce->cart->cart_contents[$key]['vkv_AlternCart_value'] = $updtitem->meta;
}
}
}
// This is the magic: With this function, the modified object gets saved.
$woocommerce->cart->set_session();
wp_die('{"e":"ok", "Updt": "'.count($arrupdt).'"}');
}
Of course, this should be hooked as any other ajax event.
add_action('wp_ajax_nopriv_Ajax_Update_My_MetaData_33322805', 'fn_Ajax_Update_My_MetaData_33322805');
add_action('wp_ajax_Ajax_Update_My_MetaData_33322805', 'fn_Ajax_Update_My_MetaData_33322805');
Conclussion
You can update the meta data of a cart item with a synchronous Ajax call, overwritting the object directly to the $woocommerce global variable and saving it with the $woocommerce->cart->set_session(); method.
Footnotes
It's not the ideal method, and is quite risky to work directlly with the $woocommerce global. I would love to know of a better approach.
I'm newbie and my English is not good so the answer may be a little confusing.
Thank vlad274 for the advice.
My approach:
Same with Daniel Salcedo, I also try to change the metadata by editing woocommerce_sessions, but there is a problem. When adding an item to the cart, the woocommerce will create a unique cart_item_key for it by md5 (product_id + ... + metadata), then look for this cart_item_key already exists, if available, this item will be updated in quantity, otherwise will create a new item.
This means that if you change the meta value of the hat product from blue to red, then add the hat product with blue, instead of creating a new item blue hat, it will only increase the quantity of red hat, you need to change the cart_item_key if you want the cart to be updated correctly.
Changing cart_item_key is quite risky, instead we can simply remove and re-add the product. Like this
// get cart_item_key of item you want to change
$cart_item_key_old = $_POST['cart_item_key'];
// retrieve its information
$cart_item_old = WC()->cart->cart_contents[ $cart_item_key_old ];
$product_id_old = $cart_item_old['product_id'];
$quantity_old = $cart_item_old['quantity'];
$variation_id_old = $cart_item_old['variation_id'];
$variation_old = $cart_item_old['variation'];
// creating a cart_item_key with the same information except metadata
$cart_item_key_new = WC()->cart->generate_cart_id( $product_id_old, $variation_id_old, $variation_old, ['color'=>'red'] );
// check new cart_item_key already exists
$found = WC()->cart->find_product_in_cart( $cart_item_key_new );
// if true, update its quantity
if ($found != '') {
$new_quantity = $cart_item_old['quantity'] + WC()->cart->cart_contents[ $cart_item_key_new ]['quantity'];
WC()->cart->set_quantity( $cart_item_key_new, $new_quantity );
}
// else, re-add with new metadata
else {
WC()->cart->add_to_cart($product_id_old, $quantity_old, $variation_id_old, $variation_old, ['color'=>'red'] );
}
// finally delete the old item
WC()->cart->remove_cart_item($cart_item_key_old);
wp_die();
Note: If you want to submit cart form instead of page refreshes after running the above ajax, the quantity of item is likely to be overridden by the set_quantity method when woocommerce update_cart. In this case you just need to return new_quantity and change the input value by js before submitting the form.
Full code:
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
...
?>
<!-- place it anywhere within the foreach -->
<div class="box-type-field">
<select class="box-type" name="box-type" cart_item_key="<?php echo $cart_item_key ?>">
<option <?php echo $cart_item['box-type']=='boxes'?"selected":""; ?> value="boxes"><?php _e( 'Boxes', 'woocommerce' ); ?></option>
<option <?php echo $cart_item['box-type']=='bags'?"selected":""; ?> value="bags"><?php _e( 'Bags', 'woocommerce' ); ?></option>
</select>
</div>
<?php
...
}
AJAX:
$('.box-type-field .box-type').live('change', function () {
var cartItemKey = $(this).attr("cart_item_key");
var boxType = $(this).val();
$.ajax({
type : "post",
url : '<?php echo admin_url('admin-ajax.php');?>',
datatype: 'json',
data : {
action : "update_cart_boxtype",
cart_item_key : cartItemKey,
box_type : boxType,
},
success: function(cartItem) {
cartItemKey = cartItem[0];
cartItemQty = cartItem[1];
if (cartItem) $('input[name="cart['+cartItemKey+'][qty]"]').val(cartItemQty); // update quantity
$('.woocommerce-cart-form button[type="submit"]').click(); // submit form
}
})
})
PHP:
add_action( 'wp_ajax_update_cart_boxtype', 'update_cart_boxtype_init' );
add_action( 'wp_ajax_nopriv_update_cart_boxtype', 'update_cart_boxtype_init' );
function update_cart_boxtype_init() {
if ( ! WC()->cart->is_empty() ) {
$cart_item_key = (isset($_POST['cart_item_key']))?$_POST['cart_item_key'] : '';
$cart_item = WC()->cart->cart_contents[ $cart_item_key ];
$box_type = (isset($_POST['box_type']))?$_POST['box_type'] : '';
$cart_updated = false;
$cart_item_key_new = WC()->cart->generate_cart_id( $cart_item['product_id'], $cart_item['variation_id'], $cart_item['variation'], ['box-type'=>$box_type] );
$found = WC()->cart->find_product_in_cart( $cart_item_key_new );
if ($found != '') {
$new_qty = $cart_item['quantity'] + WC()->cart->cart_contents[ $cart_item_key_new ]['quantity'];
WC()->cart->remove_cart_item($cart_item_key);
wp_send_json_success([$cart_item_key_new, $new_qty]);
} else {
WC()->cart->add_to_cart($cart_item['product_id'], $cart_item['quantity'], $cart_item['variation_id'], $cart_item['variation'], ['box-type' => $box_type]);
$cart_updated = true;
WC()->cart->remove_cart_item($cart_item_key);
wp_send_json_success(false);
}
}
wp_die();
}
Step 1: Add Data in a Custom Session, on ‘Add to Cart’ Button Click
For those of you who have worked with WooCommerce might know that on the click of the ‘Add to Cart’ button the product page gets refreshed and the user data is lost. Hence, we should add the custom data from our product page to a custom session created using Ajax. This code is invoked before the WooCommerce session is created.
<?php
add_action('wp_ajax_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
add_action('wp_ajax_nopriv_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
function wdm_add_user_custom_data_options_callback()
{
//Custom data - Sent Via AJAX post method
$product_id = $_POST['id']; //This is product ID
$user_custom_data_values = $_POST['user_data']; //This is User custom value sent via AJAX
session_start();
$_SESSION['wdm_user_custom_data'] = $user_custom_data_values;
die();
}
Step 2: Add Custom Data in WooCommerce Session
At this step, the WooCommerce session has been created and is now available for us to add our custom data. We use the following code to add the custom data from the session we have created into the WooCommerce session. At this step, our session is also unset since the data in it has been captured and it is not needed anymore.
add_filter('woocommerce_add_cart_item_data','wdm_add_item_data',1,2);
if(!function_exists('wdm_add_item_data'))
{
function wdm_add_item_data($cart_item_data,$product_id)
{
/*Here, We are adding item in WooCommerce session with, wdm_user_custom_data_value name*/
global $woocommerce;
session_start();
if (isset($_SESSION['wdm_user_custom_data'])) {
$option = $_SESSION['wdm_user_custom_data'];
$new_value = array('wdm_user_custom_data_value' => $option);
}
if(empty($option))
return $cart_item_data;
else
{
if(empty($cart_item_data))
return $new_value;
else
return array_merge($cart_item_data,$new_value);
}
unset($_SESSION['wdm_user_custom_data']);
//Unset our custom session variable, as it is no longer needed.
}
}
Step 3: Extract Custom Data from WooCommerce Session and Insert it into Cart Object
At this stage, we have default product details along with the custom data in the WooCommerce session. The default data gets added to the cart object owing to the functionality provided by the plugin. However, we need to explicitly extract the custom data from the WooCommerce session and insert it into the cart object. This can be implemented with the following code.
add_filter('woocommerce_get_cart_item_from_session', 'wdm_get_cart_items_from_session', 1, 3 );
if(!function_exists('wdm_get_cart_items_from_session'))
{
function wdm_get_cart_items_from_session($item,$values,$key)
{
if (array_key_exists( 'wdm_user_custom_data_value', $values ) )
{
$item['wdm_user_custom_data_value'] = $values['wdm_user_custom_data_value'];
}
return $item;
}
}
Step 4: Display User Custom Data on Cart and Checkout page
Now that we have our custom data in the cart object all we need to do now is to display this data in the Cart and the Checkout page. This is how your cart page should look after the custom data has been added from the WooCommerce session to your Cart.
My-Cart-Page
I would like to add rel="nofollow" and target="_blank" for all external links in my Wordpress posts and pages permanently. I am aware, that there are plugins, which do the same, but as soon as they get disabled, all changes will be reversed and the articles are the same as from the beginning.
I do not know how to differ between internal or external links nor how to check if there is already a rel="nofollow" or target="_blank" attribute.
I guess the best way of doing this would be using PHP instead of MySQL. I already searched the web for guides, tutorials or plugins, without success.
May someone help me? I appreciate your support.
I have got a solution for applying nofollow to all existing and new external links.
Copy the code into your functions.php of your activated theme
function add_nofollow_content($content) {
$content = preg_replace_callback('/]*href=["|\']([^"|\']*)["|\'][^>]*>([^<]*)<\/a>/i', function($m) {
if (strpos($m[1], "YOUR_DOMAIN_ADDRESS") === false)
return ''.$m[2].'';
else
return ''.$m[2].'';
}, $content);
return $content;
}
add_filter('the_content', 'add_nofollow_content');
You can also call the function home_url() instead of "YOUR_DOMAIN_ADDRESS" in the space provided to avoid hard coding of the domain name.
The code is tested and it works.
Hope this one helps.
You can use following snippet:
http://wpsnipp.com/index.php/functions-php/nofollow-external-links-only-the_content-and-the_excerpt/
This great little snippet that will add rel=”nofollow” to external
links within both the_content and the_excerpt. Add this snippet to the
functions.php of your wordpress theme to enable nofollow external
links.
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;
}
I think adding rel"nofollow" and target="_blank" to outgoing links permanently is more work than it can be shown here. You will have to rebuild the functions of plugins like External Links so that even links in your wp_nav_menus can be rewritten.
I have a suggestion that adds the desired attributes via JavaScript when the page is loaded. You can add this script directly to your theme header or you can keep it in a seperate file enqueing the script in your themes' functions.php:
$(document).ready(function () {
$( "a:not(a[href^='http://www.your-domain-name.com'],a[href^='javascript'],a[href^='#'])" ).attr({
rel: "nofollow",
target: "_blank"
});
});
I took the answer of #rony-samuel and adjusted few things you might find useful.
Use the built-in make_clickable function to wrap links automatically. (E.g. useful when creating posts via API) - then check if the user has added additional classes to a link, (like a button to have a different styling) – we don't want to overwrite that, so just return the given markup with $m[0].
Last thing is the regex. In combination with make_clickable it would output <a <a href... so a link in a link. I corrected the regex to avoid that.
// Auto warp links within content
add_filter( 'the_content', 'make_clickable' );
// Add target blank and nofollow to external links
// Do not overwrite links that probably have been placed manually in the content
// and contain classes like "button" or whatever etc. Since they were placed manually
// with additional styling, the editor can add target="_blank" manually as well if needed.
function external_links ($content) {
$content = preg_replace_callback(
'/<a[^>]*href=["|\']([^"|\']*)["|\'][^>]*>([^<]*)<\/a>/i',
function($m) {
$hasClass = (bool) preg_match('/class="[^"]*[^"]*"/', $m[0]);
if (strpos($m[1], home_url()) === false && $hasClass === false)
return ''.$m[2].'';
else
return $m[0];
},
$content);
return $content;
}
// set a very low priority to ensure,
// all the content and shortcode things has been completed already
add_filter('the_content', 'external_links', 999);