WordPress is probably the most famous Content Management System with many great features such as widgets or plugins. I’d like to show you, how to use WordPress together with MapTiler and provide your users the option to display MapTiler maps in any widget area on their sites.
Step 1: Create your Widget Plugin
First things first, we need to create a plugin that will be the base for our widget code. The best place where to start is the official Plugin Handbook by WordPress.
We will start with creating a folder with the plugin and also the main plugin file where will be our code.
wordpress $ cd wp-content wp-content $ cd plugins plugins $ mkdir maptiler plugins $ cd maptiler maptiler $ vi maptiler.php
In the example above, vi
is the name of the text editor. Use whichever editor that is comfortable for you.
Header
According to the WordPress Handbook, there are some header requirements. This is how the header should look like:
/** * Plugin Name: My Basics Plugin * Plugin URI: https://example.com/plugins/the-basics/ * Description: Handle the basics with this plugin. * Version: 1.10.3 * Requires at least: 5.2 * Requires PHP: 7.2 * Author: John Smith * Author URI: https://author.example.com/ * License: GPL v2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Update URI: https://example.com/my-plugin/ * Text Domain: my-basics-plugin * Domain Path: /languages */
So we will follow instructions and modify the comments as we need:
/** * Plugin Name: MapTiler maps in widget * Plugin URI: https://maptiler.com/docs/ * Description: Use the MapTiler maps in your widget area * Version: 1.0.0 * Requires at least: 5.2 * Requires PHP: 7.2 * Author: Kamil Baranek * Author URI: https://maptiler.com/ * License: GPL v2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html */
Widget skeleton
We are now going to create the widget itself. This widget will be a PHP class extending the core WordPress class WP_Widget. Basically, our widget will be defined this way:
// The widget class class MapTiler extends WP_Widget { // Main constructor public function __construct() { /* ... */ } // The widget form (for the backend ) public function form( $instance ) { /* ... */ } // Update widget settings public function update( $new_instance, $old_instance ) { /* ... */ } // Display the widget public function widget( $args, $instance ) { /* ... */ } } // Register the widget function register_maptiler_widget() { register_widget( 'MapTiler' ); } add_action( 'widgets_init', 'register_maptiler_widget' );
This code gives WordPress all the information the system needs to be able to use the widget:
-
The constructor, to initiate the widget
-
The form() function to create the widget form in the administration
-
The update() function, to save widget data during edition
-
And the widget() function to display the widget content on the front-end
Constructor
// Widget constructor public function __construct() { parent::__construct( 'maptiler_widget', __( 'Maps by MapTiler', 'text_domain' ), array( 'customize_selective_refresh' => true, ) ); }
‘customize_selective_refresh’ parameter allows the widget to be refreshed under Appearance > Customize when editing the widget so instead of refreshing the entire page only the widget is refreshed when making changes.
The form() function
The main responsibility of the form() function is to output the options form on admin. Let's take a breath now and try to figure out, what we will need as options for our plugin. Of course, there should be some TITLE of the widget. Also, if we will need to display the map, we should know where is the location we want to focus on (coordinates). This brings us to other parameters, the longitude as LON and the latitude as LAT. It would be also nice if we will be able to ZOOM the map based on our needing. I can also imagine some of you would like to provide the possibility of showing map CONTROLS. And finally, if there will be a map in the widget area, there might be some additional info below the map in separate TEXTAREA.
So we have some defaults we need to use:
// Set widget defaults $defaults = array( 'title' => '', 'lat' => '', 'lon' => '', 'textarea' => '', 'controls' => '', 'zoom' => '', ); // Parse current settings with defaults extract( wp_parse_args( ( array ) $instance, $defaults ) ); ?>
And now lets complete the form of our widget:
<?php // Widget TITLE ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /> </p> <?php // LAT Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'lat' ) ); ?>"><?php _e( 'Lat:', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'lat' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'lat' ) ); ?>" type="text" value="<?php echo esc_attr( $lat ); ?>" /> </p> <?php // LON Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'lon' ) ); ?>"><?php _e( 'Lon:', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'lon' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'lon' ) ); ?>" type="text" value="<?php echo esc_attr( $lon ); ?>" /> </p> <?php // CONTROLS ?> <p> <input id="<?php echo esc_attr( $this->get_field_id( 'controls' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'controls' ) ); ?>" type="checkbox" value="1" <?php checked( '1', $controls ); ?> /> <label for="<?php echo esc_attr( $this->get_field_id( 'controls' ) ); ?>"><?php _e( 'Display map navigation controls', 'text_domain' ); ?></label> </p> <?php // ZOOM ?> <p> <label for="<?php echo $this->get_field_id( 'zoom' ); ?>"><?php _e( 'Zoom', 'text_domain' ); ?></label> <select name="<?php echo $this->get_field_name( 'zoom' ); ?>" id="<?php echo $this->get_field_id( 'zoom' ); ?>" class="widefat"> <?php // Your options array $options = array( '' => __( 'Select zoom level', 'text_domain' ), '0' => __( '0 (Whole world)', 'text_domain' ), '1' => __( '1', 'text_domain' ), '2' => __( '2', 'text_domain' ), '3' => __( '3 (States)', 'text_domain' ), '4' => __( '4', 'text_domain' ), '5' => __( '5', 'text_domain' ), '6' => __( '6 (Big Cities)', 'text_domain' ), '7' => __( '7', 'text_domain' ), '8' => __( '8', 'text_domain' ), '9' => __( '9 (Region)', 'text_domain' ), '10' => __( '10', 'text_domain' ), '11' => __( '11', 'text_domain' ), '12' => __( '12 (Main streets)', 'text_domain' ), '13' => __( '13', 'text_domain' ), '14' => __( '14 (Detail)', 'text_domain' ), '15' => __( '15', 'text_domain' ), '16' => __( '16', 'text_domain' ), '17' => __( '17 (Max zoom in)', 'text_domain' ), ); // Loop through options and add each one to the select dropdown foreach ( $options as $key => $name ) { echo '<option value="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '" '. selected( $zoom, $key, false ) . '>'. $name . '</option>'; } ?> </select> </p> <?php // Textarea INFO Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>"><?php _e( 'Aditional info:', 'text_domain' ); ?></label> <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'textarea' ) ); ?>"><?php echo wp_kses_post( $textarea ); ?></textarea> </p>
And this is how the form looks like in WordPress admin:
But there are still some steps we need to make to see the map on the front end.
The update() function
This function will help us to update whatever we enter in the widget form. Basically, we will just check every setting and if it’s not empty, we will store the value in the database.
// Update widget settings public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = isset( $new_instance['title'] ) ? wp_strip_all_tags( $new_instance['title'] ) : ''; $instance['lat'] = isset( $new_instance['lat'] ) ? wp_strip_all_tags( $new_instance['lat'] ) : ''; $instance['lon'] = isset( $new_instance['lon'] ) ? wp_strip_all_tags( $new_instance['lon'] ) : ''; $instance['zoom'] = isset( $new_instance['zoom'] ) ? wp_strip_all_tags( $new_instance['zoom'] ) : ''; $instance['textarea'] = isset( $new_instance['textarea'] ) ? wp_kses_post( $new_instance['textarea'] ) : ''; $instance['controls'] = isset( $new_instance['controls'] ) ? 1 : false; return $instance; }
The widget() function
The widget function is the one that will display our widget on the front end. First, we will get the settings from the database and display them.
// Display the widget public function widget( $args, $instance ) { extract( $args ); // Check the widget options $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'] ) : ''; $lat = isset( $instance['lat'] ) ? $instance['lat'] : ''; $lon = isset( $instance['lon'] ) ? $instance['lon'] : ''; $textarea = isset( $instance['textarea'] ) ?$instance['textarea'] : ''; $zoom = isset( $instance['zoom'] ) ? $instance['zoom'] : ''; $controls = ! empty( $instance['controls'] ) ? $instance['controls'] : false; $w_id = isset( $args['widget_id'] ) ? $args['widget_id'] : ''; // WordPress core before_widget hook (always include ) echo $before_widget; // Display the widget echo '<div class="widget widget-text wp_widget_plugin_box">'; // Display widget title if defined if ( $title ) { echo $before_title . $title . $after_title; } // Display the map (container) echo '<div id="map_'.$w_id.'" style="width: 400px; height: 300px;"></div>'; // Set LAT field if ( $lat ) { echo '<input type="hidden" id="lat_map_'.$w_id.'" value="'.$lat.'">'; } // Set LON field if ( $lon ) { echo '<input type="hidden" id="lon_map_'.$w_id.'" value="'.$lon.'">'; } // Set ZOOM field if ( $zoom == 0 || ($zoom > 0 && $zoom < 18)) { echo '<input type="hidden" id="zoom_map_'.$w_id.'" value="'.$zoom.'">'; } else { echo '<input type="hidden" id="zoom_map_'.$w_id.'" value="7">'; //default } // Display textarea field if ( $textarea ) { echo '<p>' . $textarea . '</p>'; } // Display something if checkbox is true if ( $controls ) { echo '<input type="hidden" id="controls_map_'.$w_id.'" value="1">'; } else { echo '<input type="hidden" id="controls_map_'.$w_id.'" value="0">'; //default } echo '</div>'; // WordPress core after_widget hook (always include ) echo $after_widget; }
So far so good, widget is in its place and if not, just make sure you already placed it by moving the widget to the footer in the WordPress admin area (either under Appearance > Widgets or Appearance > Customize > Widgets). But MAP is still missing, right?
So let's create another file, this time javascript file in our maptiler plugin folder:
maptiler $ vi maptiler_maps.js
MapTiler Maps
We are in maptiler_maps.js file and this is where the magic happen:
(function( $ ) { 'use strict'; // 1. DO the stuff when DOM ready $(function() { console.log('DOM Ready and plugin js works'); // Go and find every widget where the map is expected $('div[id^="map_maptiler_widget"]').each(function( index ) { // Get the settings we stored by widget var lat = $("#lat_" + this.id).val(); var lon = $("#lon_" + this.id).val(); var zoom = $("#zoom_" + this.id).val(); var ctrl = $("#controls_" + this.id).val(); // Just in case, give me defaults if there is no location saved if (typeof lat === "undefined" || typeof lon === "undefined") { lat = 55.665957; lon = 12.550343; } // I am interested in ZOOM between 0 ~ 17 if (zoom && (zoom == 0 || (zoom > 0 && zoom <18))) { //good } else { zoom = 7; } // Create map using the settings var map = new maplibregl.Map({ container: this.id, style: 'https://api.maptiler.com/maps/streets/style.json?key=slEr7lXnTotdWgNA0oLf', // stylesheet location center: [lon, lat], // starting position [lng, lat] zoom: zoom, // starting zoom attributionControl: true }); // Display the attribution > mandatory according to policy map.addControl(new maplibregl.AttributionControl({ compact: false }), 'bottom-left'); // If there is a complete location (LAT/LON), add marker to map if ( lat && lon ) { var marker = new maplibregl.Marker() .setLngLat([lon, lat]) .addTo(map); } // If maps controls should be shown, show them if (ctrl == 1) { // Add zoom and rotation controls to the map. map.addControl(new maplibregl.NavigationControl()); } }); }); })( jQuery );
Now, the only final step is to include this script together with maplibre library in widget main file:
// Include file with MapTiler maps function add_maptiler_maps() { // Styles wp_enqueue_style( 'maplibre-style', 'https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.css', null, '1.0.0', 'all' ); // Scripts wp_enqueue_script('maplibre-scripts', 'https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.js', null , '1.0.0', false ); wp_enqueue_script( 'script', plugin_dir_url( __FILE__ ) . '/maptiler_map.js', array ( 'jquery' ), 1.1, true); } add_action( 'wp_enqueue_scripts', 'add_maptiler_maps' );
So now we have two files:
maptiler.php
<?php /** * Plugin Name: MapTiler maps in widget * Plugin URI: https://maptiler.com/docs/ * Description: Use the MapTiler maps in your widget area * Version: 1.0.0 * Requires at least: 5.2 * Requires PHP: 7.2 * Author: Kamil Baranek * Author URI: https://maptiler.com/ * License: GPL v2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html */ // The widget class class MapTiler extends WP_Widget { // Widget constructor public function __construct() { parent::__construct( 'maptiler_widget', __( 'Maps by MapTiler', 'text_domain' ), array( 'customize_selective_refresh' => true, ) ); } // The widget form (for the backend ) public function form( $instance ) { // Set widget defaults $defaults = array( 'title' => '', 'lat' => '', 'lon' => '', 'textarea' => '', 'controls' => '', 'zoom' => '', ); // Parse current settings with defaults extract( wp_parse_args( ( array ) $instance, $defaults ) ); ?> <?php // Widget TITLE ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /> </p> <?php // LAT Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'lat' ) ); ?>"><?php _e( 'Lat:', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'lat' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'lat' ) ); ?>" type="text" value="<?php echo esc_attr( $lat ); ?>" /> </p> <?php // LON Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'lon' ) ); ?>"><?php _e( 'Lon:', 'text_domain' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'lon' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'lon' ) ); ?>" type="text" value="<?php echo esc_attr( $lon ); ?>" /> </p> <?php // CONTROLS ?> <p> <input id="<?php echo esc_attr( $this->get_field_id( 'controls' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'controls' ) ); ?>" type="checkbox" value="1" <?php checked( '1', $controls ); ?> /> <label for="<?php echo esc_attr( $this->get_field_id( 'controls' ) ); ?>"><?php _e( 'Display map navigation controls', 'text_domain' ); ?></label> </p> <?php // ZOOM ?> <p> <label for="<?php echo $this->get_field_id( 'zoom' ); ?>"><?php _e( 'Zoom', 'text_domain' ); ?></label> <select name="<?php echo $this->get_field_name( 'zoom' ); ?>" id="<?php echo $this->get_field_id( 'zoom' ); ?>" class="widefat"> <?php // Your options array $options = array( '' => __( 'Select zoom level', 'text_domain' ), '0' => __( '0 (Whole world)', 'text_domain' ), '1' => __( '1', 'text_domain' ), '2' => __( '2', 'text_domain' ), '3' => __( '3 (States)', 'text_domain' ), '4' => __( '4', 'text_domain' ), '5' => __( '5', 'text_domain' ), '6' => __( '6 (Big Cities)', 'text_domain' ), '7' => __( '7', 'text_domain' ), '8' => __( '8', 'text_domain' ), '9' => __( '9 (Region)', 'text_domain' ), '10' => __( '10', 'text_domain' ), '11' => __( '11', 'text_domain' ), '12' => __( '12 (Main streets)', 'text_domain' ), '13' => __( '13', 'text_domain' ), '14' => __( '14 (Detail)', 'text_domain' ), '15' => __( '15', 'text_domain' ), '16' => __( '16', 'text_domain' ), '17' => __( '17 (Max zoom in)', 'text_domain' ), ); // Loop through options and add each one to the select dropdown foreach ( $options as $key => $name ) { echo '<option value="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '" '. selected( $zoom, $key, false ) . '>'. $name . '</option>'; } ?> </select> </p> <?php // Textarea INFO Field ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>"><?php _e( 'Aditional info:', 'text_domain' ); ?></label> <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'textarea' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'textarea' ) ); ?>"><?php echo wp_kses_post( $textarea ); ?></textarea> </p> <?php } // Update widget settings public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = isset( $new_instance['title'] ) ? wp_strip_all_tags( $new_instance['title'] ) : ''; $instance['lat'] = isset( $new_instance['lat'] ) ? wp_strip_all_tags( $new_instance['lat'] ) : ''; $instance['lon'] = isset( $new_instance['lon'] ) ? wp_strip_all_tags( $new_instance['lon'] ) : ''; $instance['zoom'] = isset( $new_instance['zoom'] ) ? wp_strip_all_tags( $new_instance['zoom'] ) : ''; $instance['textarea'] = isset( $new_instance['textarea'] ) ? wp_kses_post( $new_instance['textarea'] ) : ''; $instance['controls'] = isset( $new_instance['controls'] ) ? 1 : false; return $instance; } // Display the widget public function widget( $args, $instance ) { extract( $args ); // Check the widget options $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'] ) : ''; $lat = isset( $instance['lat'] ) ? $instance['lat'] : ''; $lon = isset( $instance['lon'] ) ? $instance['lon'] : ''; $textarea = isset( $instance['textarea'] ) ?$instance['textarea'] : ''; $zoom = isset( $instance['zoom'] ) ? $instance['zoom'] : ''; $controls = ! empty( $instance['controls'] ) ? $instance['controls'] : false; $w_id = isset( $args['widget_id'] ) ? $args['widget_id'] : ''; // WordPress core before_widget hook (always include ) echo $before_widget; // Display the widget echo '<div class="widget widget-text wp_widget_plugin_box">'; // Display widget title if defined if ( $title ) { echo $before_title . $title . $after_title; } // Display the map (container) echo '<div id="map_'.$w_id.'" style="width: 400px; height: 300px;"></div>'; // Set LAT field if ( $lat ) { echo '<input type="hidden" id="lat_map_'.$w_id.'" value="'.$lat.'">'; } // Set LON field if ( $lon ) { echo '<input type="hidden" id="lon_map_'.$w_id.'" value="'.$lon.'">'; } // Set ZOOM field if ( $zoom == 0 || ($zoom > 0 && $zoom < 18)) { echo '<input type="hidden" id="zoom_map_'.$w_id.'" value="'.$zoom.'">'; } else { echo '<input type="hidden" id="zoom_map_'.$w_id.'" value="7">'; //default } // Display textarea field if ( $textarea ) { echo '<p>' . $textarea . '</p>'; } // Display something if checkbox is true if ( $controls ) { echo '<input type="hidden" id="controls_map_'.$w_id.'" value="1">'; } else { echo '<input type="hidden" id="controls_map_'.$w_id.'" value="0">'; //default } echo '</div>'; // WordPress core after_widget hook (always include ) echo $after_widget; } } // Include file with MapTiler maps function add_maptiler_maps() { // Styles wp_enqueue_style( 'maplibre-style', 'https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.css', null, '1.0.0', 'all' ); // Scripts wp_enqueue_script('maplibre-scripts', 'https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.js', null , '1.0.0', false ); wp_enqueue_script( 'script', plugin_dir_url( __FILE__ ) . '/maptiler_map.js', array ( 'jquery' ), 1.1, true); } add_action( 'wp_enqueue_scripts', 'add_maptiler_maps' ); // Register the widget function register_maptiler_widget() { register_widget( 'MapTiler' ); } add_action( 'widgets_init', 'register_maptiler_widget' );
maptiler_map.js
(function( $ ) { 'use strict'; // 1. DO the stuff when DOM ready $(function() { console.log('DOM Ready and plugin js works'); // Go and find every widget where the map is expected $('div[id^="map_maptiler_widget"]').each(function( index ) { // Get the settings we stored by widget var lat = $("#lat_" + this.id).val(); var lon = $("#lon_" + this.id).val(); var zoom = $("#zoom_" + this.id).val(); var ctrl = $("#controls_" + this.id).val(); // Just in case, give me defaults if there is no location saved if (typeof lat === "undefined" || typeof lon === "undefined") { lat = 55.665957; lon = 12.550343; } // I am interested in ZOOM between 0 ~ 17 if (zoom && (zoom == 0 || (zoom > 0 && zoom <18))) { //good } else { zoom = 7; } // Create map using the settings var map = new maplibregl.Map({ container: this.id, style: 'https://api.maptiler.com/maps/streets/style.json?key=slEr7lXnTotdWgNA0oLf', // stylesheet location center: [lon, lat], // starting position [lng, lat] zoom: zoom, // starting zoom attributionControl: false }); // Display the attribution > mandatory according to policy map.addControl(new maplibregl.AttributionControl({ compact: false }), 'bottom-left'); // If there is a complete location (LAT/LON), add marker to map if ( lat && lon ) { var marker = new maplibregl.Marker() .setLngLat([lon, lat]) .addTo(map); } // If maps controls should be shown, show them if (ctrl == 1) { // Add zoom and rotation controls to the map. map.addControl(new maplibregl.NavigationControl()); } }); }); })( jQuery );
The result might look like this (I used the basic Twenty Twenty-One theme included in WP installation)
Congratulation! Now you are using open-source maps and open-source CMS altogether.
Comments
0 comments
Please sign in to leave a comment.