Oke..menyambung bagian pertama, dari sebuah sumber yang telah dibangun akan diregister sebagai plugin wordpress. Langsung saja tanpa babibu…hehe, berikut bahasannya

Pustaka:

  1. PHP Simple HTML DOM Parsera HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way!

Kontent:

  1. Konfigurasi File dan Folder
  2. Register Plugin
  3. Hooks WP
  4. Javascript dan Less Implementation
  5. Widget
  6. install plugin dan screenshot

Konfigurasi File dan Folder

Konfigurasi file dan folder dari source yang kita buat untuk plugin :

  1. Buat folder plugin, beri nama jne-shipping-rate
  2. Copy seluruh file dan folder dari source yg telah dibuat ke folder plugin
  3. Ubah nama file functions.php menjadi jne-shipping-rate-functions.php
  4. Buat file jne-shipping-rate-init.php  untuk hook wordpress API
  5. Tambahkan folder widget untuk widget plugin

Register Plugin

Buat File : jne-shipping-rate.php

Tambahkan sebuah header Plugin informasi spesifik untuk mengimplementasikan plugin wordpress.

/*
Plugin Name: JNE Shipping Rate
Plugin URI: http://codex.wordpress.org
Description: Menampilkan daftar ongkos dan pelacakan pengiriman JNE
Version: 1.0
Author: Dani Gojay
Author URI: http://gojayincode.com
License: GPL2
*/

Periksa versi minimum yang diperlukan wordpress > 3.0, kirim pesan untuk memperbarui versi wordpress jika versi wordpress yang terinstall < 3.0. Hal yang tidak kalah pentingnya adalah membuat penamaan unik untuk fungsi dan kelas pada wordpress plugin, agar tidak conflict atau deprecated terdapa fungsi-fungsi plugin lainnya.

Pada plugin ini kita beri prefix JNE setiap nama fungsi dan kelas, serta definisikan konstanta untuk plugin:

  1. JNE_PLUGIN_BASENAME ( plugin basename ) = ”webroot\wp-content\plugins
  2. JNE_PLUGIN_NAME (plugin name) = “jne-shipping-rate”
  3. JNE_PLUGIN_DIR (plugin directory) = ”webroot\wp-content\plugins\jne-shipping-rate
  4. JNE_PLUGIN_URL (plugin URL) = ”http://yoursite.com/wp-content/plugins/jne-shipping-rate
  5. JNE_PLUGIN_ASSET_URL (plugin assets URL) = “http://yoursite.com/wp-content/plugins/jne-shipping-rate/assets
  6. JNE_PLUGIN_ASSET_DIR (plugin assets directory) = “webroot\wp-content\plugins\jne-shipping-rate\assets
  7. JNE_PLUGIN_DATA_DIR (plugin data directory) = “webroot\wp-content\plugins\jne-shipping-rate\data
  8. JNE_PLUGIN_INC_DIR (plugin includes directory) = “webroot\wp-content\plugins\jne-shipping-rate\includes
  9. JNE_PLUGIN_TPL_DIR (plugin data templates) = “webroot\wp-content\plugins\jne-shipping-rate\templates
global $wp_version;
define( 'JNE_REQUIRED_WP_VERSION', '3.0' );

$exit_msg = 'Plugin JNE Shipping Rate requires WordPress '. JNE_REQUIRED_WP_VERSION .' or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>';
if ( version_compare( $wp_version, JNE_REQUIRED_WP_VERSION, "<" ) ) {
    exit($exit_msg);
}

