1

This is my very first WP plugin. Theres probably going to be more than one question here and they're all related so I'm going to ask all here.

Working test here

Tracking number to use: lc0614061377

Example XML Response from tracenow.net:

<Consignment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
    <consignmentNumber>lc0614061377</consignmentNumber>
    <customerRef/>
    <itemCount>1</itemCount>
    <name>six red squares</name>
    <address1>unit 7b victoria business park</address1>
    <address2>short street</address2>
    <town>SOUTHEND ON SEA</town>
    <county>ESSEX</county>
    <postcode>ss2 5by</postcode>
    <country>United Kingdom</country>
    <recipient>cannon</recipient>
    <status>Delivered</status>
    <collected>0001-01-01T00:00:00</collected>
    <delivered>2014-07-25T10:00:50</delivered>
    <collectionCode/>
    <deliveryCode>Delivered OK</deliveryCode>
    <comments/>
    <created>2014-07-24T15:00:40</created>
    <deliveryLatitude>51.54617</deliveryLatitude>
    <deliveryLongitude>0.712245</deliveryLongitude>
    <deliverySignature>
        http://resources.tracenow.net/signatures/d5dbffb4-0336-44bf-b72c-00fb9aaac759/0a630170-1390-4ca4-b104-5ca4b9b6199f.jpg
    </deliverySignature>
    <deliveryImage>
        http://resources.tracenow.net/images/d5dbffb4-0336-44bf-b72c-00fb9aaac759/6de6c2bd-236b-447d-a106-b1a7bd537645.jpg
    </deliveryImage>
</Consignment>

This is the current state of my code:

// tracenow.php

<?php
 /*
 Plugin Name: TraceNow Tracking
 Plugin URI: http://www.titandesign.co.uk
 Description: A simple parcel tracking plugin for Codeway TraceNow.
 Version: 4.0
 Author: Richard King
 Author URI: http://www.titandesign.co.uk/
 License: GPL-2.0+
 License URI: http://www.opensource.org/licenses/gpl-license.php 
 */

 wp_enqueue_script( 'my-ajax-handle', plugin_dir_url( __FILE__ ) . 'ajax.js', array( 'jquery' ) );
 wp_enqueue_style( 'my-ajax-handle', plugin_dir_url( __FILE__ ) . 'style.css' );
 wp_localize_script( 'my-ajax-handle', 'the_ajax_script', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );

 // THE TRACKING FUNCTION
 if( !class_exists( 'WP_Http' ) )
include_once( ABSPATH . WPINC. '/class-http.php' );

 function tracking_function(){ 
 //if ( isset( $_POST['consignmentNumber'] ) && '1' == $_POST['consignmentNumber'] ){
    $consignmentNumber = $_POST['consignmentNumber'];
    $url = ('http://services.tracenow.net/TraceNowAccess.asmx/GetConsignment?consignmentNumber=' .($consignmentNumber) .'&externalAccessGuid=d5dbffb4-0336-44bf-b72c-00fb9aaac759');
    $args = array(
                'method' => 'GET',
                'timeout' => 45,
                'redirection' => 5,
                'httpversion' => '1.1',
                'content-type' => 'application/x-www-form-urlencoded',
                'body' => array(),
                'headers' => array(),
                'blocking' => true,
                'cookies' => array(),
                'connection' => 'close',
                );

    $response = wp_remote_get( $url, $args );
    if ( is_wp_error( $response ) ) {
            $error_message = $response->get_error_message();
                 echo "Something went wrong: $error_message";
            } else {
                /*include ('/render_xml_to_html.php');
                        if(function_exists('render_xml_data')){
                            render_xml_data('example_data.xml');
                            }else{
                            echo null;
                        } */
                    echo '<h3>Consignment Details</h3>' . '<pre>' . print_r($response['body'], true) . '</pre>';// this is passed back to the javascript function
                    die();
            } // end if
    //} // end if               
 }

 // THE AJAX ADD ACTIONS
add_action( 'wp_ajax_the_ajax_hook', 'tracking_function' );
add_action( 'wp_ajax_nopriv_the_ajax_hook', 'tracking_function' ); // need this to serve non logged in users

// ADD TRACKING FORM TO THE PAGE

function tracenow_frontend(){
$the_form = '
<form id="theForm" method="POST">
<input id="consignmentNumber" name="consignmentNumber" value="Consignment No" type="text" />
<input name="action" type="hidden" value="the_ajax_hook" /> <!-- this puts the action the_ajax_hook into the serialized form -->
<input id="submit_button" value="Track" type="button" onClick="submit_me();" />
 </form>
 <div id="response_area">
    Your tracking details will appear here
</div>';// END DIV RESPONSE AREA
return $the_form;
}
 add_shortcode("tn_ajax_frontend", "tracenow_frontend");

