From c9e43ec2033e354899b5ecebb07dd0758e5d7dfa Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Sun, 1 Jan 2012 23:15:11 +0000 Subject: [PATCH 1/3] Added JSON output functionality to API calls (see format[xml] or format[json]). --- application/controllers/api.php | 4 ++++ application/views/api/help.php | 10 +++++----- application/views/api/index.php | 23 ++++++++++++++++++----- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/application/controllers/api.php b/application/controllers/api.php index 5a2ccc71..c6ebda1c 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -148,6 +148,7 @@ class API extends CI_Controller { // Retrieve the arguments from the query string $arguments = $this->_retrieve(); + $data['data']['format'] = $arguments['format']; // Call the parser within the API model to build the query $query = $this->api_model->select_parse($arguments); @@ -244,6 +245,7 @@ class API extends CI_Controller { $limit = preg_grep("/^limit\[(.*)\]$/", $this->uri->segments); $order = preg_grep("/^order\[(.*)\]$/", $this->uri->segments); $fields = preg_grep("/^fields\[(.*)\]$/", $this->uri->segments); + $format = preg_grep("/^format\[(.*)\]$/", $this->uri->segments); // Strip each argument $arguments['query'] = substr(array_pop($query), 6); @@ -254,6 +256,8 @@ class API extends CI_Controller { $arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order']) - 1); $arguments['fields'] = substr(array_pop($fields), 7); $arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields']) - 1); + $arguments['format'] = substr(array_pop($format), 7); + $arguments['format'] = substr($arguments['format'], 0, strlen($arguments['format']) - 1); // Return the arguments return $arguments; diff --git a/application/views/api/help.php b/application/views/api/help.php index f48fd690..eb4335db 100644 --- a/application/views/api/help.php +++ b/application/views/api/help.php @@ -81,15 +81,15 @@
  • Key with Read & Write Access
  • Key with Read Only Access
  • -There are a number of API calls you can make from other applications. +There are a number of API calls you can make from other applications, with output available in either XML or JSON.

    API Guide

    Description

    -Query the logbook +Query the logbook, and output in XML format.

    Syntax

    -
  • /search/query[<field><=|~><value>{(and|or)...]}/limit[<num>]/fields[<field1>,{<field2>}]/order[<field>]
    +
  • /search/format[xml]/query[<field><=|~><value>{(and|or)...]}/limit[<num>]/fields[<field1>,{<field2>}]/order[<field>]

    Example

    Search for entries with a call beginning with M0 and a locator beginning with I or J, show the callsign and locator fields, order it by callsign and limit the results to 10. -
  • /search/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)]
    -
  • Run it! +
  • /search/format[xml]/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)]
    +
  • Run it! (XML) or Run it! (JSON) diff --git a/application/views/api/index.php b/application/views/api/index.php index 725187b7..83c63382 100644 --- a/application/views/api/index.php +++ b/application/views/api/index.php @@ -1,8 +1,5 @@ saveXML()); +// Output + +// Check whether we want XML or JSON output +if($data['format'] == "xml") { + // Set the content-type for browsers + header("Content-type: text/xml"); + echo formatXmlString($xmlDoc->saveXML()); +} else if($data['format'] == "json") { + // Set the content-type for browsers + header("Content-type: application/json"); + // For now, our JSON output is simply the XML re-parsed with SimpleXML and + // then re-encoded with json_encode + $x = simplexml_load_string($xmlDoc->saveXML()); + $j = json_encode($x); + echo $j; +} else { + echo "Error: Unknown format type '".$data['format']."'."; +} // This function tidies up the outputted XML function formatXmlString($xml) { From c93bc608ddc7d9818c21a3af6612c7a0162d1c94 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Mon, 2 Jan 2012 00:08:36 +0000 Subject: [PATCH 2/3] API key-based authorization implemented for api/search. --- application/controllers/api.php | 37 ++++++++++++++++++++++++-- application/models/api_model.php | 45 +++++++++++++++++++++----------- application/views/api/help.php | 2 +- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/application/controllers/api.php b/application/controllers/api.php index c6ebda1c..7ac07636 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -144,10 +144,14 @@ class API extends CI_Controller { $this->load->model('api_model'); $this->load->model('logbook_model'); $this->load->model('user_model'); - //if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); } + + $arguments = $this->_retrieve(); + + if((!$this->user_model->authorize(3)) && ($this->api_model->authorize($arguments['key']) == 0)) { + $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); + } // Retrieve the arguments from the query string - $arguments = $this->_retrieve(); $data['data']['format'] = $arguments['format']; // Call the parser within the API model to build the query @@ -189,6 +193,27 @@ class API extends CI_Controller { $this->load->view('api/index', $data); } + function validate() + { + // Load the API and Logbook models + $this->load->model('api_model'); + $this->load->model('logbook_model'); + + // Retrieve the arguments from the query string + $arguments = $this->_retrieve(); + + // Add some debugging information to the XML output + $data['data'] = $arguments; + $data['data']['queryInfo']['call'] = "validate"; + $data['data']['queryInfo']['dbQuery'] = ""; + $data['data']['queryInfo']['numResults'] = 1; + $data['data']['queryInfo']['executionTime'] = 0; + + $data['data']['validate_Result']['results'] = array(0 => array('Result' => $this->api_model->authorize($arguments['key']))); + + $this->load->view('api/index', $data); + } + function add() { // Load the API and Logbook models @@ -246,6 +271,7 @@ class API extends CI_Controller { $order = preg_grep("/^order\[(.*)\]$/", $this->uri->segments); $fields = preg_grep("/^fields\[(.*)\]$/", $this->uri->segments); $format = preg_grep("/^format\[(.*)\]$/", $this->uri->segments); + $key = preg_grep("/^key\[(.*)\]$/", $this->uri->segments); // Strip each argument $arguments['query'] = substr(array_pop($query), 6); @@ -258,6 +284,13 @@ class API extends CI_Controller { $arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields']) - 1); $arguments['format'] = substr(array_pop($format), 7); $arguments['format'] = substr($arguments['format'], 0, strlen($arguments['format']) - 1); + $arguments['key'] = substr(array_pop($key), 4); + $arguments['key'] = substr($arguments['key'], 0, strlen($arguments['key']) - 1); + + // By default, assume XML for the format if not otherwise set + if($arguments['format'] == "") { + $arguments['format'] = "xml"; + } // Return the arguments return $arguments; diff --git a/application/models/api_model.php b/application/models/api_model.php index 11cc2e31..0eba5f84 100644 --- a/application/models/api_model.php +++ b/application/models/api_model.php @@ -38,26 +38,41 @@ class API_Model extends CI_Model { function access($key) { + // No key = no access, mate + if(!$key) { + return $status = "No Key Found"; + } + // Check that the key is valid $this->db->where('key', $key); - $query = $this->db->get('api'); + $query = $this->db->get('api'); - if ($query->num_rows() > 0) - { - foreach ($query->result() as $row) - { - if($row->status == "active") { - return $status = $row->rights; - } else { - return $status = "Key Disabled"; - } - - } - } else { - return $status = "No Key Found"; - } + if ($query->num_rows() > 0) + { + foreach ($query->result() as $row) + { + if($row->status == "active") { + return $status = $row->rights; + } else { + return $status = "Key Disabled"; + } + } + } else { + return $status = "No Key Found"; + } } + function authorize($key) { + $r = $this->access($key); + if($r == "rw") { + return 2; + } else if($r == "r") { + return 1; + } else { + return 0; + } + } + // FUNCTION: string name(string $column) // Converts a MySQL column name to a more friendly name function name($col) diff --git a/application/views/api/help.php b/application/views/api/help.php index eb4335db..d775b6b9 100644 --- a/application/views/api/help.php +++ b/application/views/api/help.php @@ -64,7 +64,7 @@ ?> - status); ?> + status); ?> - Test From 0e291200faf0b73e9368c73aba4e45ae52a8f7ea Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Tue, 3 Jan 2012 23:30:53 +0000 Subject: [PATCH 3/3] API query string changes - now uses = instead of square brackets. --- application/controllers/api.php | 74 +++++++++++++++------------- application/models/api_model.php | 9 +++- application/models/logbook_model.php | 8 ++- application/views/api/help.php | 8 +-- application/views/api/index.php | 31 ++++++++---- css/api.xsl | 4 +- 6 files changed, 82 insertions(+), 52 deletions(-) diff --git a/application/controllers/api.php b/application/controllers/api.php index 7ac07636..32144058 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -159,30 +159,36 @@ class API extends CI_Controller { // Execute the query, and retrieve the results $s = $this->logbook_model->api_search_query($query); - $results = $s['results']; - - // Cycle through the results, and translate between MySQL column names - // and more friendly, descriptive names $a = 0; - if($results->num_rows != 0) - { - foreach ($results->result() as $row) { - $record = (array)$row; - $r[$a]['rid'] = $a; - while (list($key, $val) = each($record)) { - $r[$a][$this->api_model->name($key)] = $val; - } - $a++; - } - // Add the result record to the main results array + + if(isset($s['results'])) { + $results = $s['results']; + + // Cycle through the results, and translate between MySQL column names + // and more friendly, descriptive names + if($results->num_rows != 0) + { + foreach ($results->result() as $row) { + $record = (array)$row; + $r[$a]['rid'] = $a; + while (list($key, $val) = each($record)) { + $r[$a][$this->api_model->name($key)] = $val; + } + $a++; + } + // Add the result record to the main results array $data['data']['search_Result']['results'] = $r; - } - else - { - // We've got no results, so make this empty for completeness - $data['data']['search_Result']['results'] = ""; - } - + } + else + { + // We've got no results, so make this empty for completeness + $data['data']['search_Result']['results'] = ""; + } + } else { + $data['data']['error'] = $s['error']; + $data['data']['search_Result']['results'] = ""; + } + // Add some debugging information to the XML output $data['data']['queryInfo']['call'] = "search"; $data['data']['queryInfo']['dbQuery'] = $s['query']; @@ -266,26 +272,26 @@ class API extends CI_Controller { $arguments = array(); // Retrieve each arguments - $query = preg_grep("/^query\[(.*)\]$/", $this->uri->segments); - $limit = preg_grep("/^limit\[(.*)\]$/", $this->uri->segments); - $order = preg_grep("/^order\[(.*)\]$/", $this->uri->segments); - $fields = preg_grep("/^fields\[(.*)\]$/", $this->uri->segments); - $format = preg_grep("/^format\[(.*)\]$/", $this->uri->segments); - $key = preg_grep("/^key\[(.*)\]$/", $this->uri->segments); + $query = preg_grep("/^query=(.*)$/", $this->uri->segments); + $limit = preg_grep("/^limit=(.*)$/", $this->uri->segments); + $order = preg_grep("/^order=(.*)$/", $this->uri->segments); + $fields = preg_grep("/^fields=(.*)$/", $this->uri->segments); + $format = preg_grep("/^format=(.*)$/", $this->uri->segments); + $key = preg_grep("/^key=(.*)$/", $this->uri->segments); // Strip each argument $arguments['query'] = substr(array_pop($query), 6); - $arguments['query'] = substr($arguments['query'], 0, strlen($arguments['query']) - 1); + $arguments['query'] = substr($arguments['query'], 0, strlen($arguments['query'])); $arguments['limit'] = substr(array_pop($limit), 6); - $arguments['limit'] = substr($arguments['limit'], 0, strlen($arguments['limit']) - 1); + $arguments['limit'] = substr($arguments['limit'], 0, strlen($arguments['limit'])); $arguments['order'] = substr(array_pop($order), 6); - $arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order']) - 1); + $arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order'])); $arguments['fields'] = substr(array_pop($fields), 7); - $arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields']) - 1); + $arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields'])); $arguments['format'] = substr(array_pop($format), 7); - $arguments['format'] = substr($arguments['format'], 0, strlen($arguments['format']) - 1); + $arguments['format'] = substr($arguments['format'], 0, strlen($arguments['format'])); $arguments['key'] = substr(array_pop($key), 4); - $arguments['key'] = substr($arguments['key'], 0, strlen($arguments['key']) - 1); + $arguments['key'] = substr($arguments['key'], 0, strlen($arguments['key'])); // By default, assume XML for the format if not otherwise set if($arguments['format'] == "") { diff --git a/application/models/api_model.php b/application/models/api_model.php index 0eba5f84..352a49ad 100644 --- a/application/models/api_model.php +++ b/application/models/api_model.php @@ -193,6 +193,8 @@ class API_Model extends CI_Model { $s[3] = '/\(asc\)/'; $s[4] = '/\(desc\)/'; $s[5] = '/,$/'; + $s[6] = '/\[/'; + $s[7] = '/\]/'; $r[0] = '('; $r[1] = ')'; @@ -200,6 +202,8 @@ class API_Model extends CI_Model { $r[3] = ' ASC '; $r[4] = ' DESC '; $r[5] = ''; + $r[6] = ''; + $r[7] = ''; $q .= preg_replace($s, $r, $arguments['order']); @@ -258,8 +262,9 @@ class API_Model extends CI_Model { $r[1] = ' OR '; $r[2] = ' < '; $r[3] = ' > '; - $r[4] = '['; - $r[5] = ']'; + // Strip out square brackets + $r[4] = ''; + $r[5] = ''; $r[6] = '++$1++ ='; $r[7] = '= \'$1\''; $r[8] = 'UNIX_TIMESTAMP(NOW())'; diff --git a/application/models/logbook_model.php b/application/models/logbook_model.php index 6dcb4690..22641ea4 100644 --- a/application/models/logbook_model.php +++ b/application/models/logbook_model.php @@ -411,7 +411,10 @@ class Logbook_model extends CI_Model { function api_search_query($query) { $time_start = microtime(true); - $results = @$this->db->query($query); + $results = $this->db->query($query); + if(!$results) { + return array('query' => $query, 'error' => $this->db->_error_number(), 'time' => 0); + } $time_end = microtime(true); $time = round($time_end - $time_start, 4); @@ -421,6 +424,9 @@ class Logbook_model extends CI_Model { function api_insert_query($query) { $time_start = microtime(true); $results = $this->db->insert($this->config->item('table_name'), $query); + if(!$results) { + return array('query' => $query, 'error' => $this->db->_error_number(), 'time' => 0); + } $time_end = microtime(true); $time = round($time_end - $time_start, 4); diff --git a/application/views/api/help.php b/application/views/api/help.php index d775b6b9..07f92e9a 100644 --- a/application/views/api/help.php +++ b/application/views/api/help.php @@ -64,7 +64,7 @@ ?> - status); ?> - Test + status); ?> - Test @@ -86,10 +86,10 @@ There are a number of API calls you can make from other applications, with outpu

    Description

    Query the logbook, and output in XML format.

    Syntax

    -
  • /search/format[xml]/query[<field><=|~><value>{(and|or)...]}/limit[<num>]/fields[<field1>,{<field2>}]/order[<field>]
    +
  • /search/format=<format>/query=<field><=|~><value>{(and|or)...}/limit=<num>/fields=<field1>,{<field2>/order=<field>

    Example

    Search for entries with a call beginning with M0 and a locator beginning with I or J, show the callsign and locator fields, order it by callsign and limit the results to 10. -
  • /search/format[xml]/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)]
    -
  • Run it! (XML) or Run it! (JSON) +
  • /search/format=xml/query=Call~M0*(and)(Locator~I*(or)Locator~J*)/limit=10/fields=distinct(Call),Locator/order=Call(asc)
    +
  • Run it! (XML) or Run it! (JSON) diff --git a/application/views/api/index.php b/application/views/api/index.php index 83c63382..2042c3e0 100644 --- a/application/views/api/index.php +++ b/application/views/api/index.php @@ -2,13 +2,16 @@ // Create the DOMDocument for the XML output $xmlDoc = new DOMDocument("1.0"); -// Add reference to the XSLT -$xsl = $xmlDoc->createProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"/css/api.xsl\""); -$xmlDoc->appendChild($xsl); + +if($data['format'] == "xml") { + // Add reference to the XSLT + $xsl = $xmlDoc->createProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"/css/api.xsl\""); + $xmlDoc->appendChild($xsl); +} // Get the method called, and build the root node $call = $data['queryInfo']['call']; -$rootNode = $xmlDoc->createElement("HRDWebLogbook-API"); +$rootNode = $xmlDoc->createElement("Cloudlog-API"); $parentNode = $xmlDoc->appendChild($rootNode); // Get the results output @@ -33,14 +36,14 @@ $queryElement->setAttribute("executionTime", $data['queryInfo']['executionTime'] $queryElement->setAttribute("logbookURL", $this->config->item('base_url')); // Add the main results node -$node = $xmlDoc->createElement("elements"); +$node = $xmlDoc->createElement("results"); $elementsNode = $parentNode->appendChild($node); // Cycle through the results and add to the results node if($output['results']) { foreach($output['results'] as $e) { - $node = $xmlDoc->createElement("element"); + $node = $xmlDoc->createElement("result"); $element = $elementsNode->appendChild($node); foreach($e as $attr) { @@ -67,12 +70,22 @@ if($output['results']) } } +if(isset($data['error'])) +{ + $node = $xmlDoc->createElement("error"); + $errorNode = $parentNode->appendChild($node); + + $errorNode->setAttribute("id", $data['error']); +} + // Output // Check whether we want XML or JSON output -if($data['format'] == "xml") { - // Set the content-type for browsers - header("Content-type: text/xml"); +if(($data['format'] == "xml") || ($data['format'] == "xmlp") || ($data['format'] == "xmlt")) { + if(($data['format'] == "xml") || ($data['format'] == "xmlp")) { + // Set the content-type for browsers + header("Content-type: text/xml"); + } echo formatXmlString($xmlDoc->saveXML()); } else if($data['format'] == "json") { // Set the content-type for browsers diff --git a/css/api.xsl b/css/api.xsl index f81aa8b2..424ecaaf 100644 --- a/css/api.xsl +++ b/css/api.xsl @@ -11,11 +11,11 @@

    Output of ''

    - + - +