WordPress Plugins
Write a plugin which adds a text to the footer
Description: Create a plugin called Footer Credit that prints Built with WordPress at the end of every page.
Hint:
- The plugin needs a header comment with
Plugin Name. - Use
add_action( 'wp_footer', ... ).
Solution
<?php
/**
* Plugin Name: Footer Credit
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'wp_footer', function () {
echo '<p class="credit">Built with WordPress</p>';
} );
$ wp plugin activate tc-footer-credit
Plugin 'tc-footer-credit' activated.
Success: Activated 1 of 1 plugins.
$ wp plugin list –name=tc-footer-credit –fields=name,status
name status
tc-footer-credit active
$ curl -s http://example.com/ | grep -o '<p class="credit">[^<]*</p>'
<p class="credit">Built with WordPress</p>
How it works
- WordPress reads the header comment and shows the plugin in the list.
- After activation, the file is loaded on every request, and the function waits for the
wp_footeraction.
Create a [today] shortcode with a date format
Description: Make a shortcode [today] which prints the date in the format d F Y. It should also accept an attribute: [today format="Y-m-d"].
Hint:
- Use
add_shortcode()andshortcode_atts(). - Return the text, do not echo it. Use
wp_date()for the date.
Solution
add_shortcode( 'today', function ( $atts ) {
$a = shortcode_atts( array( 'format' => 'd F Y' ), $atts, 'today' );
return esc_html( wp_date( $a['format'] ) );
} );
echo 'Default: ' . ( strlen( do_shortcode( '[today]' ) ) > 0 ? 'works' : 'empty' ) . "\n";
echo 'Format Y-m-d has the right shape: ' . ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', do_shortcode( '[today format="Y-m-d"]' ) ) ? 'yes' : 'no' );
Default: works
Format Y-m-d has the right shape: yes
How it works
shortcode_atts()merges the given attributes with the defaults.wp_date()prints the date in the timezone of the site.- The output changes every day, so the test checks its shape, not the exact text.
Read Shortcode API.
Register a Movie custom post type
Description: Register a post type movie with the label Movies, an archive at /movies/ and REST support. Add a movie Inception and check its URL.
Hint:
- Use
register_post_type()on theinithook. - Flush the rewrite rules once after registering the type.
Solution
<?php
/**
* Plugin Name: Movies
*/
add_action( 'init', function () {
register_post_type( 'movie', array(
'label' => 'Movies',
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'movies' ),
'show_in_rest' => true,
'supports' => array( 'title', 'editor', 'thumbnail' ),
) );
} );
$ wp plugin activate tc-movies
Plugin 'tc-movies' activated.
Success: Activated 1 of 1 plugins.
$ wp rewrite flush
Success: Rewrite rules flushed.
$ wp post create –post_type=movie –post_title="Inception" –post_status=publish –porcelain
5
$ wp eval 'echo get_permalink( 5 );'
http://example.com/movies/inception/
$ curl -s -o /dev/null -w "archive: %{http_code}\n" http://example.com/movies/
archive: 200
$ curl -s -o /dev/null -w "single: %{http_code}\n" http://example.com/movies/inception/
single: 200
How it works
- The type is saved in
wp_postswithpost_type = movie. has_archivegives the list page, andrewritesets the URL base.- Without the flush, the new URLs can show a 404 error.
Read Custom Post Types.
Add a hierarchical Level taxonomy to a Course post type
Description: Register a post type course and a hierarchical taxonomy level. Add the terms Beginner and Advanced. Create a course WordPress Basics in the level Beginner, and list the courses of that level with WP_Query.
Hint:
register_taxonomy( 'level', 'course', array( 'hierarchical' => true ) ).- Use a
tax_queryfor the list.
Solution
<?php
/**
* Plugin Name: Courses
*/
add_action( 'init', function () {
register_post_type( 'course', array( 'label' => 'Courses', 'public' => true, 'show_in_rest' => true ) );
register_taxonomy( 'level', 'course', array( 'label' => 'Levels', 'hierarchical' => true, 'show_in_rest' => true ) );
} );
$ wp plugin activate tc-courses
Plugin 'tc-courses' activated.
Success: Activated 1 of 1 plugins.
$ wp term create level "Beginner" –slug=beginner –porcelain
2
$ wp term create level "Advanced" –slug=advanced –porcelain
3
$ wp post create –post_type=course –post_title="WordPress Basics" –post_status=publish –porcelain
5
$ wp post create –post_type=course –post_title="Plugin Security" –post_status=publish –porcelain
6
$ wp post term add 5 level beginner
Success: Added term.
$ wp post term add 6 level advanced
Success: Added term.
$q = new WP_Query( array(
'post_type' => 'course',
'tax_query' => array( array( 'taxonomy' => 'level', 'field' => 'slug', 'terms' => 'beginner' ) ),
) );
foreach ( $q->posts as $p ) {
echo $p->post_title . "\n";
}
WordPress Basics
How it works
- A taxonomy is registered for a post type, and its terms are connected to the posts through
wp_term_relationships. - The
tax_queryasks only for the posts which have the term beginner.
Read Custom Taxonomies.
Save a plugin setting with a sanitize callback
Description: Register an option site_slogan in a plugin, which cleans the value with sanitize_text_field. Save the value <b>Learn</b> fast and print what is stored.
Hint:
register_setting()withsanitize_callbackruns a filter every timeupdate_option()saves the option.
Solution
register_setting( 'my_group', 'site_slogan', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
) );
update_option( 'site_slogan', '<b>Learn</b> fast' );
var_dump( get_option( 'site_slogan' ) );
string(10) "Learn fast"
How it works
register_setting()connects the sanitize function to the option name.- The tags are removed before the value goes to the database, so the stored value is plain text.
- In a real plugin you call
register_setting()on theadmin_inithook.
Read Plugin Settings Page.
Create a custom table and insert a row with $wpdb
Description: Create a table wp_tc_visits with the columns id, page and hits. Insert the row (home, 10), and read it back with a prepared query.
Hint:
- Use
$wpdb->prefix,dbDelta(),$wpdb->insert()and$wpdb->prepare().
Solution
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$table = $wpdb->prefix . 'tc_visits';
$charset = $wpdb->get_charset_collate();
dbDelta( "CREATE TABLE $table (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
page varchar(100) NOT NULL,
hits int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset;" );
$wpdb->insert( $table, array( 'page' => 'home', 'hits' => 10 ), array( '%s', '%d' ) );
$row = $wpdb->get_row( $wpdb->prepare( "SELECT page, hits FROM $table WHERE page = %s", 'home' ) );
echo $row->page . ' => ' . $row->hits;
home => 10
How it works
dbDelta()creates the table (and it would update it if it existed).insert()writes the row and prepares the values with the formats%sand%d.prepare()protects the select query from SQL injection.
Read WordPress $wpdb.