// ajax.js
function submit_me(){
        jQuery.post(the_ajax_script.ajaxurl, jQuery('#theForm').serialize()
        ,
        function(response_from_tracking_function){
            jQuery("#response_area").html(response_from_tracking_function);
            }
        );
        jQuery('#theForm').find('input[type=text]').val('Consignment No');      
}

// render_xml_to_html.php
<?php
/*
    Written by Gary Hollands Sept 2010
    This work is available under the terms of the GNU General Public License, http://www.gnu.org/licenses/gpl.html
    render_xml_data is a function that can use XML data and present that data as HTML.
*/
function render_xml_data($path_to_xml_file){
    if (!file_exists($path_to_xml_file)){
        return;
    }else{
        $chars_to_replace = array('[\r]','[\n]','[\t]');
        $xmlstring = trim(preg_replace($chars_to_replace, '', file_get_contents($path_to_xml_file)));
    }
    $xml = new SimpleXMLElement($xmlstring);
    foreach ($xml->consignment as $consign) {
            echo '<div class="consignment">'."\n";
            echo '<h3>'.$consign->consignmentnumber.'</h3>'."\n";
            echo '<p><span class="category">Customer Ref: </span>'.$consign->customerref.'</p>'."\n";
            echo '<p><span class="category">Number of items: </span>'.$consign->itemcount.'</p>'."\n";
            echo '<p><span class="category">Name: </span>'.$consign->name.'</p>'."\n";
            echo '<p><span class="category">Address 1: </span>'.$consign->address1.'</p>'."\n";
            echo '<p><span class="category">Address 2: </span>'.$consign->address2.'</p>'."\n";
            echo '<p><span class="category">Town: </span>'.$consign->town.'</p>'."\n";
            echo '<p><span class="category">County: </span>'.$consign->county.'</p>'."\n";
            echo '<p><span class="category">Postcode: </span>'.$consign->postcode.'</p>'."\n";
            echo '<p><span class="category">Country: </span>'.$consign->country.'</p>'."\n";
            echo '<p><span class="category">Recipient: </span>'.$consign->recipient.'</p>'."\n";
            echo '<p><span class="category">Status: </span>'.$consign->status.'</p>'."\n";
            echo '<p><span class="category">Collected: </span>'.$consign->collected.'</p>'."\n";
            echo '<p><span class="category">Delivered: </span>'.$consign->delivered.'</p>'."\n";
            echo '<p><span class="category">Collection Code: </span>'.$consign->collectioncode.'</p>'."\n";
            echo '<p><span class="category">Delivery Code: </span>'.$consign->deliverycode.'</p>'."\n";
            echo '<p><span class="category">Comments: </span>'.$consign->comments.'</p>'."\n";
            echo '<p><span class="category">Created: </span>'.$consign->created.'</p>'."\n";
            echo '<p><span class="category">Delivery Latitude: </span>'.$consign->deliverylatitude.'</p>'."\n";
            echo '<p><span class="category">Delivery Longitude: </span>'.$consign->deliverylongitude.'</p>'."\n";
            echo '<p><span class="category">Delivery Signature: </span><img src="/'.$consign->deliverysignature.'/"/></p>'."\n";
            echo '<p><span class="category">Delivery Image: </span><img src="/'.$consign->deliveryimage.'/"/></p>'."\n";
            echo '</div><!--end consignment-->'."\n";
    }
}

Link to GitHub Gist

So to get started. The tracking function is working and returns the response from the server fine.

  1. How do I get the response to render as HTML to be displayed on a page. At the moment it is returning the XML body of the request which is exactly what I need. I need to target the nodes that are not empty and return them with a title. Example for the consignment number I want to add the title 'Consignment No:'. The same for address, status, time, GeoLoc etc. I have considered using jQuery and CSS :before to add the title. I also have a render_xml_to_html.php file which I assumed was the correct method, however the only return I get when implimented is 0. Any suggestions?
  2. There are links to 2 images in the XML response but they don't have the img tag or classes or ID's, so I'm finding it difficult to target these image files. I tried targeting .jpg but that just did not work at all. I need to add the img, src="" and alt="" tags so I can display the image links as images. Help please.
  3. Securing the plugin: I have tried adding wp_nonce to the ajax call to verify the form request. I have also tried creating and adding the nonce to the tracking function but again I get an error. This has really got me stumped. I have now read so many blogs on adding nonce that I am totally confused and could do with a gentle push in the right direction.
  4. I would like to send the response to a page called results. This is an existing page. I checked on codex but the only option I could find was addpage, which creates a new page everytime. The reason: I would like to be able to add the plugin as a widget.
0

Registering dependencies