if ( ! defined( 'JNE_PLUGIN_BASENAME' ) )
    define( 'JNE_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

if ( ! defined( 'JNE_PLUGIN_NAME' ) )
    define( 'JNE_PLUGIN_NAME', trim( dirname( JNE_PLUGIN_BASENAME ), '/' ) );

if ( ! defined( 'JNE_PLUGIN_DIR' ) )
    define( 'JNE_PLUGIN_DIR', WP_PLUGIN_DIR . '/' . JNE_PLUGIN_NAME );

if ( ! defined( 'JNE_PLUGIN_URL' ) )
    define( 'JNE_PLUGIN_URL', WP_PLUGIN_URL . '/' . JNE_PLUGIN_NAME );

if ( ! defined( 'JNE_PLUGIN_ASSET_URL' ) )
    define( 'JNE_PLUGIN_ASSET_URL', JNE_PLUGIN_URL . '/assets' );

if ( ! defined( 'JNE_PLUGIN_ASSET_DIR' ) )
    define( 'JNE_PLUGIN_ASSET_DIR', JNE_PLUGIN_DIR . '/assets' );

if ( ! defined( 'JNE_PLUGIN_DATA_DIR' ) )
    define( 'JNE_PLUGIN_DATA_DIR', JNE_PLUGIN_DIR . '/data' );

if ( ! defined( 'JNE_PLUGIN_INC_DIR' ) )
    define( 'JNE_PLUGIN_INC_DIR', JNE_PLUGIN_DIR . '/includes' );

if ( ! defined( 'JNE_PLUGIN_TPL_DIR' ) )
    define( 'JNE_PLUGIN_TPL_DIR', JNE_PLUGIN_DIR . '/templates' );

Register plugin ke wordpress dengan register_activation_hook.  Saat plugin diaktifasi, lakukan aksi instalasi pada method install kelas JNE_Shipping_Rate. Definisi global variable jne untuk parsing jne (kelas Parse_JNE)

include 'jne-shipping-rate-functions.php';
include 'jne-shipping-rate-init.php';
include 'includes/class-parse-jne.php';

if( class_exists('JNE_Shipping_Rate') )
{
    $JNE = new JNE_Shipping_Rate();
    register_activation_hook( __FILE__, array( &$JNE, 'install' ) );
}
// Global variable jne
$GLOBALS['jne'] = new Parse_JNE( 'Jakarta.xls' );

Hooks WP

Beberapa hook WP API untuk plugin ini :

  • shortcode : membuat shortcode untuk page JNE
  • admin menu : menu admin untuk setting JNE
  • widget : daftarkan widget JNE
  • scripts : daftarkan script (css dan javascript) untuk plugin JNE
  • ajax : setting AJAX untuk plugin JNE

File : jne-shipping-rate-init.php

class JNE_Shipping_Rate
{
	const NONCE_AJAX = 'ajax-jne-nonce';

	public function __construct()
	{
		// shortcode

		// admin menu

		// widget

		// register scripts

		// ajax handler
	}

Tambah setting option JNE saat aktifasi plugin

public function install()
{
	if (!get_option('jne_settings'))
	{
		$settings = array(
			'display' => 200,
			'provinces' => array()
		);
		add_option('jne_settings', $settings, '', 'yes');
	}
}

Tambah  shortcode untuk menampilkan daftar tarif JNE dengan page.

public function __construct()
{
	// shortcode
	add_shortcode('jne', array(
		&$this,
		'display_page'
	));
	...
}

public function display_page()
{
	include( JNE_PLUGIN_TPL_DIR . '/page.php');
}

Dan berikut template shortcode untuk daftar plugin JNE. Template sama seperti index pada sebelumnya.

File : templates/page.php

<?php get_header() ?>

<div id="jne" class="bootstrap">
	<div class="row">
		...
	</div>
	<div id="loading" class="row">
		...
	</div>
	<div id="taxResults" class="row"></div>
</div>

<?php get_footer() ?>

Tambah  admin setting menu JNE beserta proses update setting.  Letak link berada di sub menu admin setting

public function __construct()
{
	// admin menu
	add_action('admin_menu', array(
		&$this,
		'jne_setting_menu'
	));
	...
}

public function jne_setting_menu()
{
	// add sub menu in the Admin "Settings" Menu
	add_options_page(
		'JNE Settings',
		'JNE Settings',
		'manage_options',
		'setting_jne',
		array( &$this, 'display_setting_menu' )
	);
}

public function display_setting_menu()
{
	global $jne;

	// get provinces
	$provinsi = $jne->getProvinces();

	// action save
	if ('save' == $_REQUEST['action'])
	{
		$provinces = $_POST['jne_provinsi'];
		$settings = array(
			'display' => $_POST['jne_display'],
			'provinces' => ( count($provinces) == count($provinsi) ) ? array() : $provinces
		);

		update_option('jne_settings', $settings);

		?><p><div id="message" class="updated" >Settings saved successfully</div></p><?php
	}
	// get settings option
	$jne_settings = get_option('jne_settings');
	?>

	<div class="wrap">
		<?php echo $message ?>
		<div id="icon-options-general" class="icon32"><br/></div>
		<h2>JNE Settings</h2>

		<!-- settings template -->
		<?php include( JNE_PLUGIN_TPL_DIR . '/settings.php'); ?>

	</div>
	<?php
}

Daftarkan script css dan javascript untuk plugin JNE. Dan buat global  javascript dengan localize script untuk javascript.

public function __construct()
{
	// register scripts
	add_action('wp_enqueue_scripts', array(
		&$this,
		'register_scripts'
	));
	...
}

public function register_scripts()
{
	global $post;

	// register styles
	wp_enqueue_style('jne-css', JNE_PLUGIN_ASSET_URL . '/css/style.css');

	// jquery core
	wp_enqueue_script('jquery');

	// bootstrap modal
	wp_enqueue_script('bootstrap-modal', JNE_PLUGIN_ASSET_URL . '/js/bootstrap-modal.js', array(
		'jquery'
	));
	// bootstrap tooltip
	wp_enqueue_script('bootstrap-tooltip', JNE_PLUGIN_ASSET_URL . '/js/bootstrap-tooltip.js', array(
		'jquery'
	));

	// ajax
	wp_enqueue_script('jne-ajax', JNE_PLUGIN_ASSET_URL . '/js/ajax.js', array(
		'jquery'
	));

	$jne_params =  array(
		'ajaxurl' => admin_url('admin-ajax.php'),
		'ajaxJNENonce' => wp_create_nonce(self::NONCE_AJAX),						'is_jne' => ( $post->post_content == '[jne]' )
	);
	wp_localize_script( 'jne-ajax', 'jne_params', $jne_params );
}

Tambah aksi untuk menghandle AJAX pada method ajax_handler. Bagian yang terpenting adalah nama action harus sesuai dengan bagian mengikuti wp_ajax_ dan wp_ajax_nopriv. contoh:

$.post( jne_params.ajaxurl,
	{action:’JNE-ajax’, nonce:jne_params.ajaxJNENonce, /* parameter lainnya */} ,
	/* callback */
)

Terlihat diatas adalah :

  1. AJAX URL : menggunakan default untuk aksi ajax pada wordpress, yaitu admin-ajax.php
  2. Parameter action adalah nama actionnya, jadi hook action wp_ajax_nopriv_JNE-ajax dan wp_ajax_JNE-ajax untuk AJAX handler

Untuk proses ajax sama dengan ajax.php sebelumnya, hanya saja ditambah pengecekan nonce. Nonce digunakan sebagai perlindungan keamanan untuk mencegah serangan dan kesalahan.

public function __construct()
{
	// ajax handler
	add_action('wp_ajax_nopriv_JNE-ajax', array(
		&$this,
		'ajax_handler'
	));
	add_action('wp_ajax_JNE-ajax', array(
		&$this,
		'ajax_handler'
	));
}

public function ajax_handler()
{
	global $jne;

	$jne_settings = get_option('jne_settings');

	$nonce = $_GET['nonce'];

	if ( !wp_verify_nonce($nonce, self::NONCE_AJAX) )
		die('error');

	$get = $_GET['get'];
	switch( $get ){
		/* @return JSON */
		case 'provinsi':
			...
			break;

		/* @return JSON */
		case 'kota':
			...
			break;

		/* @return String html */
		case 'pagination':
		case 'index':
			...
			include( JNE_PLUGIN_TPL_DIR . '/data.php');
			break;

	}

	exit;
}

Javascript

Sedikit perbubahan pada javascript untuk penyesuaian dengan pluign wordpress:

  1. Bundle ajax loading dengan pemanggilan fungsi loadAll, dengan nama load-jne
  2. Untuk beberapa aksi asynchronous, tambahkan parameter untuk action dan nonce.
jQuery(function($){

	$('body').bind('load-jne', function(){
		$('#loading').ajaxStart(function(){
			$(this).css('visibility','visible');
			$('#taxResults').hide();
		}).ajaxStop(function(){
			$(this).css('visibility','hidden');
			$('#taxResults').show();
		})
		loadAll()
	})

	var getJSON = function( param ){
		return $.getJSON( jne_params.ajaxurl, param, null );
	}

	var getHTML = function( param ){
		return $.get( jne_params.ajaxurl, param, null );
	}

	/* load all */
	var loadAll = function(){

		$.when(
				getJSON( { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'provinsi' } ),
				getHTML( { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'index' } )
			).done(function( data, html ){
				...
			});
	}

	// load hanya di page jne
	if( jne_params.is_jne )
	{
		$('body').trigger('load-jne')
	}

	/* aksi combobox provinsi */
	$('#combobox_provinsi').live('change', function(){
		...
			...

			$.when(
				getJSON( { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'kota', provinsi:index_provinsi } ),
				getHTML( { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'index', index_provinsi:index_provinsi } )
			).done(function( data, html ){
				...
			});

		...
	});

	/* aksi combobox kota */
	$('#combobox_kota').live('change', function(){
		...
			$.get( jne_params.ajaxurl, { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'index', index_kota:index_kota },
				function( html ){
					...
				}
			);
		...
	});

	/* aksi pagination */
	$('.pagination a').live('click', function(){
		...

		$.get( jne_params.ajaxurl, { action:'JNE-ajax', nonce:jne_params.ajaxJNENonce, get:'pagination', offset:offset },
			function( html ){
				...
			}
		);

		...
	})

	...

LESS Implementation

Mengingat sebelumnya source yang kita buat menggunakan twitter bootstrap, ada beberapa kendala jika kita meregister script twitter bootstrap kedalam plugin wordpress. Kemungkinnya adalah overridding atau penumpukan style/css dengan theme wordpress, sehingga tampilan menjadi berantakan.

LESS adalah dynamic stylesheet, dalam LESS menginjinkan penggunaan sebuah namespace. Bertujuan untuk implementasikan style terhadap elemen yang merupakan turunanya (children).

Twitter bootstrap sudah memiliki file LESS, copy folder less pada twitter bootstrap kedalam folder assets plugin. Lalu download javascript less untuk compile LESS, letakkan file tersebut pada assest folder js. Beri wrap class bootstrap pada file bootstrap.less dan responsive.less

file : assets/less/bootstrap.less

.bootstrap {
	// CSS Reset
	@import "reset.less";

	// Core variables and mixins
	...

	// Grid system and page structure
	...

	// Base CSS
	...

	// Components: common
	...

	// Components: Buttons & Alerts
	...

	// Components: Nav
	...

	// Components: Popovers
	...

	// Components: Misc
	...

	// Utility classes
	...
}

file : assets/less/responsive.less

.bootstrap {
	// REPEAT VARIABLES & MIXINS
	// -------------------------
	// Required since we compile the responsive stuff separately

	...

	// RESPONSIVE CLASSES
	// ------------------

	...

	// MEDIA QUERIES
	// ------------------

	// Large desktops
	...

	// Tablets to regular desktops
	....

	// Phones to portrait tablets and narrow desktops
	...

	// RESPONSIVE NAVBAR
	// ------------------

	// From 979px and below, show a button to toggle navbar contents
	...
}

Jadi setiap style elemen yang merupakan children dari class bootstrap, akan di-override oleh twitter bootstrap

Lanjut, tambahkan aksi hook untuk menambahkan file bootstrap.less dan responsive.less  pada header hanya untuk frontend bukan untuk admin. Pendefinisian style LESS sedikit berbeda dari css, yaitu pada rel=”stylesheet/less”

public function __construct()
{
	...

	add_action('wp_head', array(
		&$this,
		'load_less'
	));

	...
}

public function load_less()
{
	if ( !is_admin() )
	{
		// Actually printing the lines we need to load LESS in the HEAD
		print "\n<!-- Loading LESS styles and js -->\n";
		print "<link rel='stylesheet/less' id='style-less-css' href='" . JNE_PLUGIN_ASSET_URL . "/less/bootstrap.less' type='text/css' media='screen, projection' />\n";
		print "<link rel='stylesheet/less' id='style-less-css' href='" . JNE_PLUGIN_ASSET_URL . "/less/responsive.less' type='text/css' media='screen, projection' />\n";
		print "<script type='text/javascript' src='" . JNE_PLUGIN_ASSET_URL . "/less/less-1.3.1.min.js'></script>\n\n";
	}
}

Widget

Fitur widget :

  1. Menampilkan tracking order JNE dengan memasukan input nomor AWB dalam popup
  2. Menampilkan link untuk lihat daftar JNE. Jika diklik tampilkan dalam popup.

Buat kelas widget dengan turunan WP_Widget untuk membuat widget pada wordpress :

  1. __construct : Daftarkan widget option pada parentnya
  2. form : Menampilkan form setting widget, form berisi input title dan checkbox untuk  memilih menampilkan daftar JNE
  3. update : Proses update form setting widget
  4. widget : Menampilkan widget di frontend

File : widget/widget-jne.php

class JNE_Widget extends WP_Widget
{
	public function __construct()
	{
		$widget_options = array(
			'classname' => 'JNE_Widget_class',
			'description' => 'Menampilkan pelacakan dan daftar ongkos JNE'
		);

		parent::__construct( __CLASS__, 'JNE Express Across Nation', $widget_options);
	}

	public function form($instance)
	{
		$defaults = array(
			'title' => 'Tracking JNE'
		);
		$instance = wp_parse_args( (array) $instance, $defaults );
		$title 		= $instance['title'];
		$show_jne 	= $instance['show_jne'];
		?>
		<p>Title:
		<input class="widefat" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
		</p>
		<p>Show JNE in popup:
		<input type="checkbox" name="<?php echo $this->get_field_name('show_jne'); ?>" <?php checked( $show_jne, 'on' ); ?> />
		</p>
		<?php
	}

	public function update( $new_instance, $old_instance )
	{
		$instance = $old_instance;
		$instance['title']     = strip_tags( $new_instance['title'] );
		$instance['show_jne'] = strip_tags( $new_instance['show_jne'] );

		return $instance;
	}

	public function widget( $args, $instance )
	{
		extract( $args );

		$title 	   = apply_filters( 'widget_title', $instance['title'] );
		$show_jne  = empty( $instance['show_jne'] ) ? false : true ;

		echo $before_widget;

		if ( !empty( $title ) ) {
			echo $before_title . $title . $after_title;
		}

		include( JNE_PLUGIN_TPL_DIR . '/widget.php' );

		echo $after_widget;
	}
}

Beri class bootstrap agar template widget menggunakan twitter bootstrap. tampilan widget hanyalah sebuah form input tracking JNE dan link untuk menampilkan daftar  (jika link disetting check). Tentunya tambahkan template untuk modal tracking JNE dan daftar tarif JNE (berikut screenshot)

File: templates/widget.php

<div class="bootstrap widget-jne">
	<form class="form-search">
		<div class="input-append">
			<input type="text" name="awb"
				   class="span2 search-query" placeholder="Enter JNE Airwaybill"
				   rel="tooltip" title="Please enter JNE Airwaybill number">
			<button type="submit" class="btn" data-toggle="modal" data-target="#trackingModal">Submit</button>
	  </div>
	</form>

	<!-- Modal Tacking -->
	<div id="trackingModal" class="modal large hide fade" tabindex="-1" role="dialog" aria-labelledby="trackingModalLabel" aria-hidden="true">
		<div class="modal-header">
			<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
			<h3 id="trackingModalLabel" class="textcenter">Tracking JNE</h3>
		</div>
		<div class="modal-body">
			<img src="<?php echo JNE_PLUGIN_ASSET_URL . '/img/loader-bar.gif' ?>" class="aligncenter" style="border:0" />
		</div>
		<div class="modal-footer">
			<button class="btn" data-dismiss="modal">Close</button>
		</div>
	</div>

	<?php if( $show_jne ) : ?>
		<p><a href="#taxModal" role="button" class="btn-link" data-toggle="modal">Lihat daftar tarif JNE</a></p>
		<!-- Modal TAX -->
		<div id="taxModal" class="modal large hide fade" tabindex="-1" role="dialog" aria-labelledby="taxModalLabel" aria-hidden="true">
			<div class="modal-header">
				<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
				<h3 id="taxModalLabel" class="textcenter">Daftar tarif JNE</h3>
			</div>
			<div class="modal-body">
				<img src="<?php echo JNE_PLUGIN_ASSET_URL . '/img/loader-bar.gif' ?>" class="aligncenter" style="border:0" />
			</div>
			<div class="modal-footer">
				<button class="btn" data-dismiss="modal">Close</button>
			</div>
		</div>
	<?php endif; ?>
</div>

Tambahkan aksi show modal pada ajax handler :

  1. Show JNE tracking in popup

    Proses tracking JNE menggunakan cURL, seblumnya pastikan ektensi curl php anda aktif. Set CURL_URL dengan url jne untuk melihat tracking order dan nomor AWB pada parameternya. Eksekusi curl dari url yang dihasilkan adalah  html dari halaman http://jne.co.id tarcking order tersebut.

    Download kelas simple_html_dom. Dengan kelas ini html hasil eksekusi curl akan dikonversi menjadi dom element, jadi hampir sama seperti jquery pada javascript.Dari hasil kontent, kita hanya ambil table content tracking saja (berikut screenshot)

  2. $html = str_get_html($content);
    echo $html->find('td.content', 2)->innertext;
    
  3. Show JNE in popup

    Tampilkan template halaman daftar JNE (berikut screenshot)

    public function ajax_handler()
    {
    	global $jne;
    
    	...
    
    	switch( $get ){
    		/* @return JSON */
    		case 'provinsi':
    			...
    			break;
    
    		/* @return JSON */
    		case 'kota':
    			...
    			break;
    
    		/* @return String html */
    		case 'pagination':
    		case 'index':
    			....
    			break;
    
    		/* @return String html */
    		case 'show_tracking_in_modal':
    			include 'includes/html-dom/simple_html_dom.php';
    
    			$awb = $_GET['awb'];
    
    			if (!function_exists("curl_init"))
    			{
    				die('Aktifkan ekstensi CURL pada PHP anda...');
    			}
    
    			$chp = curl_init();
    			curl_setopt($chp, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
    			curl_setopt($chp, CURLOPT_FOLLOWLOCATION, 1);
    			curl_setopt($chp, CURLOPT_RETURNTRANSFER, 1);
    
    			$url = "http://jne.co.id/index.php?mib=tracking.detail&awb=".$awb;
    			curl_setopt($chp, CURLOPT_URL, $url);
    			curl_setopt($chp, CURLOPT_REFERER, "http://www.jne.co.id/index.php");
    			curl_setopt($chp, CURLOPT_URL, $url);
    			curl_setopt($chp, CURLOPT_CONNECTTIMEOUT,0);
    			curl_setopt($chp, CURLOPT_TIMEOUT, 400); //timeout in seconds
    			$content = curl_exec($chp);
    
    			$html = str_get_html($content);
    			echo $html->find('td.content', 2)->innertext;
    
    			curl_close($chp);
    			break;
    
    		/* @return String html */
    		case 'show_jne_in_modal':
    			include( JNE_PLUGIN_TPL_DIR . '/page-modal.php');
    			break;
    	}
    
    	exit;
    }
    

Modal  template, hampir sama seperti index.php pada source sebelumnya hanya menghapus stylesheet bootstrap

<html>
<head>
	<link href="<?php echo JNE_PLUGIN_ASSET_URL ?>/css/style.css" type="text/css" rel="stylesheet">
</head>
<body>
	<div id="jne" class="bootstrap">
		<table class="table table-bordered">
			<tr>
				<th class="textcenter">Cari Ongkos Kirim dari Jakarta</th>
			</tr>
			<tr>
				<td class="textcenter">
					<form id="formSearch" class="form-inline" method="get">
						<div class="group">
							<label>Provinsi</label>
							<select id="combobox_provinsi" class="field-text">
								<option value=""> Semua </option>
							</select>
						</div>
						<div class="group">
							<span id="loading-kota" class="hide" style="position:absolute; margin: 5px 0 0 40px !important">
								<img src="<?php echo JNE_PLUGIN_ASSET_URL ?>/img/ajax-spin.gif" style="vertical-align: middle;"/> loading kota
							</span>
							<label>Kota </label>
							<select id="combobox_kota" class="field-text" name="index">
								<option> Pilih Kota </option>
							</select>
						</div>
					</form>
				</td>
			</tr>
		</table>
		<div id="loading">
			<img src="<?php echo JNE_PLUGIN_ASSET_URL ?>/img/loader-bar.gif" alt="ajax-loader" class="aligncenter"/>
		</div>
		<div id="taxResults"></div>
	</div>
</body>
</html>

Tambahkan javascript untuk aksi modal.

var img_loading,
	    input_awb = $('form.form-search').find('input[name="awb"]');

	/* set default html img loading */
	$('#trackingModal, #taxModal').on('hidden', function(){
		$(this).find('.modal-body').html( img_loading )
	})
	/*
	 * Tracking Modal
	 * jangan tampilkan modal, jika input AWB == null (KOSONG)
	 * http://stackoverflow.com/questions/11736249/killing-close-a-twitter-boostrap-modal-already-opened#answer-11742343
	 */
	$('#trackingModal').on('show', function (e) {
		var awb = input_awb.val();
		if (!awb)
		{
			e && e.preventDefault()
			/*
			 * focus input search
			 * show tooltip
			 */
			input_awb.each(function(){
				$(this).focus();
				$(this).tooltip('show');
			})
		}
	}).on('shown', function (e) {
		var bodyModal = $(this).find('.modal-body')	,
				  awb = input_awb.val();

		// overlay wrap bootstrap
		$('body').find('.modal-backdrop').wrap('<div class="bootstrap" />')

		// ambil img loading
		img_loading = bodyModal.html()

		/* @return html */
		$.get( jne_params.ajaxurl, {
								action:'JNE-ajax',
								nonce:jne_params.ajaxJNENonce,
								get:'show_tracking_in_modal',
								awb:awb
			},
			function( html ){
				/*
				 * filtering html
				 * tambahkan class 'table' pada element table
				 * tambahkan style 'background-color' untuk tr
				 * tambahkan style 'font-weight' untuk tr children (td)
				 */
				bodyModal.html( html )
						 .find('table')
						 .addClass('table')
						 .end()
							 .find('tr.trackH')
							 .css('background-color','#ddd')
								 .children()
								 .css('font-weight','bold')
				input_awb.val('')
			}
		)

	});

	/* Tax Modal */
	$('#taxModal').on('shown', function (e) {
		var bodyModal = $(this).find('.modal-body')

		// overlay wrap bootstrap
		$('body').find('.modal-backdrop').wrap('<div class="bootstrap" />')

		// ambil img loading
		img_loading = bodyModal.html()

		/* @return html */
		$.get( jne_params.ajaxurl, {
								action:'JNE-ajax',
								nonce:jne_params.ajaxJNENonce,
								get:'show_jne_in_modal'
							},
			function( html ){
				bodyModal.html( html )
				$('body').trigger('load-jne')
			}
		)
	});
})

daftarkan widget dengan hook widget_init

public function __construct()
{
	...

	// widget
	add_action('widgets_init', array(
		&$this,
		'register_widget'
	));

	...
}

public function register_widget()
{
	include 'widget/widget-jne.php';
	register_widget( 'JNE_Widget' );
}

Install Plugin

Install Plugin JNE
Download plugin, Plugins > Add New > upload > pilih plugin JNE (download) > Install Now > active
Page JNE
Buat halaman JNE, Page > Add New > Title : JNE (atau lainnya, terserah anda), tulis [jne] pada kontent
JNE Setting
Setting JNE, Settings > JNE Settings : isikan input jumlah baris yang ditampilkan (default 200) dan pilih provinsi yang diijinkan (default semua provinsi)