WordPress Top Codes
WordPress Custom Excerpt Length and Read More Link
WordPress cuts the automatic excerpt at 55 words and adds […]. This snippet changes both: it makes the excerpt 15 words long and adds a “Read more” link to the post. Put it in the functions.php of your theme, or in a plugin.
// 1. Excerpt length in words
add_filter( 'excerpt_length', function () {
return 15;
}, 999 ); // a high priority, so it wins over the theme
// 2. The text after the excerpt
add_filter( 'excerpt_more', function () {
return '… <a class="read-more" href="' . esc_url( get_permalink() ) . '">Read more</a>';
} );
Test it
We make a post with a long text and ask for its excerpt:
$id = wp_insert_post( array(
'post_title' => 'A long post',
'post_status' => 'publish',
'post_content' => 'WordPress is a free and open source content management system. It is written in PHP and uses a database. You can build a blog, a business site or an online store with it, and extend it with plugins and themes.',
) );
global $post;
$post = get_post( $id );
setup_postdata( $post );
echo get_the_excerpt( $post );
wp_reset_postdata();
WordPress is a free and open source content management system. It is written in PHP… <a class="read-more" href="http://example.com/a-long-post/">Read more</a>
The excerpt has 15 words and the link. Use the_excerpt() in your template to print it. If a post has a hand written excerpt, WordPress uses it as it is, and the filters do not change it.
WordPress Login Redirect by User Role
By default every user goes to the dashboard after the login. With the login_redirect filter you can send them to another place, based on the role. In this code administrators and editors go to the dashboard, and everyone else goes to the home page.
add_filter( 'login_redirect', function ( $redirect_to, $requested_redirect_to, $user ) {
// $user can be a WP_Error when the login fails
if ( ! ( $user instanceof WP_User ) ) {
return $redirect_to;
}
if ( user_can( $user, 'edit_others_posts' ) ) {
return admin_url(); // administrators and editors
}
return home_url( '/' ); // authors, contributors, subscribers
}, 10, 3 );
Test it
We create three users and ask the filter where each of them goes:
foreach ( array( 'admin', 'editor1', 'reader1' ) as $login ) {
$user = get_user_by( 'login', $login );
echo str_pad( $login . ' (' . $user->roles[0] . ')', 28 ) . ' => ' . apply_filters( 'login_redirect', admin_url(), '', $user ) . "\n";
}
admin (administrator) => http://example.com/wp-admin/
editor1 (editor) => http://example.com/wp-admin/
reader1 (subscriber) => http://example.com/
The filter gets three values: the address WordPress wants to use, the address the user asked for, and the user object (or an error). Return the new address, always with a full URL.
Clean Up the WordPress wp_head: Remove Generator, Emoji and Extra Links
WordPress prints some extra tags in the <head> of every page: the version number (generator), emoji scripts, an RSD link and a shortlink. Many sites do not need them. Removing them makes the HTML smaller, and hides the WordPress version from simple scanners.
add_action( 'init', function () {
remove_action( 'wp_head', 'wp_generator' ); // <meta name="generator">
remove_action( 'wp_head', 'rsd_link' ); // Really Simple Discovery
remove_action( 'wp_head', 'wlwmanifest_link' ); // Windows Live Writer
remove_action( 'wp_head', 'wp_shortlink_wp_head' ); // <link rel="shortlink">
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
} );
Test it
$ curl -s http://example.com/ | grep -c -E 'name="generator"|rel="EditURI"|rel="shortlink"|wp-emoji|emoji'
9
$ curl -s http://example.com/ | grep -c -E 'name="generator"|rel="EditURI"|rel="shortlink"|wp-emoji|emoji'
0
The first count (before the snippet) is higher than zero, and after we added the code the extra lines are gone. The count can be different in your theme, because some themes remove or add these tags by themselves.
A note
- Hiding the version is a very small help for security. Real security needs updates and strong passwords.
- Removing the emoji scripts means emoji will use the emoji of the browser or system. This is fine for the modern browsers.
- Do not remove
wp_head()itself. Plugins and the theme need it.
WordPress Related Posts by Category Function
This function finds posts from the same categories as the current post, and returns an HTML list. Use it in single.php, or inside a filter on the_content.
function tc_related_posts( $post_id = null, $limit = 3 ) {
$post_id = $post_id ? $post_id : get_the_ID();
$cats = wp_get_post_categories( $post_id );
if ( empty( $cats ) ) {
return '';
}
$query = new WP_Query( array(
'category__in' => $cats,
'post__not_in' => array( $post_id ),
'posts_per_page' => $limit,
'ignore_sticky_posts' => true,
'no_found_rows' => true, // faster: we do not need pagination
) );
if ( ! $query->have_posts() ) {
return '';
}
$html = '<ul class="related-posts">';
while ( $query->have_posts() ) {
$query->the_post();
$html .= '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></li>';
}
wp_reset_postdata();
return $html . '</ul>';
}
Test it
We create two categories and four posts, and ask for the related posts of the first one:
function tc_related_posts( $post_id = null, $limit = 3 ) {
$post_id = $post_id ? $post_id : get_the_ID();
$cats = wp_get_post_categories( $post_id );
if ( empty( $cats ) ) {
return '';
}
$query = new WP_Query( array(
'category__in' => $cats,
'post__not_in' => array( $post_id ),
'posts_per_page' => $limit,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
) );
if ( ! $query->have_posts() ) {
return '';
}
$html = '<ul class="related-posts">';
while ( $query->have_posts() ) {
$query->the_post();
$html .= '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></li>';
}
wp_reset_postdata();
return $html . '</ul>';
}
echo "Related to post 5 (Learn hooks):\n" . tc_related_posts( 5, 3 ) . "\n\n";
echo "Related to post 8 (Site news, only post in News): [" . tc_related_posts( 8, 3 ) . ']';
Related to post 5 (Learn hooks):
<ul class="related-posts"><li><a href="http://example.com/learn-the-loop/">Learn the Loop</a></li><li><a href="http://example.com/learn-wp_query/">Learn WP_Query</a></li></ul>
Related to post 8 (Site news, only post in News): []
The post itself is not in its own list, and a post with no other post in its category gets an empty string. Print the result in your template with echo tc_related_posts();.
Add a Custom Dashboard Widget in WordPress
You can add your own widget to the admin dashboard, for example to show a message, a link list or a site statistic. Use wp_add_dashboard_widget() on the wp_dashboard_setup hook.
<?php
add_action( 'wp_dashboard_setup', function () {
wp_add_dashboard_widget(
'tc_site_stats', // widget ID
'Site Statistics', // title
'tc_site_stats_content' // function which prints the content
);
} );
function tc_site_stats_content() {
$counts = wp_count_posts();
printf(
'<p>Published posts: <strong>%d</strong></p><p>Drafts: <strong>%d</strong></p>',
(int) $counts->publish,
(int) $counts->draft
);
}
Test it
We log in as the admin (like a browser) and read the dashboard page:
$ curl -s -b cookies.txt http://example.com/wp-admin/index.php | grep -o -E 'id="tc_site_stats"|Site Statistics|<p>Published posts: <strong>[0-9]*</strong></p><p>Drafts: <strong>[0-9]*</strong></p>' | sort -u
<p>Published posts: <strong>1</strong></p><p>Drafts: <strong>1</strong></p>
Site Statistics
id="tc_site_stats"
The widget is on the dashboard with the numbers. Show it only to some users by checking current_user_can( 'manage_options' ) before you call wp_add_dashboard_widget().
Add a Custom Column to the WordPress Posts List
You can add your own column to the list of posts in the dashboard. This example adds a Words column with the number of words of each post. It needs two hooks: one to add the column title, and one to print the value for each row.
<?php
// 1. Add the column after the title
add_filter( 'manage_posts_columns', function ( $columns ) {
$new = array();
foreach ( $columns as $key => $label ) {
$new[ $key ] = $label;
if ( 'title' === $key ) {
$new['tc_words'] = 'Words';
}
}
return $new;
} );
// 2. Print the value of each row
add_action( 'manage_posts_custom_column', function ( $column, $post_id ) {
if ( 'tc_words' === $column ) {
echo (int) str_word_count( wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ) );
}
}, 10, 2 );
Test it
$ curl -s -b cookies.txt "http://example.com/wp-admin/edit.php" | grep -o -E 'data-colname="Words">[0-9]+' | sed 's/.*>//'
5
15
$ wp eval 'echo str_word_count( "One two three four five" );'
5
The list shows the values of the new column: 5 for our new post and 15 for the sample post “Hello world!”. To make the column sortable, add the filter manage_edit-post_sortable_columns and handle the order in pre_get_posts. For a custom post type use manage_{post_type}_posts_columns.