WordPress uses the Dependency API for that. It's fairly simple: Register & enqueue a script, then pass data that you want to pass from WP/PHP to JS using wp_localize_script(), which adds a <script> tag containing an Array to your DOM (exactly before your script gets added to it):

add_action( 'wp_enqueue_scripts', function()
{
    $name = 'handle';
    wp_register_script(
        $name,
        plugins_url( 'assets/ajax.js', __FILE__ ),
        [ 'jquery' ],
        filemtime( plugins_dir_path( __FILE__ ).'assets/ajax.js' ),
        true
    );
    wp_enqueue_script( $name );
    wp_localize_script(
        $name,
        "{$name}Obj", // This string is what gives you access to below array
        [
            'ajaxurl'     => esc_url( admin_url( 'admin-ajax.php' ) ),
            '_ajax_nonce' => wp_create_nonce( "{$name}_action" ),
            'action'      => "{$name}_action",
            'data'        => [ /* additional data as array */ ],
        ]
    );
} );

You might notice, that I used wp_create_nonce() and passed an action attribute to the call for security reasons (verifying the origin of a request in your callback).

How to perform a remote request and fetch XML data

When loading data from a remote request, you should use the WP HTTP API, but with its higher level functions.

Example plugin, that will perform a request and add the result below your footer (both admin and front end):

<?php
/** Plugin Name: (#160775) Fetch XML from TraceNow.net - Part 1 */

add_action( 'shutdown', function()
{
    $request  = wp_remote_get( 
        'http://services.tracenow.net/TraceNowAccess.asmx/GetConsignment?'.
        join( '&', [
            'consignmentNumber'  => 'lc0614061377',
            'externalAccessGuid' => 'd5dbffb4-0336-44bf-b72c-00fb9aaac759'
        ],
    );
    $response = wp_remote_retrieve_body( $request, [
        // *additional args
    ] );

    is_wp_error( $response )
        AND wp_send_json_error( $response->get_error_code() ); 

    if ( 
        'OK' !== wp_remote_retrieve_response_message( $response )
        OR 200 !== wp_remote_retrieve_response_code( $response )
    )
        wp_send_json_error( $response );

    var_dump( wp_send_json_success( $response ) );
} );

This is the easiest way to perform such requests. As you can see, I'm using JSON. XML is gone from WPs HTTP API since some versions as JSON is easier to use.

Hint: wp_remote_*() takes a second argument, an array of args on which you can read about on Code.

Performing an action

No matter if AJAX-ified (see archive of for more info) or not, you need a form.

<form id="asktracenow">
    <input name="consignmentNumber" />
    <input name="submit" value="submit" />
</form>

Now that this form submits $_POST data, you will want to sanitize it, before using it in your URl. You have multiple options depending on how the data is constructed. First we just $cnum = trim( $_POST['consignmentNumber'] ), then we validate and in case sanitize it.

if ( false !== filter_var( $cnum, FILTER_VALIDATE_INT ) )
{
    // do stuff with your absolute, positive integer
    // adjust the check to only pass valid data
}

Make sure that you add a lot of weight into this - you never know what you get in return.

AJAXifying things

Simply hook things to the WP action. Summed up this will look close to the following.

// Public or private?
add_action( 'wp_ajax_handle_action', function( $data )
{
    check_ajax_referer( $data['action'] );

    # @TODO sanitize data here:
    // filter_var() filter_var_array() and filter_input()
    # @TODO Custom logic here

    // Error?
    if ( is_wp_error( $thing ) )
        wp_send_json_error( $data );

    // Success!
    wp_send_json_success( $data );
} );

Hint: Put all this in a separate plugin for separation of concerns and portability.

JavaScript AJAX/request handling

To make this answer a bit more complete, here's a link to another answer by me that explains this in detail and has a huge example Gist attached.

  • Thank you @kaiser, looks like I have a lot more reading to do. The code you have posted looks nothing like any of the blogs, articles or tutorials I have managed to find about wp http on the web – Richard King Sep 9 '14 at 17:15
  • @RichardKing You're welcome. And yeah, there aren't that many good resources about that topic out there. Most valuable stuff is on this site. – kaiser Sep 9 '14 at 17:27
  • sorry be a pain. I dropped part one into a new plugin folder on my local install (XAMPP, WP 3.9.2 Win 8.1) as you suggested. It returned an error. Parse error: syntax error, unexpected ')' in C:\xampp\htdocs\bancroft\wp-content\plugins\tracking5\tracenow.php on line 23. Its the ending parenthesis for the remote_get, it looks right to me so I copied the code by hand, it comes out the same and returns the same parsing error. – Richard King Sep 10 '14 at 7:50
  • No figured it out, there was a , where there should have been a closing ) – Richard King Sep 10 '14 at 12:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.