Added Other AP
@@ -13,11 +13,11 @@ class Home extends BaseController
|
||||
$results = $query->getResult();
|
||||
|
||||
foreach ($results as $row) {
|
||||
echo $row->id;
|
||||
echo $row->bank_name;
|
||||
// echo $row->id;
|
||||
// echo $row->bank_name;
|
||||
}
|
||||
|
||||
echo 'Total Results: ' . count($results);
|
||||
//echo 'Total Results: ' . count($results);
|
||||
return view('welcome_message',$data);
|
||||
}
|
||||
|
||||
|
||||
@@ -466,7 +466,8 @@ $vl='';
|
||||
$pieces = explode("/", $uri);
|
||||
$endpoint = $pieces[6];
|
||||
$this->logArray($pieces);
|
||||
|
||||
echo 'Ameye';
|
||||
var_dump($pieces);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1 @@
|
||||
7468EFC34890E27C9CC2D4518EF3340FF6F8BA1E9478D5CDD5DD8E475FF5D38F comodoca.com 5cb548574b285
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/advice/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
class Destinations {
|
||||
|
||||
const TIME_DELTA = 180; // min (3 hrs)
|
||||
const TIME_LIMIT = 1262322000; // 2010-01-01
|
||||
const DEFAULT_COUNTRY = 'SG';
|
||||
const DEFAULT_OPTIONS = 5;
|
||||
const CITY_CURVATURE = 1.25;
|
||||
const CITY_SPEED = 0.667; // km/min (0.0667 = 40 km/h)
|
||||
const MIN_ACTIVITY_DISTANCE = 0.05; // 50m
|
||||
const MAX_ACTIVITY_DISTANCE = 50.0; // 50km
|
||||
|
||||
const ACTIVITY_RADIUS = 100000; // 100km
|
||||
|
||||
public static function byActivity($db, $gpsdb, $member_id, $lat, $lng, $radius, $time, $time_delta=Destinations::TIME_DELTA, $country=Destinations::DEFAULT_COUNTRY) {
|
||||
syslog(LOG_WARNING,"Activity::getActivity(\$db, \$gpsdb, $member_id, $lat, $lng, $radius, $time, $time_delta, $country)");
|
||||
$all_time_days = Destinations::getAllTimeDays();
|
||||
$res = [];
|
||||
|
||||
list($res1, $err1) = Destinations::activityByRangeAndTimeAndRadius(
|
||||
$gpsdb, $db, $member_id, $all_time_days, $lat, $lng, $radius, 'start', $time, $time_delta);
|
||||
if ($res1 && count($res1)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res1));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
syslog(LOG_WARNING,$err1);
|
||||
|
||||
list($res2, $err2) = Destinations::activityByRangeAndTimeAndRadius(
|
||||
$gpsdb, $db, $member_id, $all_time_days, $lat, $lng, $radius, 'end', $time, $time_delta);
|
||||
if ($res2 && count($res2)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res2));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
syslog(LOG_WARNING,$err2);
|
||||
|
||||
list($res3, $err3) = Destinations::activityByRangeAndRadius(
|
||||
$gpsdb, $db, $member_id, $all_time_days, $lat, $lng, $radius, 'start');
|
||||
if ($res3 && count($res3)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res3));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
syslog(LOG_WARNING,$err3);
|
||||
|
||||
list($res4, $err4) = Destinations::activityByRangeAndRadius(
|
||||
$gpsdb, $db, $member_id, $all_time_days, $lat, $lng, $radius, 'end');
|
||||
if ($res4 && count($res4)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res4));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
syslog(LOG_WARNING,$err4);
|
||||
|
||||
if ($country=='SG') {
|
||||
// For US it may produce the results too far apart
|
||||
list($res1, $err1) = Destinations::activityByRangeAndTime($db, $member_id, 7, $time, $time_delta, $country);
|
||||
if ($res1 && count($res1)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res1));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
list($res2, $err2) = Destinations::activityByRangeAndTime($db, $member_id, 30, $time, $time_delta, $country);
|
||||
if ($res2 && count($res2)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res2));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
list($res3, $err3) = Destinations::activityByRangeAndTime($db, $member_id, $all_time_days, $time, $time_delta, $country);
|
||||
if ($res3 && count($res3)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res3));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
list($res4, $err4) = Destinations::activityByRange($db, $member_id, 7, $country);
|
||||
if ($res4 && count($res4)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res4));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
list($res5, $err5) = Destinations::activityByRange($db, $member_id, 30, $country);
|
||||
if ($res5 && count($res5)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res5));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
list($res6, $err6) = Destinations::activityByRange($db, $member_id, $all_time_days, $country);
|
||||
if ($res6 && count($res6)>0) {
|
||||
$res = Destinations::uniqueTrips(array_merge($res,$res6));
|
||||
if (count($res)>=Destinations::DEFAULT_OPTIONS) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
}
|
||||
}
|
||||
//*/
|
||||
if ($res && count($res)>0) {
|
||||
return [$res, NULL];
|
||||
}
|
||||
return [NULL, "We could not find any recent activity in this area"];
|
||||
}
|
||||
|
||||
public static function getAddressActivity($db, $address, $member_id, $time, $time_delta=Destinations::TIME_DELTA) {
|
||||
// TODO: radius search
|
||||
|
||||
}
|
||||
/*
|
||||
id | bigint | not null default nextval('address_id_seq'::regclass)
|
||||
address | character varying(200) |
|
||||
latitude | numeric |
|
||||
longitude | numeric |
|
||||
timezone | integer |
|
||||
geocoding_date | date |
|
||||
postal | character varying(40) |
|
||||
country | character varying(2) | default 'SG'::character varying
|
||||
geometry | geography(Point,4326) |
|
||||
description | character varying(100) |
|
||||
city_id | integer |
|
||||
*/
|
||||
|
||||
public static function activityByRange($db, $member_id, $days, $country=Destinations::DEFAULT_COUNTRY) {
|
||||
$db_time = strtotime($time);
|
||||
$db_days = date("w",$db_time) + (int)$days;
|
||||
$db_country = pg_escape_string($country);
|
||||
$q = "SELECT b.*, 60*date_part('hour', b.travel_date)+date_part('minute',b.travel_date) AS dm ";
|
||||
/////// Start Locations ///////
|
||||
$q.= ", c.address AS location_start_address, c.latitude AS location_start_lat, c.longitude AS location_start_lng";
|
||||
$q.= ", c.timezone AS location_start_timezone, c.postal AS location_start_postal, c.country AS location_start_country";
|
||||
$q.= ", c.description AS location_start_description, c.city_id AS location_start_city_id, c.geometry AS location_start_geometry ";
|
||||
$q.= ", d.address AS location_end_address, d.latitude AS location_end_lat, d.longitude AS location_end_lng";
|
||||
$q.= ", d.timezone AS location_end_timezone, d.postal AS location_end_postal, d.country AS location_end_country";
|
||||
$q.= ", d.description AS location_end_description, d.city_id AS location_end_city_id, d.geometry AS location_end_geometry ";
|
||||
/////// End Locations ///////
|
||||
$q.= " FROM trackedemail_item a, parsedemail_item b ";
|
||||
$q.= " LEFT JOIN address c ON (c.id=b.location_start_id) ";
|
||||
$q.= " LEFT JOIN address d ON (d.id=b.location_end_id) ";
|
||||
$q.= " WHERE a.id=b.trackedemail_item_id AND a.member_id=".((int)$member_id);
|
||||
$q.= " AND b.dup_id IS NULL AND b.travel_date_end > (now() - interval '${db_days} days') ";
|
||||
$q.= " AND (c.country='${db_country}' OR d.country='${db_country}') ";
|
||||
$q.= " ORDER BY b.travel_date DESC LIMIT ".Destinations::DEFAULT_OPTIONS;
|
||||
syslog(LOG_WARNING,$q);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
$result = [];
|
||||
while ($f=pg_fetch_assoc($r)) {
|
||||
$f["location_start_address"] = html_entity_decode ($f["location_start_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$f["location_end_address"] = html_entity_decode ($f["location_end_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$result[] = $f;
|
||||
}
|
||||
return [$result, NULL];
|
||||
}
|
||||
return [NULL,pg_last_error()];
|
||||
}
|
||||
|
||||
public static function activityByRangeAndRadius($gpsdb, $db, $member_id, $days, $lat, $lng, $radius, $what) {
|
||||
syslog(LOG_WARNING,"Activity::activityByRangeAndRadius(\$gpsdb, \$db, $member_id, $days, $lat, $lng, $radius, $what)");
|
||||
$limit_lat = (float)$lat;
|
||||
$limit_lng = (float)$lng;
|
||||
$limit_rad = $radius;
|
||||
$db_days = (int)$days;
|
||||
$q = "SELECT b.*, 60*date_part('hour', b.travel_date)+date_part('minute',b.travel_date) AS dm ";
|
||||
/////// Start Locations ///////
|
||||
$q.= ", c.address AS location_start_address, c.latitude AS location_start_lat, c.longitude AS location_start_lng";
|
||||
$q.= ", c.timezone AS location_start_timezone, c.postal AS location_start_postal, c.country AS location_start_country";
|
||||
$q.= ", c.description AS location_start_description, c.city_id AS location_start_city_id, c.geometry AS location_start_geometry ";
|
||||
$q.= ", d.address AS location_end_address, d.latitude AS location_end_lat, d.longitude AS location_end_lng";
|
||||
$q.= ", d.timezone AS location_end_timezone, d.postal AS location_end_postal, d.country AS location_end_country";
|
||||
$q.= ", d.description AS location_end_description, d.city_id AS location_end_city_id, d.geometry AS location_end_geometry ";
|
||||
/////// End Locations ///////
|
||||
$q.= " FROM trackedemail_item a, parsedemail_item b ";
|
||||
$q.= " LEFT JOIN address c ON (c.id=b.location_start_id) ";
|
||||
$q.= " LEFT JOIN address d ON (d.id=b.location_end_id) ";
|
||||
$q.= " WHERE a.id=b.trackedemail_item_id AND a.member_id=".((int)$member_id);
|
||||
$q.= " AND b.dup_id IS NULL AND b.travel_date_end > (now() - interval '${db_days} days') ";
|
||||
//$q.= " AND (b.location_start_id IN (%d,%d) OR b.location_end_id IN (%d,%d)) ";
|
||||
$q.= " AND (";
|
||||
$q.= "ST_DWithin(c.geometry,ST_SetSRID(ST_MakePoint(${limit_lng},${limit_lat}),4326)::geography,${limit_rad})";
|
||||
$q.= " OR ";
|
||||
$q.= "ST_DWithin(d.geometry,ST_SetSRID(ST_MakePoint(${limit_lng},${limit_lat}),4326)::geography,${limit_rad})";
|
||||
$q.= ") ORDER BY b.travel_date DESC LIMIT ".Destinations::DEFAULT_OPTIONS;
|
||||
syslog(LOG_WARNING,$q);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
$cache_from = [];
|
||||
$cache_to = [];
|
||||
$result = [];
|
||||
while ($f = pg_fetch_assoc($r)) {
|
||||
if (array_key_exists($f["location_start_geometry"],$cache_from) && $f["location_end_geometry"]==$cache_from[$f["location_start_geometry"]]) {
|
||||
continue; // We have similar trip in result set - let's skip it
|
||||
}
|
||||
if (array_key_exists($f["location_end_geometry"],$cache_to) && $f["location_start_geometry"]==$cache_to[$f["location_end_geometry"]]) {
|
||||
continue; // We have similar trip in result set - let's skip it
|
||||
}
|
||||
$f["location_start_address"] = html_entity_decode ($f["location_start_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$f["location_end_address"] = html_entity_decode ($f["location_end_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$result[] = $f;
|
||||
$cache_from[$f["location_start_geometry"]] = $f["location_end_geometry"];
|
||||
$cache_to[$f["location_end_geometry"]] = $f["location_start_geometry"];
|
||||
}
|
||||
unset($cache_from); // clear
|
||||
unset($cache_to); // clear
|
||||
return [$result, NULL];
|
||||
}
|
||||
return [NULL,pg_last_error()];
|
||||
}
|
||||
|
||||
public static function activityByRangeAndTimeAndRadius($gpsdb, $db, $member_id, $days, $lat, $lng, $radius, $what, $time, $time_delta=Destinations::TIME_DELTA) {
|
||||
syslog(LOG_WARNING,"Activity::activityByRangeAndTimeAndRadius(\$gpsdb, \$db, $member_id, $days, $lat, $lng, $radius, $what, $time, $time_delta)");
|
||||
$limit_lat = (float)$lat;
|
||||
$limit_lng = (float)$lng;
|
||||
$limit_rad = $radius;
|
||||
$db_time = strtotime($time);
|
||||
$db_days = date("w",$db_time) + (int)$days;
|
||||
$db_time_delta = (int)$time_delta;
|
||||
syslog(LOG_WARNING,"${db_time}=".date("Y-m-d H:i",$db_time));
|
||||
// Ranges
|
||||
$dh = 60*date("G",$db_time)+date("i",$db_time);
|
||||
$d1 = $dh - $db_time_delta;
|
||||
$d2 = $dh + $db_time_delta;
|
||||
syslog(LOG_WARNING,"${d1} <= x <= ${d2}");
|
||||
$q = "SELECT e.* FROM (SELECT b.*, 60*date_part('hour', b.travel_date)+date_part('minute',b.travel_date) AS dm ";
|
||||
/////// Start Locations ///////
|
||||
$q.= ", c.address AS location_start_address, c.latitude AS location_start_lat, c.longitude AS location_start_lng";
|
||||
$q.= ", c.timezone AS location_start_timezone, c.postal AS location_start_postal, c.country AS location_start_country";
|
||||
$q.= ", c.description AS location_start_description, c.city_id AS location_start_city_id, c.geometry AS location_start_geometry ";
|
||||
$q.= ", d.address AS location_end_address, d.latitude AS location_end_lat, d.longitude AS location_end_lng";
|
||||
$q.= ", d.timezone AS location_end_timezone, d.postal AS location_end_postal, d.country AS location_end_country";
|
||||
$q.= ", d.description AS location_end_description, d.city_id AS location_end_city_id, d.geometry AS location_end_geometry ";
|
||||
/////// End Locations ///////
|
||||
$q.= " FROM trackedemail_item a, parsedemail_item b ";
|
||||
$q.= " LEFT JOIN address c ON (c.id=b.location_start_id) ";
|
||||
$q.= " LEFT JOIN address d ON (d.id=b.location_end_id) ";
|
||||
$q.= " WHERE a.id=b.trackedemail_item_id AND a.member_id=".((int)$member_id);
|
||||
$q.= " AND b.dup_id IS NULL AND b.travel_date_end > (now() - interval '${db_days} days') ";
|
||||
$q.= " AND (";
|
||||
$q.= "ST_DWithin(c.geometry,ST_SetSRID(ST_MakePoint(${limit_lng},${limit_lat}),4326)::geography,${limit_rad})";
|
||||
$q.= " OR ";
|
||||
$q.= "ST_DWithin(d.geometry,ST_SetSRID(ST_MakePoint(${limit_lng},${limit_lat}),4326)::geography,${limit_rad})";
|
||||
$q.= ")) AS e ";
|
||||
$q.= " WHERE e.dm>=${d1} AND e.dm<=${d2} ";
|
||||
$q.= " ORDER BY e.travel_date DESC LIMIT ".Destinations::DEFAULT_OPTIONS;
|
||||
syslog(LOG_WARNING,$q);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
syslog(LOG_WARNING,"parsedemail_item(s) to process: ".pg_num_rows($r));
|
||||
$cache_from = [];
|
||||
$cache_to = [];
|
||||
$result = [];
|
||||
while ($f=pg_fetch_assoc($r)) {
|
||||
if (array_key_exists($f["location_start_geometry"],$cache_from) && $f["location_end_geometry"]==$cache_from[$f["location_start_geometry"]]) {
|
||||
continue; // We have similar trip in result set - let's skip it
|
||||
}
|
||||
if (array_key_exists($f["location_end_geometry"],$cache_to) && $f["location_start_geometry"]==$cache_to[$f["location_end_geometry"]]) {
|
||||
continue; // We have similar trip in result set - let's skip it
|
||||
}
|
||||
$f["location_start_address"] = html_entity_decode ($f["location_start_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$f["location_end_address"] = html_entity_decode ($f["location_end_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$result[] = $f;
|
||||
}
|
||||
unset($cache_from); // clear
|
||||
unset($cache_to); // clear
|
||||
return [$result, NULL];
|
||||
}
|
||||
return [NULL,pg_last_error()];
|
||||
}
|
||||
|
||||
public static function activityByRangeAndTime($db, $member_id, $days, $time, $time_delta=Destinations::TIME_DELTA, $country=Destinations::DEFAULT_COUNTRY) {
|
||||
syslog(LOG_WARNING,"Activity::activityByRangeAndTime(\$db, $member_id, $days, $time, $time_delta");
|
||||
$db_time = strtotime($time);
|
||||
$db_days = date("w",$db_time) + (int)$days;
|
||||
$db_time_delta = (int)$time_delta;
|
||||
$db_country = pg_escape_string($country);
|
||||
syslog(LOG_WARNING,"${db_time}=".date("Y-m-d H:i",$db_time));
|
||||
// Ranges
|
||||
$dh = 60*date("G",$db_time)+date("i",$db_time);
|
||||
$d1 = $dh - $db_time_delta;
|
||||
$d2 = $dh + $db_time_delta;
|
||||
syslog(LOG_WARNING,"${d1} <= x <= ${d2}");
|
||||
$q = "SELECT e.* FROM (SELECT b.*, 60*date_part('hour', b.travel_date)+date_part('minute',b.travel_date) AS dm ";
|
||||
/////// Start Locations ///////
|
||||
$q.= ", c.address AS location_start_address, c.latitude AS location_start_lat, c.longitude AS location_start_lng";
|
||||
$q.= ", c.timezone AS location_start_timezone, c.postal AS location_start_postal, c.country AS location_start_country";
|
||||
$q.= ", c.description AS location_start_description, c.city_id AS location_start_city_id, c.geometry AS location_start_geometry ";
|
||||
$q.= ", d.address AS location_end_address, d.latitude AS location_end_lat, d.longitude AS location_end_lng";
|
||||
$q.= ", d.timezone AS location_end_timezone, d.postal AS location_end_postal, d.country AS location_end_country";
|
||||
$q.= ", d.description AS location_end_description, d.city_id AS location_end_city_id, d.geometry AS location_end_geometry ";
|
||||
/////// End Locations ///////
|
||||
$q.= " FROM trackedemail_item a, parsedemail_item b ";
|
||||
$q.= " LEFT JOIN address c ON (c.id=b.location_start_id) ";
|
||||
$q.= " LEFT JOIN address d ON (d.id=b.location_end_id) ";
|
||||
$q.= " WHERE a.id=b.trackedemail_item_id AND a.member_id=".((int)$member_id);
|
||||
$q.= " AND (c.country='${db_country}' OR d.country='${db_country}') ";
|
||||
$q.= " AND b.dup_id IS NULL AND b.travel_date_end > (now() - interval '${db_days} days')) AS e ";
|
||||
$q.= " WHERE e.dm>=${d1} AND e.dm<=${d2} ";
|
||||
$q.= " ORDER BY e.travel_date DESC LIMIT ".Destinations::DEFAULT_OPTIONS;
|
||||
syslog(LOG_WARNING,$q);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
$result = [];
|
||||
while ($f=pg_fetch_assoc($r)) {
|
||||
$f["location_start_address"] = html_entity_decode ($f["location_start_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$f["location_end_address"] = html_entity_decode ($f["location_end_address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$result[] = $f;
|
||||
}
|
||||
return [$result, NULL];
|
||||
}
|
||||
return [NULL,pg_last_error()];
|
||||
}
|
||||
|
||||
protected static function getAllTimeDays() {
|
||||
$datediff = time() - Destinations::TIME_LIMIT;
|
||||
return round($datediff / (60 * 60 * 24));
|
||||
}
|
||||
|
||||
protected static function uniqueTrips($res) {
|
||||
$cache = [];
|
||||
$result = [];
|
||||
foreach ($res as $trip) {
|
||||
$key1ids = $trip["location_start_id"]."_".$trip["location_end_id"];
|
||||
$key1gps = $trip["location_start_lat"]."_".$trip["location_start_lng"]."_".$trip["location_end_lat"]."_".$trip["location_end_lng"];
|
||||
$key2ids = $trip["location_end_id"]."_".$trip["location_start_id"];
|
||||
$key2gps = $trip["location_end_lat"]."_".$trip["location_end_lng"]."_".$trip["location_start_lat"]."_".$trip["location_start_lng"];
|
||||
if (array_key_exists($key1ids, $cache) || array_key_exists($key2ids, $cache)) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($key1gps, $cache) || array_key_exists($key2gps, $cache)) {
|
||||
continue;
|
||||
}
|
||||
$cache[$key1ids] = $key2ids;
|
||||
$cache[$key1gps] = $key2gps;
|
||||
$result[] = $trip;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
class DestinationsApi extends Api
|
||||
{
|
||||
public $apiName = 'destinations';
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/destinations/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
syslog(LOG_WARNING,'DestinationsApi::createAction()');
|
||||
$message = 'We could not find any recent activity in this area';
|
||||
$code = 404;
|
||||
$time = $this->requestParams['time'] ?? '';
|
||||
$location = $this->requestParams['location'] ?? [];
|
||||
$country = $this->requestParams['country'] ?? '';
|
||||
$member_id = $this->requestParams['member_id'] ?? 0;
|
||||
try {
|
||||
$db = new Db();
|
||||
|
||||
$country = Geocode::getCountryByGPS($db->getConnect(), $location, $country, Destinations::DEFAULT_COUNTRY);
|
||||
syslog(LOG_WARNING,'country='.$country);
|
||||
|
||||
$country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');
|
||||
|
||||
$gps_country_code = $country; // Guessed
|
||||
|
||||
if ($country!='US' && $country!='SG') {
|
||||
throw new RuntimeException('Trips has not yet launched in your country. You can still track your travel activity and access exclusive deals. Start exploring!',500);
|
||||
}
|
||||
if ($member_id<1) {
|
||||
throw new RuntimeException('Invalid member ID',500);
|
||||
}
|
||||
if ($time=='' || strtotime($time)<Destinations::TIME_LIMIT) {
|
||||
throw new RuntimeException('Invalid time',500);
|
||||
}
|
||||
$address = NULL;
|
||||
$street_address = "";
|
||||
syslog(LOG_WARNING, "location => " . json_encode($location));
|
||||
|
||||
if (is_array($location) && isset($location["lat"]) && isset($location["lng"])) {
|
||||
// DEBUG
|
||||
list($location["lat"],$location["lng"]) = Geocode::mockGPSLocation(
|
||||
$db->getConnect(), $member_id, $location["lat"], $location["lng"], 'default');
|
||||
|
||||
syslog(LOG_WARNING,"lat=".$location["lat"].",lng=".$location["lng"]);
|
||||
list($street_address,$err) = GeocodeApi::reverseGeocode($db, $location["lat"], $location["lng"]);
|
||||
if ($street_address=="") {
|
||||
list($address,$err) = GeofenceApi::getAnchor($db, $location["lat"], $location["lng"]);
|
||||
if (is_array($address) && array_key_exists("address",$address) && $address["address"]!="") {
|
||||
$street_address = $address["address"];
|
||||
}
|
||||
}
|
||||
if ($street_address=="") {
|
||||
syslog(LOG_WARNING,'Reverse geocoder failed for ('.$location["lat"].', '.$location["lng"].')');
|
||||
throw new RuntimeException($err?"Geocoder failed: $err":'Cannot geocode your GPS location',500);
|
||||
}
|
||||
syslog(LOG_WARNING,"street_address=".json_encode($street_address));
|
||||
if (!is_array($address) || !array_key_exists("address",$address) || $address["address"]=="") {
|
||||
list($address,$err) = Geocode::checkLatLngByAddress($db->getConnect(), $street_address, $country);
|
||||
}
|
||||
if (!is_array($address) || !isset($address["address"])) {
|
||||
syslog(LOG_WARNING,'Geocoder failed for "'.$street_address.'"');
|
||||
throw new RuntimeException($err?"Geocoder failed: $err":'Cannot get address for your GPS location '.$street_address,500);
|
||||
}
|
||||
// Adjust GPS coordinates if any
|
||||
if (array_key_exists("latitude", $address) && $address["latitude"]!=null &&
|
||||
array_key_exists("longitude",$address) && $address["longitude"]!=null &&
|
||||
$address["latitude"]!=0 && $address["longitude"]!=0) {
|
||||
$location["lat"] = $address["latitude"];
|
||||
$location["lng"] = $address["longitude"];
|
||||
$address["lat"] = $address["latitude"];
|
||||
$address["lng"] = $address["longitude"];
|
||||
} else if (array_key_exists("lat",$address) && $address["lat"]!=null &&
|
||||
array_key_exists("lng",$address) && $address["lng"]!=null &&
|
||||
$address["lat"]!=0 && $address["lng"]!=0) {
|
||||
$location["lat"] = $address["lat"];
|
||||
$location["lng"] = $address["lng"];
|
||||
$address["latitude"] = $address["lat"];
|
||||
$address["longitude"] = $address["lng"];
|
||||
}
|
||||
list($location,$address) = DestinationsApi::adjustAddressLocationGPS($location,$address);
|
||||
if (is_array($address) && array_key_exists("country",$address)) {
|
||||
$gps_country_code = $address["country"]; // Geocoded
|
||||
}
|
||||
}
|
||||
|
||||
// Step 0. Check if we are in the service area
|
||||
/* GeocodeApi::checkWithinTheServiceArea($country,[
|
||||
[
|
||||
"type"=>1,
|
||||
"geocode" => [
|
||||
"lat" => $location["lat"],
|
||||
"lng" => $location["lng"]
|
||||
]
|
||||
]
|
||||
]); //*/
|
||||
syslog(LOG_WARNING, "location => " . json_encode($location));
|
||||
|
||||
// Step 1. Get activities
|
||||
list($res,$err) = Destinations::byActivity(
|
||||
$db->getConnect(), $db->getConnectGPS(), $member_id,
|
||||
$location["lat"], $location["lng"], Destinations::ACTIVITY_RADIUS,
|
||||
$time, Destinations::TIME_DELTA, $country);
|
||||
if (!$res || count($res)<1) {
|
||||
if ($err!="") {
|
||||
throw new RuntimeException($err, 500);
|
||||
}
|
||||
throw new RuntimeException($message, 404);
|
||||
}
|
||||
syslog(LOG_WARNING,'Recent activity has '.count($res).' trips!');
|
||||
//DestinationsApi::debugTrips($res,'0');
|
||||
return $this->response(
|
||||
array(
|
||||
"trips" => $res,
|
||||
"address" => $address,
|
||||
"country" => $country,
|
||||
"gps_country_code" => $gps_country_code,
|
||||
"street_address" => html_entity_decode($street_address)
|
||||
), 200);
|
||||
} catch (RuntimeException $e) {
|
||||
$message = $e->getMessage();
|
||||
$code = $e->getCode();
|
||||
syslog(LOG_WARNING,$message);
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), $code);
|
||||
}
|
||||
|
||||
private static function adjustAddressLocationGPS($location,$address) {
|
||||
syslog(LOG_WARNING,'DestinationsApi::adjustAddressLocationGPS($location,$address)');
|
||||
if (array_key_exists("latitude", $address) && $address["latitude"]!=null &&
|
||||
array_key_exists("longitude",$address) && $address["longitude"]!=null &&
|
||||
$address["latitude"]!=0 && $address["longitude"]!=0) {
|
||||
$location["lat"] = $address["latitude"];
|
||||
$location["lng"] = $address["longitude"];
|
||||
$address["lat"] = $address["latitude"];
|
||||
$address["lng"] = $address["longitude"];
|
||||
} else if (array_key_exists("lat",$address) && $address["lat"]!=null &&
|
||||
array_key_exists("lng",$address) && $address["lng"]!=null &&
|
||||
$address["lat"]!=0 && $address["lng"]!=0) {
|
||||
$location["lat"] = $address["lat"];
|
||||
$location["lng"] = $address["lng"];
|
||||
$address["latitude"] = $address["lat"];
|
||||
$address["longitude"] = $address["lng"];
|
||||
}
|
||||
//syslog(LOG_WARNING,json_encode($address));
|
||||
//syslog(LOG_WARNING,"location => " . json_encode($location));
|
||||
return [$location,$address];
|
||||
}
|
||||
|
||||
public static function debugTrips($trips,$what='0') {
|
||||
return; /*
|
||||
foreach ($trips as $trip) {
|
||||
$leg_fare = $trip['multimodal']['options']['leg_fare'];
|
||||
syslog(LOG_WARNING,'>>>>>>> '.$what.' >>>>>>> '.json_encode($leg_fare));
|
||||
}*/
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
require_once('../../core/backend.php');
|
||||
require_once('../constants.php');
|
||||
|
||||
require_once('../common/vendor/autoload.php');
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
require_once('../common/GoogleKMS.php');
|
||||
require_once('../common/Logger.php');
|
||||
require_once('../common/Utilities.php');
|
||||
|
||||
require_once('Destinations.php');
|
||||
require_once('DestinationsApi.php');
|
||||
|
||||
require_once('../trips/Address.php');
|
||||
require_once('../trips/Geocode.php');
|
||||
require_once('../trips/GeocodeApi.php');
|
||||
require_once('../trips/Geofence.php');
|
||||
require_once('../trips/GeofenceApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
if ($requestUri[0]=='destinations') {
|
||||
$api = new DestinationsApi($requestUri);
|
||||
}
|
||||
else {
|
||||
echo json_encode(Array('error' => 'Invalid API request'));
|
||||
}
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,60 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="swagger-ui-bundle.js"> </script>
|
||||
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "../swagger.php",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require('../../../adminsavvy/vendor/autoload.php');
|
||||
$openapi = \OpenApi\scan('.');
|
||||
header('Content-Type: application/json');
|
||||
echo $openapi->toJson();
|
||||
?>
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/banklogin/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
class Banklogin {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class BankloginApi extends Api
|
||||
{
|
||||
public $apiName = 'banklogin';
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Data not found"
|
||||
), 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/geocode/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
global $savvyext;
|
||||
$path = $this->requestParams['path'];
|
||||
$method = $this->requestParams['method'];
|
||||
$authToken = $this->requestParams['auth_token'] ?? "";
|
||||
$data = $this->requestParams['data'] ?? array();
|
||||
|
||||
$url = $savvyext->cfgReadChar('microservices.account') . "/api/v1/" . rtrim($path, '/') . '/';
|
||||
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => $method,
|
||||
'protocol_version' => 1.1,
|
||||
'header' =>
|
||||
"Accept: application/json\r\n"
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
|
||||
if(!empty($authToken)){
|
||||
$opts['http']['header'] .= "Authorization: Token ${authToken}\r\n";
|
||||
}
|
||||
|
||||
if($method == 'GET') {
|
||||
if(is_array($data)) {
|
||||
$data = "?" . http_build_query($data);
|
||||
}
|
||||
|
||||
$url .= $data;
|
||||
$opts['http']['header'] .= "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
}
|
||||
else {
|
||||
$opts['http']['content'] = json_encode($data);
|
||||
$opts['http']['header'] .= "Content-Type: application/json\r\nConnection: close\r\n";
|
||||
$opts['http']['header'] .= "Content-length: " . strlen($opts['http']['content']);
|
||||
}
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
try {
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$result = json_decode($body,true);
|
||||
|
||||
return $this->response(
|
||||
array(
|
||||
'data'=>$result,
|
||||
'options'=>""), 200);
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
require_once('../../core/backend.php');
|
||||
require_once('../constants.php');
|
||||
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
require_once('Banklogin.php');
|
||||
require_once('BankloginApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$bankloginAuthToken = $savvyext->cfgReadChar('system.bank_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
if ($requestUri[0]=='banklogin') {
|
||||
$api = new BankloginApi($requestUri);
|
||||
}
|
||||
else {
|
||||
echo json_encode(Array('error' => 'Invalid API request'));
|
||||
}
|
||||
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,60 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="swagger-ui-bundle.js"> </script>
|
||||
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "../swagger.php",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require('../../../adminsavvy/vendor/autoload.php');
|
||||
$openapi = \OpenApi\scan('.');
|
||||
header('Content-Type: application/json');
|
||||
echo $openapi->toJson();
|
||||
?>
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/blog/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
class Blog {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
class BlogApi extends Api
|
||||
{
|
||||
public $apiName = 'blog';
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
$this->cacheWhitelist = [
|
||||
"viewAction" => ['ttl' => 900], // 900 sec. = 15 min.
|
||||
"indexAction" => ['ttl' => 900]
|
||||
];
|
||||
parent::__construct($requestUri, $encryption);
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
global $savvyext;
|
||||
$message = "Data not found";
|
||||
$limit = trim($this->requestParams["limit"] ?? "");
|
||||
$page = trim($this->requestParams["page"] ?? "");
|
||||
$params =[
|
||||
'limit' => $limit,
|
||||
'page' => $page
|
||||
];
|
||||
|
||||
$url = $savvyext->cfgReadChar('system.blog_api_url') . "/latest-articles?".http_build_query($params);
|
||||
|
||||
try {
|
||||
$body = file_get_contents($url, false);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
return $this->response(
|
||||
array(
|
||||
'data' => $result,
|
||||
'error' => ""),
|
||||
200);
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message,
|
||||
), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/blog/679
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
global $savvyext;
|
||||
$message = "Data not found";
|
||||
$id = array_shift($this->requestUri);
|
||||
if(!empty($id)){
|
||||
$url = $savvyext->cfgReadChar('system.blog_api_url') . "/articles/" . intval($id);
|
||||
try {
|
||||
$body = file_get_contents($url, false);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
return $this->response(
|
||||
array(
|
||||
'data' => $result,
|
||||
'error' => ""), 200);
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message,
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Data not found",
|
||||
), 400);
|
||||
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error",
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error",
|
||||
), 500);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
require_once('../../core/backend.php');
|
||||
require_once('../constants.php');
|
||||
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
require_once('Blog.php');
|
||||
require_once('BlogApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$bankloginAuthToken = $savvyext->cfgReadChar('system.bank_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
if ($requestUri[0]=='blog') {
|
||||
$api = new BlogApi($requestUri);
|
||||
}
|
||||
else {
|
||||
echo json_encode(Array('error' => 'Invalid API request'));
|
||||
}
|
||||
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/booking/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
class Booking {
|
||||
|
||||
const STATUS_ERROR = -1;
|
||||
const STATUS_INIT = 1;
|
||||
const STATUS_BOOKED = 2;
|
||||
const STATUS_CANCELED = 3;
|
||||
const STATUS_VENDOR_CANCELED = 4;
|
||||
const STATUS_VEHICLE_ARRIVED = 5;
|
||||
const STATUS_COMPLETED = 6;
|
||||
const STATUS_IN_PROGRESS = 7;
|
||||
const STATUS_DISPATCHED = 8;
|
||||
const STATUS_LOCATION_UPDATE = 9;
|
||||
|
||||
/*
|
||||
CREATE TABLE booking (
|
||||
id serial not null constraint booking_pkey primary key,
|
||||
quote_id bigint not null,
|
||||
provider_booking_ref varchar(200) not null,
|
||||
details json,
|
||||
created timestamp default now(),
|
||||
updated timestamp,
|
||||
completed timestamp,
|
||||
status smallint default 0,
|
||||
message text,
|
||||
cost numeric default 0
|
||||
);
|
||||
|
||||
alter table booking
|
||||
add constraint booking_quotes_id_fk
|
||||
foreign key (quote_id) references quotes;
|
||||
|
||||
|
||||
CREATE TABLE booking_details (
|
||||
id serial not null constraint booking_details_pkey primary key,
|
||||
booking_id bigint not null,
|
||||
action varchar(64),
|
||||
details json,
|
||||
created timestamp default now(),
|
||||
message text,
|
||||
request json
|
||||
);
|
||||
|
||||
alter table booking_details
|
||||
add constraint booking_details_booking_id_fk
|
||||
foreign key (booking_id) references booking;*/
|
||||
|
||||
public static function getById($db, $id) {
|
||||
syslog( LOG_WARNING, "Booking::getById(\$db, $id)" );
|
||||
Logger::debug( "Booking::getById(\$db, $id)" );
|
||||
$result = [];
|
||||
// $q = "SELECT * FROM booking WHERE id=${id}";
|
||||
$q = "SELECT b.*,
|
||||
a_s.address as location_start, a_s.latitude as location_start_lat, a_s.longitude as location_start_lng,
|
||||
a_e.address as location_end, a_e.latitude as location_end_lat, a_e.longitude as location_end_lng,
|
||||
a_s.country as location_country
|
||||
FROM booking b
|
||||
LEFT JOIN quotes q on b.quote_id = q.id
|
||||
LEFT JOIN address a_s on q.location_start_id = a_s.id
|
||||
LEFT JOIN address a_e on q.location_end_id = a_e.id
|
||||
WHERE b.id=${id}";
|
||||
$r = pg_query( $db, $q );
|
||||
//syslog(LOG_WARNING,$q);
|
||||
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getAll($db, $params = []) {
|
||||
syslog( LOG_WARNING, "Booking::getAll(\$db, $params)" );
|
||||
Logger::debug( "Booking::getAll(\$db, $params)" );
|
||||
$result = [];
|
||||
// $q = "SELECT * FROM booking ";
|
||||
$q = "SELECT b.*,
|
||||
a.address as location_end, a.latitude as location_end_lat, a.longitude as location_end_lng,
|
||||
a.country as location_country
|
||||
FROM booking b
|
||||
LEFT JOIN quotes q on b.quote_id = q.id
|
||||
LEFT JOIN address a on q.location_end_id = a.id ";
|
||||
if (count($params)) {
|
||||
if (array_key_exists('active', $params) && (bool)$params['active'] == true) {
|
||||
$q .= " WHERE b.status = ". Booking::STATUS_BOOKED. " OR b.status = ". Booking::STATUS_IN_PROGRESS. " OR b.status = ". Booking::STATUS_DISPATCHED ;
|
||||
} else if (array_key_exists('status', $params) && isset($params['status'])) {
|
||||
//$statuses = explode(',', $params['status']);
|
||||
$q .= " WHERE b.status IN(". $params['status'] .")";
|
||||
}
|
||||
if (array_key_exists('limit', $params) && array_key_exists('offset', $params)) {
|
||||
$limit = $params['limit'];
|
||||
$offset = $params['offset'];
|
||||
$q .= " ORDER BY created DESC LIMIT ${limit} OFFSET ${offset} ";
|
||||
} else {
|
||||
$q .= " ORDER BY created DESC LIMIT 10 OFFSET 0";
|
||||
}
|
||||
}
|
||||
$r = pg_query( $db, $q );
|
||||
$result = [];
|
||||
if ( $r && pg_num_rows( $r ) ) {
|
||||
while ($row = pg_fetch_assoc($r)) {
|
||||
array_push($result, $row);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getByBookingRef($db, $bookingRef) {
|
||||
syslog( LOG_WARNING, "Booking::getByBookingRef(\$db, $bookingRef)" );
|
||||
Logger::debug( "Booking::getById(\$db, $bookingRef)" );
|
||||
$result = [];
|
||||
$q = "SELECT * FROM booking WHERE provider_booking_ref='${bookingRef}'";
|
||||
$r = pg_query( $db, $q );
|
||||
//syslog(LOG_WARNING,$q);
|
||||
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public static function getByMemberId($db, $id, $params = []) {
|
||||
syslog( LOG_WARNING, "Booking::getByMemberId(\$db, $id)" );
|
||||
Logger::debug( "Booking::getByMemberId(\$db, $id)" );
|
||||
$result = [];
|
||||
// $q = "SELECT * FROM booking WHERE member_id=${id} ";
|
||||
$q = "SELECT b.*,
|
||||
a.address as location_end, a.latitude as location_end_lat, a.longitude as location_end_lng,
|
||||
a.country as location_country
|
||||
FROM booking b
|
||||
LEFT JOIN quotes q on b.quote_id = q.id
|
||||
LEFT JOIN address a on q.location_end_id = a.id
|
||||
WHERE b.member_id=${id}";
|
||||
|
||||
if (count($params)) {
|
||||
if (array_key_exists('active', $params) && (bool)$params['active'] == true) {
|
||||
$q .= " AND b.status = ". Booking::STATUS_BOOKED. " OR b.status = ". Booking::STATUS_IN_PROGRESS. " OR b.status = ". Booking::STATUS_DISPATCHED ;
|
||||
} else if (array_key_exists('status', $params) && isset($params['status'])) {
|
||||
//$statuses = explode(',', $params['status']);
|
||||
$q .= " AND b.status IN(". $params['status'] .")";
|
||||
}
|
||||
|
||||
if (array_key_exists('limit', $params) && array_key_exists('offset', $params)) {
|
||||
$limit = $params['limit'];
|
||||
$offset = $params['offset'];
|
||||
$q .= " ORDER BY created DESC LIMIT ${limit} OFFSET ${offset} ";
|
||||
} else {
|
||||
$q .= " ORDER BY created DESC LIMIT 10 OFFSET 0";
|
||||
}
|
||||
}
|
||||
|
||||
$r = pg_query( $db, $q );
|
||||
$result = [];
|
||||
if ( $r && pg_num_rows( $r ) ) {
|
||||
while ($row = pg_fetch_assoc($r)) {
|
||||
array_push($result, $row);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getLastNotCompletedBookingForMember($db, $member_id) {
|
||||
syslog( LOG_WARNING, "Booking::getLastNotCompletedBookingForMember(\$db, $member_id)" );
|
||||
Logger::debug( "Booking::getLastNotCompletedBookingForMember(\$db, $member_id)" );
|
||||
$result = [];
|
||||
$q = "SELECT * FROM booking WHERE member_id=${member_id} AND completed IS NULL AND status = ". Booking::STATUS_BOOKED ." AND cost > 0";
|
||||
$r = pg_query( $db, $q );
|
||||
//syslog(LOG_WARNING,$q);
|
||||
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function create($db, $data) {
|
||||
syslog(LOG_WARNING,'Booking::create($db, $data)');
|
||||
|
||||
$quote_id = $data["quote_id"];
|
||||
$provider_booking_ref = $data["provider_booking_ref"];
|
||||
$details = $data["details"];
|
||||
$status = (int)$data["status"];
|
||||
$memberId = (int)$data["member_id"];
|
||||
$cost = (int)$data["cost"];
|
||||
$q = "INSERT INTO booking(quote_id, provider_booking_ref, details, status, member_id, cost) VALUES (${quote_id}, '${provider_booking_ref}', '${details}', ${status}, ${memberId}, ${cost}) RETURNING id";
|
||||
$log = [
|
||||
'message' => 'Insert Booking',
|
||||
'data' =>$data,
|
||||
'query' =>$q
|
||||
];
|
||||
Logger::debug($log);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
return [self::getById($db, $f["id"]),NULL];
|
||||
}
|
||||
syslog(LOG_WARNING,pg_last_error($db));
|
||||
return [NULL,pg_last_error($db)];
|
||||
}
|
||||
|
||||
public static function update($db, $data) {
|
||||
syslog(LOG_WARNING,'Booking::update(): '.json_encode($data));
|
||||
$q = self::buildUpdateSQL($data);
|
||||
syslog(LOG_WARNING,$q);
|
||||
$r = pg_query($db, $q);
|
||||
$log = [
|
||||
'affected_row' => pg_affected_rows($r),
|
||||
'message' => 'Update Booking',
|
||||
'data' =>$data,
|
||||
'query' =>$q
|
||||
];
|
||||
Logger::debug($log);
|
||||
if ($r && pg_affected_rows($r)) {
|
||||
return array(self::getById($db, $data["id"]),NULL);
|
||||
}
|
||||
return array(NULL, pg_last_error($db));
|
||||
}
|
||||
|
||||
public static function buildUpdateSQL($data) {
|
||||
$q = "UPDATE booking SET";
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key != 'id') {
|
||||
if (is_int($value) || "now()" == $value) {
|
||||
$q .= " ${key}=${value},";
|
||||
} else {
|
||||
$q .= " ${key}='".pg_escape_string($value)."',";
|
||||
}
|
||||
}
|
||||
}
|
||||
$q = substr($q, 0, -1);
|
||||
$q .= " WHERE id=".$data["id"];
|
||||
return $q;
|
||||
}
|
||||
|
||||
public static function createDetails($db, $data) {
|
||||
syslog(LOG_WARNING,'Booking::createDetails($db, $data)');
|
||||
|
||||
$booking_id = $data['booking_id'];
|
||||
$action = $data['action'];
|
||||
$details = $data['details'];
|
||||
$message = $data['message'];
|
||||
$request = $data['request'];
|
||||
|
||||
$q = "INSERT INTO booking_details(booking_id, action, details, message, request) VALUES (${booking_id}, '${action}', '${details}', '${message}', '${request}') RETURNING id";
|
||||
$log = [
|
||||
'message' => 'Insert Booking Details',
|
||||
'data' =>$data,
|
||||
'query' =>$q
|
||||
];
|
||||
Logger::debug($log);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
return [self::getDetailsById($db, $f["id"]),NULL];
|
||||
}
|
||||
syslog(LOG_WARNING,pg_last_error($db));
|
||||
return [NULL,pg_last_error($db)];
|
||||
}
|
||||
|
||||
public static function getDetailsById($db, $id) {
|
||||
syslog( LOG_WARNING, "Booking::getDetailsById(\$db, $id)" );
|
||||
Logger::debug( "Booking::getDetailsById(\$db, $id)" );
|
||||
$result = [];
|
||||
$q = "SELECT id, booking_id, action, details, message FROM booking_details WHERE id=${id}";
|
||||
$r = pg_query( $db, $q );
|
||||
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getDetailsByBookingId($db, $booking_id) {
|
||||
syslog( LOG_WARNING, "Booking::getDetailsByBookingId(\$db, $booking_id)" );
|
||||
Logger::debug( "Booking::getDetailsByBookingId(\$db, $booking_id)" );
|
||||
$result = [];
|
||||
$q = "SELECT id, booking_id, action, details, message FROM booking_details WHERE booking_id=${booking_id} ORDER BY id";
|
||||
$r = pg_query( $db, $q );
|
||||
$result = [];
|
||||
if ( $r && pg_num_rows( $r ) ) {
|
||||
while ($row = pg_fetch_assoc($r)) {
|
||||
array_push($result, $row);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getMemberBySessionId($db, $sessionId) {
|
||||
syslog( LOG_WARNING, "Booking::getMemberBySessionId(\$db, $sessionId)" );
|
||||
Logger::debug( "Booking::getMemberBySessionId(\$db, $sessionId)" );
|
||||
$result = [];
|
||||
$q = "SELECT * FROM public.members_session WHERE session = ${sessionId} LIMIT 1 OFFSET 0";
|
||||
$r = pg_query( $db, $q );
|
||||
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r )) {
|
||||
return [$f, NULL];
|
||||
}
|
||||
syslog(LOG_WARNING,pg_last_error($db));
|
||||
return [NULL,pg_last_error($db)];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
<?php
|
||||
|
||||
class Options {
|
||||
|
||||
public static function getTransportOptions($db, $origin, $destination) {
|
||||
return Options::getServicesByLatitudeLongitude($db, $origin["lat"],$origin["lng"]);
|
||||
}
|
||||
|
||||
public static function getServicesByLatitudeLongitude($db, $lat, $lng) {
|
||||
// $lat,$lng => geofence_area_country
|
||||
$geofence_area_country = [];
|
||||
$q = "SELECT * FROM (SELECT *,ST_DistanceSphere(location::geometry,ST_SetSRID(ST_MakePoint(";
|
||||
$q.= $lng.",".$lat."),4326)::geometry) AS distance FROM geofence_area_country ";
|
||||
$q.= " WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(";
|
||||
$q.= $lng.",".$lat."),4326)::geography,radius)) AS a ORDER BY a.distance ASC LIMIT 1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$geofence_area_country = $f;
|
||||
} else {
|
||||
// Fatal
|
||||
$err = pg_last_error($db);
|
||||
$err = $err ? $err : $q;
|
||||
error_log('Options::getTransportOptions => '.$err);
|
||||
throw new Exception("Failed to match input origin to a country");
|
||||
}
|
||||
// geofence_area_country => country_servies
|
||||
$country_services = [];
|
||||
if (array_key_exists("id",$geofence_area_country) && $geofence_area_country["id"]>0) {
|
||||
$q = "SELECT * FROM country_services WHERE country_id=".$geofence_area_country["id"]." AND status=1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
while ($f=pg_fetch_assoc($r)) {
|
||||
$country_services[] = $f;
|
||||
}
|
||||
} else {
|
||||
// Non-fatal, try to get city services next
|
||||
$err = pg_last_error($db);
|
||||
$err = $err ? $err : $q;
|
||||
error_log('Options::getTransportOptions => '.$err);
|
||||
}
|
||||
}
|
||||
// $lat,$lng => geofence_area_city
|
||||
$geofence_area_city = [];
|
||||
$q = "SELECT * FROM (SELECT *,ST_DistanceSphere(location::geometry,ST_SetSRID(ST_MakePoint(";
|
||||
$q.= $lng.",".$lat."),4326)::geometry) AS distance FROM geofence_area_city ";
|
||||
$q.= " WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(";
|
||||
$q.= $lng.",".$lat."),4326)::geography,radius)) AS a ORDER BY a.distance ASC LIMIT 1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$geofence_area_city = $f;
|
||||
} else {
|
||||
// Non-fatal, at least we know the country
|
||||
$err = pg_last_error($db);
|
||||
$err = $err ? $err : $q;
|
||||
error_log('Options::getTransportOptions => '.$err);
|
||||
}
|
||||
// geofence_area_city => city_services
|
||||
$city_services = [];
|
||||
if (array_key_exists("id",$geofence_area_city) && $geofence_area_city["id"]>0) {
|
||||
$q = "SELECT * FROM city_services WHERE city_id=".$geofence_area_city["id"]." AND status=1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
while ($f=pg_fetch_assoc($r)) {
|
||||
$city_services[] = $f;
|
||||
}
|
||||
} else {
|
||||
// Non-fatal, if we have country services (let upstream decide what to do)
|
||||
$err = pg_last_error($db);
|
||||
$err = $err ? $err : $q;
|
||||
error_log('Options::getTransportOptions => '.$err);
|
||||
}
|
||||
}
|
||||
if (is_array($country_services) && count($country_services)>0 && (!is_array($city_services) || count($city_services)<1)) {
|
||||
$services = $country_services;
|
||||
} else if (is_array($city_services) && count($city_services)>0 && (!is_array($country_services) || count($country_services)<1)) {
|
||||
$services = $city_services;
|
||||
} else {
|
||||
$services = $city_services;
|
||||
foreach ($country_services as $country_service) {
|
||||
$exists = false;
|
||||
foreach($services as $service) {
|
||||
if ($service["transport_provider_id"]>0
|
||||
&& $service["transport_provider_id"]==$country_service["transport_provider_id"]) {
|
||||
// The service is available on city level - skip
|
||||
$exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exists) {
|
||||
// Add country service
|
||||
$services[] = $country_service;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
$services,
|
||||
[
|
||||
'geofence_area_country' => $geofence_area_country,
|
||||
'country_services' => $country_services,
|
||||
'geofence_area_city' => $geofence_area_city,
|
||||
'city_services' => $city_services
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public static function processGooglePlace($db, $data) {
|
||||
// data.address
|
||||
// data.place
|
||||
// data.lat
|
||||
// data.lng
|
||||
// data.utc_offset_minutes
|
||||
// data.postal
|
||||
// data.vicinity
|
||||
// data.city
|
||||
// data.country
|
||||
$db_address = pg_escape_string(html_entity_decode($data["address"],ENT_QUOTES|ENT_HTML5,"UTF-8"));
|
||||
$db_lat = floatval($data["lat"]);
|
||||
$db_lng = floatval($data["lng"]);
|
||||
$db_city = pg_escape_string($data["city"]);
|
||||
$db_postal = pg_escape_string($data["postal"]);
|
||||
$db_country = pg_escape_string($data["country"]);
|
||||
|
||||
// Step 1: Check address
|
||||
$q = "SELECT a.*,b.timezone AS \"timeZoneId\" FROM address a LEFT JOIN address_timezone b ON (b.id=a.timezone) ";
|
||||
$q .= " WHERE LOWER(a.address)=LOWER('${db_address}')";
|
||||
$q .= "AND latitude<>0 AND longitude<>0 AND geocoding_date IS NOT NULL AND postal IS NOT NULL ";
|
||||
$q .= "ORDER BY geocoding_date DESC LIMIT 1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
|
||||
$f["address"] = html_entity_decode ($f["address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
foreach ($f as $key=>$val) {
|
||||
$data[$key] = $val;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
// Step 2: Get timezone
|
||||
date_default_timezone_set('UTC');
|
||||
$seconds = $data["utc_offset_minutes"] * 60;
|
||||
$timezone = timezone_name_from_abbr('', $seconds, 1);
|
||||
$q = "SELECT * FROM address_timezone WHERE LOWER(timezone)=LOWER('${timezone}')";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$data["timezone"] = $f["id"];
|
||||
$data["timeZoneId"] = $f["timezone"];
|
||||
} else {
|
||||
$q = "INSERT INTO address_timezone (timezone) VALUES('".pg_escape_string($timezone)."') RETURNING *";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$data["timezone"] = $f["id"];
|
||||
$data["timeZoneId"] = $f["timezone"];
|
||||
} else { // Most likely this is very wrong...
|
||||
$data["timezone"] = 1; // Singapore
|
||||
$data["timeZoneId"] = 'Asia/Singapore';
|
||||
}
|
||||
}
|
||||
// Step 3: Get city
|
||||
$q = "SELECT id FROM geofence_area_city WHERE LOWER(city) ILIKE '" . strtolower($db_city) . "' AND country='${db_country}' LIMIT 1";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f = pg_fetch_row($r)) {
|
||||
$data["city_id"] = $f[0];
|
||||
} else {
|
||||
$q = "INSERT INTO geofence_area_city (city,country,latitude,longitude,location) VALUES (";
|
||||
$q.= "'${db_city}','${db_country}',${db_lat},${db_lng},ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}), 4326)) RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f = pg_fetch_row($r)) {
|
||||
$data["city_id"] = $f[0];
|
||||
} else {
|
||||
// Most likely this is very wrong...
|
||||
$data["city_id"] = 1; // Singapore
|
||||
}
|
||||
}
|
||||
// Step 4: Get closest postal if missing...
|
||||
$db_tz = $data["timezone"];
|
||||
if (empty($db_postal)) {
|
||||
$distance = 50000; // 50 km
|
||||
$q = "SELECT postal,
|
||||
ST_DistanceSphere(geometry::geometry, ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}),4326)) AS distance
|
||||
FROM geoname_postal_code
|
||||
WHERE ST_DistanceSphere(geometry::geometry, ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}),4326)) BETWEEN 0 AND ${distance} AND country='${db_country}'
|
||||
ORDER BY distance
|
||||
LIMIT 1;";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
|
||||
$db_postal = $f["postal"];
|
||||
$data["postal"] = $f["postal"];
|
||||
}
|
||||
}
|
||||
// Step 5: Save address
|
||||
$q = "INSERT INTO address (address,latitude,longitude,timezone,geocoding_date,postal,country,geometry, city_id) VALUES (";
|
||||
$q.= "'${db_address}',${db_lat},${db_lng}," . ($db_tz == null ? "NULL" : $db_tz);
|
||||
$q.= ",now(),'${db_postal}','${db_country}',ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}), 4326),'".$data["city_id"]."')";
|
||||
$q.= " RETURNING id, address, latitude as lat, longitude as lng, postal, country";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
|
||||
$data["id"] = $f["id"];
|
||||
$data["address"] = $f["address"];
|
||||
$data["lat"] = $f["lat"];
|
||||
$data["lng"] = $f["lng"];
|
||||
$data["geometry"] = $f["geometry"];
|
||||
$data["postal"] = $f["postal"];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function checkValidLocations($db, $origin, $destination) {
|
||||
// Check for Google places result
|
||||
if (is_array($origin) && array_key_exists('place',$origin) && is_array($origin['place'])) {
|
||||
$origin = Options::processGooglePlace($db, $origin);
|
||||
}
|
||||
// Basic input sanity check: origin
|
||||
if (!is_array($origin) || !array_key_exists('address',$origin)
|
||||
|| !array_key_exists('lat',$origin) || !array_key_exists('lng',$origin)
|
||||
|| !array_key_exists('id',$origin) || $origin['id']<1) {
|
||||
throw new Exception('Invalid input origin');
|
||||
}
|
||||
// Check for Google places result
|
||||
if (is_array($destination) && array_key_exists('place',$destination) && is_array($destination['place'])) {
|
||||
$destination = Options::processGooglePlace($db, $destination);
|
||||
}
|
||||
// Basic input sanity check: destination
|
||||
if (!is_array($destination) || !array_key_exists('address',$destination)
|
||||
|| !array_key_exists('lat',$destination) || !array_key_exists('lng',$destination)
|
||||
|| !array_key_exists('id',$destination) || $destination['id']<1) {
|
||||
throw new Exception('Invalid input destination');
|
||||
}
|
||||
// Basic input sanity check: GPS coordinates
|
||||
if ($origin['lat']==null || $origin['lng']==null || $destination['lat']==null || $destination['lng']==null
|
||||
|| ($origin['lat']==0 && $origin['lng']==0) || ($destination['lat']==0 && $destination['lng']==0)) {
|
||||
throw new Exception('Invalid origin and/or destination coordinates');
|
||||
}
|
||||
return [$origin, $destination];
|
||||
}
|
||||
|
||||
public static function checkValidOption($db, $option) {
|
||||
// Basic input sanity check: option
|
||||
if (!is_array($option) || !array_key_exists('name',$option)
|
||||
|| !array_key_exists('transport_provider_id',$option) || $option['transport_provider_id']<1
|
||||
|| !array_key_exists('id',$option) || $option['id']<1) {
|
||||
throw new Exception('Invalid transport option');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkValidTrip($db, $trip) {
|
||||
// TODO!
|
||||
}
|
||||
|
||||
public static function createTrip($db, $country, $duration, $distance, $location_start_id, $location_end_id) {
|
||||
error_log("Options::createTrip(\$db, $country, $duration, $distance, $location_start_id, $location_end_id)");
|
||||
$data = [
|
||||
'travel_date' => Options::getDate($country),
|
||||
'duration' => ((int)($duration/60)),
|
||||
'cost_raw' => NULL,
|
||||
'trackedemail_item_id' => NULL,
|
||||
'cost' => NULL,
|
||||
'distance' => sprintf("%0.02f",$distance/1000.0),
|
||||
'transport_provider_id' => NULL,
|
||||
'scheduled' => NULL,
|
||||
'travel_date_end' => Options::getDate($country),
|
||||
'dup_id' => NULL,
|
||||
'location_start_id' => $location_start_id,
|
||||
'location_end_id' => $location_end_id,
|
||||
'private' => 't', /* this is a private entry without the source e-mail */
|
||||
'booking' => 't' /* this is a booking entry */
|
||||
];
|
||||
list($id, $err) = Options::saveTrip($db->getConnect(), $data);
|
||||
if ($id && $id>0) {
|
||||
$trip = Options::getTripById($db->getConnect(), $id);
|
||||
return $trip;
|
||||
}
|
||||
return NULL; // WTF?
|
||||
}
|
||||
|
||||
public static function getTripById($db, $id) {
|
||||
$result = array();
|
||||
$q = "SELECT a.*,
|
||||
b.address AS location_start, b.latitude AS location_start_lat, b.longitude AS location_start_lng,
|
||||
c.address AS location_end, c.latitude AS location_end_lat, c.longitude AS location_end_lng,
|
||||
a.id AS parsedemail_item_id
|
||||
FROM parsedemail_item a
|
||||
LEFT JOIN address b ON (b.id=a.location_start_id)
|
||||
LEFT JOIN address c ON (c.id=a.location_end_id)
|
||||
WHERE a.id=${id}";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$f["location_start"] = html_entity_decode ($f["location_start"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$f["location_end"] = html_entity_decode ($f["location_end"],ENT_QUOTES|ENT_HTML5,"UTF-8");
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function saveTrip($db, $data) {
|
||||
$fields = array(
|
||||
'travel_date' => false,
|
||||
'duration' => false,
|
||||
'cost_raw' => false,
|
||||
'trackedemail_item_id' => false,
|
||||
'cost' => false,
|
||||
'updated' => false,
|
||||
'distance' => false,
|
||||
'transport_provider_id' => false,
|
||||
'scheduled' => false,
|
||||
'travel_date_end' => false,
|
||||
'dup_id' => false,
|
||||
'location_start_id' => true,
|
||||
'location_end_id' => true,
|
||||
'private' => false,
|
||||
'booking' => false
|
||||
);
|
||||
$field_key_list = [];
|
||||
$field_val_list = [];
|
||||
foreach ($fields as $key=>$required) {
|
||||
if ($required && !isset($data[$key])) {
|
||||
return [NULL, "Missing required field '${key}'"];
|
||||
}
|
||||
if (!isset($data[$key])) continue;
|
||||
$field_key_list[] = "${key}";
|
||||
$field_val_list[] = (($data[$key]==NULL || $data[$key]=="")?"NULL":"'".pg_escape_string($data[$key])."'");
|
||||
}
|
||||
$q = "INSERT INTO parsedemail_item (".implode(",",$field_key_list).")
|
||||
VALUES(".implode(",",$field_val_list).") RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
$f = pg_fetch_row($r);
|
||||
return [$f[0], NULL];
|
||||
}
|
||||
return [NULL, pg_last_error($db)];
|
||||
}
|
||||
|
||||
public static function validateTrip($db, $trip) {
|
||||
if (!is_array($trip) || !array_key_exists("id",$trip) || $trip["id"]<1) {
|
||||
return false;
|
||||
}
|
||||
if (!array_key_exists("options",$trip) || !is_array($trip["options"]) || !array_key_exists("legs",$trip["options"])
|
||||
|| !is_array($trip["options"]["legs"]) || count($trip["options"]["legs"])<1
|
||||
|| !array_key_exists("id",$trip["options"]["legs"][0]) || $trip["options"]["legs"][0]["id"]<1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function saveTripAdvice($db, $trip) {
|
||||
error_log('Options::saveTripAdvice($db, $trip)');
|
||||
if (!is_array($trip) || !array_key_exists("id",$trip) || $trip["id"]<1) {
|
||||
return NULL;
|
||||
}
|
||||
$q = "INSERT INTO parsedemail_item_advice_google (parsedemail_item_id,routes,source) VALUES (".$trip["id"].",1,'".$trip["source"]."') RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
|
||||
return $f[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public static function saveTripAdviceLeg($db, $trip, $leg) {
|
||||
error_log('Options::saveTripAdviceLeg($db, $trip, $leg)');
|
||||
$fare_raw = "NULL";
|
||||
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
|
||||
$fare_raw = (int)$leg["fare_raw"];
|
||||
}
|
||||
$q = "INSERT INTO google_directions_legs (parsedemail_item_advice_google_id,arrival_time,arrival_time_zone,";
|
||||
$q.= "departure_time,departure_time_zone,distance,duration,steps,fare_raw,polyline) VALUES(";
|
||||
$q.= $leg["parsedemail_item_advice_google_id"].",'".pg_escape_string($leg["arrival_time"])."',".((int)$leg["arrival_time_zone"]);
|
||||
$q.= ",'".pg_escape_string($leg["departure_time"])."',".((int)$leg["departure_time_zone"]).",";
|
||||
$q.= ((int)$leg["distance"]).",".((int)$leg["duration"]).",1,${fare_raw},'".pg_escape_string($leg["polyline"])."') RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
error_log($q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
|
||||
return $f[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public static function saveTripAdviceLegStep($db, $trip, $leg, $step) {
|
||||
error_log('Options::saveTripAdviceLegStep($db, $trip, $leg, $step)');
|
||||
$q = "INSERT INTO google_directions_leg_steps (google_directions_leg_id,distance,duration,travel_mode,";
|
||||
$q.= "location_start_lat,location_start_lng,location_end_lat,location_end_lng,html_instructions,polyline) VALUES (";
|
||||
$q.= $leg["id"].",".((int)$step["distance"]).",".((int)$step["duration"]).",";
|
||||
$q.= "'".pg_escape_string($step["travel_mode"])."',".$step["location_start_lat"].",".$step["location_start_lng"].",";
|
||||
$q.= $step["location_end_lat"].",".$step["location_end_lng"].",'".pg_escape_string($step["html_instructions"])."',";
|
||||
$q.= "'".pg_escape_string($step["polyline"])."') RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
error_log($q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
|
||||
return $f[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public static function saveTripAdviceLegStepQuote($db, $trip, $leg, $step) {
|
||||
$fare_raw = NULL;
|
||||
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
|
||||
$fare_raw = (int)$step["fare_raw"];
|
||||
}
|
||||
if ($fare_raw>0) {
|
||||
$q = "INSERT INTO leg_step_quote (google_directions_leg_step_id,name,service,board,alight,";
|
||||
$q.= "distance,fare,fare_raw,distance_raw) VALUES (".$step["id"].",";
|
||||
$q.= "'".pg_escape_string($trip["source"])."','".pg_escape_string($trip["source"])."',";
|
||||
$q.= "'".pg_escape_string($trip["location_start"])."','".pg_escape_string($trip["location_end"])."',";
|
||||
$q.= "'".sprintf("%0.02f",$step["distance"]/1000.0)."','".sprintf("%0.02f",$fare_raw/100.0)."',";
|
||||
$q.= ((int)$fare_raw).",".((int)$step["distance"]).") RETURNING id";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
|
||||
return $f[0];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public static function getDate($country) {
|
||||
$tz = 'Asia/Singapore';
|
||||
if ($country=='US') {
|
||||
$tz = 'America/Los_Angeles';
|
||||
}
|
||||
$timestamp = time();
|
||||
$dt = new DateTime("now", new DateTimeZone($tz)); //first argument "must" be a string
|
||||
$dt->setTimestamp($timestamp); //adjust the object to correct timestamp
|
||||
return $dt->format('Y-m-d, H:i:s');
|
||||
}
|
||||
|
||||
public static function getTimezoneById($db, $id, $def='Asia/Singapore') {
|
||||
$q = "SELECT timezone FROM address_timezone WHERE id=".((int)$id);
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
return $f['timezone'];
|
||||
}
|
||||
return $def; //$id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
class OptionsApi extends Api
|
||||
{
|
||||
public $apiName = 'options';
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
$message = "Unexpected options error";
|
||||
|
||||
$latitude = $this->requestParams["latitude"] ?? 0;
|
||||
$longitude = $this->requestParams["longitude"] ?? 0;
|
||||
|
||||
try {
|
||||
if ($latitude==null || $longitude==null || ($latitude==0 || $longitude==0)) {
|
||||
throw new Exception('Invalid location: '.$latitude.",".$longitude);
|
||||
}
|
||||
$db = new Db();
|
||||
|
||||
list ($res, $details) = Options::getServicesByLatitudeLongitude($db->getConnect(), $latitude, $longitude);
|
||||
if (is_array($res)) {
|
||||
return $this->response([
|
||||
'services' => $res,
|
||||
'details' => $details
|
||||
], 200);
|
||||
} else {
|
||||
throw new Exception("Failed to get transport options for origin");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/remove/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
//id must be the first parameter after /remove/x
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
$message = "Unexpected options error";
|
||||
|
||||
$origin = $this->requestParams["origin"] ?? [];
|
||||
$destination = $this->requestParams["destination"] ?? [];
|
||||
$create = $this->requestParams["create"] ?? false;
|
||||
$country = $this->requestParams["country"] ?? NULL;
|
||||
$duration = $this->requestParams["duration"] ?? 0;
|
||||
$distance = $this->requestParams["distance"] ?? 0;
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
|
||||
try {
|
||||
$db = new Db();
|
||||
|
||||
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
|
||||
|
||||
list ($res, $details) = Options::getTransportOptions($db->getConnect(), $origin, $destination);
|
||||
if (is_array($res)) {
|
||||
$trip = NULL;
|
||||
if ($create) {
|
||||
$country = $origin["country"];
|
||||
$trip = Options::createTrip($db, $country, $duration, $distance, $origin["id"], $destination["id"]);
|
||||
}
|
||||
return $this->response([
|
||||
'services' => $res,
|
||||
'details' => $details,
|
||||
'trip' => $trip
|
||||
], 200);
|
||||
} else {
|
||||
throw new Exception("Failed to get transport options for origin");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
<?php
|
||||
|
||||
class Quote {
|
||||
|
||||
const QUOTE_CHECK_RETRIES = 10;
|
||||
const QUOTE_CHECK_TIMEOUT = 3;
|
||||
|
||||
public static function getTransportQuote($db, $origin, $destination, $option, $trip, $route) {
|
||||
$quote = [];
|
||||
switch ($option["transport_provider_id"]) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 8:
|
||||
list($option, $trip, $quote) = Quote::rideshareQuote($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 11: // BART
|
||||
list($option, $trip, $quote) = Quote::bartQuote($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 12: // Muni
|
||||
list($option, $trip, $quote) = Quote::muniQuote($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 13: // LTA
|
||||
list($option, $trip, $quote) = Quote::ltaQuote($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 14: // MARTA
|
||||
list($option, $trip, $quote) = Quote::martaQuote($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
}
|
||||
return [$option, $trip, $quote];
|
||||
}
|
||||
|
||||
public static function rideshareQuote($db, $origin, $destination, $option, $trip) {
|
||||
$res = [];
|
||||
|
||||
$input = [
|
||||
"origin" => $origin,
|
||||
"destination" => $destination,
|
||||
"member_id" => $option["member_id"],
|
||||
"country" => $origin["country"],
|
||||
"transport_provider_id" => $option["transport_provider_id"],
|
||||
"group_quote_id" => 0,
|
||||
"trackedemail_item_id" => 0,
|
||||
"prefill" => 'f'
|
||||
];
|
||||
|
||||
list($res,$body) = Quote::postServiceCall("/trips/api/quote", json_encode($input));
|
||||
$log = [
|
||||
'message' => 'BookingQuote::rideshareQuote',
|
||||
'function' =>'Quote::postServiceCall()',
|
||||
'data' =>$input,
|
||||
'response' => $res
|
||||
];
|
||||
Logger::debug($log);
|
||||
if (!is_array($res) || !array_key_exists("id",$res) || $res["id"]<1) {
|
||||
if (!is_array($res)) {
|
||||
error_log($res);
|
||||
}
|
||||
error_log($body);
|
||||
throw new Exception("Failed to schedule quote");
|
||||
}
|
||||
if (array_key_exists("complete",$res) && $res["complete"]!=NULL && $res["complete"]!="") {
|
||||
if (array_key_exists("cost",$res) && $res["cost"]>0) {
|
||||
$trip = Quote::patchRideshareTrip($trip, $res["cost"]);
|
||||
return [$option, $trip, $res];
|
||||
} else {
|
||||
throw new Exception("Failed to get the quote");
|
||||
// $res["message"] ?
|
||||
// return [$option, $trip, $res]; // Do we want to recover?
|
||||
}
|
||||
}
|
||||
// Will try for 30 seconds?
|
||||
$endpoint = "/trips/api/quote/".$res["id"];
|
||||
$i = 0;
|
||||
while ($i<Quote::QUOTE_CHECK_RETRIES) {
|
||||
sleep(Quote::QUOTE_CHECK_TIMEOUT);
|
||||
list($res,$body) = Quote::getServiceCall($endpoint);
|
||||
$log = [
|
||||
'message' => 'BookingQuote::rideshareQuote',
|
||||
'function' =>'Quote::getServiceCall()',
|
||||
'data' =>$endpoint,
|
||||
'response' => $res
|
||||
];
|
||||
Logger::debug($log);
|
||||
if (!is_array($res) || !array_key_exists("id",$res) || $res["id"]<1) {
|
||||
continue; // Try again...
|
||||
}
|
||||
if (array_key_exists("complete",$res) && $res["complete"]!=NULL && $res["complete"]!="") {
|
||||
break; // No mater the result, maybe client will want to recover
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if (!is_array($res) || !array_key_exists("cost",$res) || !isset($res["cost"]) || $res["cost"] == 0) {
|
||||
$avgPriceInfo = Quote::averagePrice($db, $origin, $destination, $option["transport_provider_id"]);
|
||||
if (isset($avgPriceInfo) && array_key_exists('cost', $avgPriceInfo)) {
|
||||
$res["cost"] = $avgPriceInfo['cost'];
|
||||
$res["is_average_cost"] = true;
|
||||
};
|
||||
}
|
||||
$trip = Quote::patchRideshareTrip($trip, $res["cost"]);
|
||||
Quote::updateRideshareCosts($db, $trip);
|
||||
return [$option, $trip, $res]; // last check quote result
|
||||
}
|
||||
|
||||
public static function patchRideshareTrip($trip, $cost) {
|
||||
// Do we save the quote?
|
||||
$fare_raw = ((int)(100.0*$cost));
|
||||
if (array_key_exists("options",$trip)) {
|
||||
$gid = $trip["options"]["gid"];
|
||||
$trip["options"]["legs"][0]["fare_raw"] = $fare_raw;
|
||||
$trip["options"]["leg_fare"][$gid] = $fare_raw;
|
||||
$trip["options"]["leg_steps"][$gid][0]["fare_raw"] = $fare_raw;
|
||||
} else {
|
||||
$trip["options"] = [
|
||||
"gid" => "1",
|
||||
"routes" => 1,
|
||||
"legs" => [
|
||||
[
|
||||
"id" => 1,
|
||||
"fare_raw" => $fare_raw
|
||||
]
|
||||
],
|
||||
"leg_steps" => [
|
||||
1 => [
|
||||
"fare_raw" => $fare_raw
|
||||
]
|
||||
],
|
||||
"leg_fare" => [
|
||||
1 => $fare_raw
|
||||
]
|
||||
];
|
||||
}
|
||||
return $trip;
|
||||
}
|
||||
|
||||
public static function updateRideshareCosts($db, $trip) {
|
||||
if (array_key_exists("options",$trip)) {
|
||||
$gid = $trip["options"]["legs"][0]["id"];
|
||||
if ($gid>1 && $trip["options"]["legs"][0]["fare_raw"]>0) {
|
||||
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$trip["options"]["legs"][0]["fare_raw"]);
|
||||
$q.= " WHERE id=".((int)$gid);
|
||||
$r = pg_query($db, $q);
|
||||
Options::saveTripAdviceLegStepQuote(
|
||||
$db, $trip, $trip["options"]["legs"][0], $trip["options"]["leg_steps"][$gid][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"id": "224583",
|
||||
"transport_provider_id": "4",
|
||||
"automation_id": "206441",
|
||||
"cost_raw": null,
|
||||
"cost": null,
|
||||
"created": "2020-01-09 13:09:01.072305",
|
||||
"completed": null,
|
||||
"member_id": "13",
|
||||
"location_start_id": "4426",
|
||||
"location_end_id": "4386",
|
||||
"quote_group_id": "0",
|
||||
"prefill": "f",
|
||||
"location_start": "97 Meyer Rd, 93, Singapore 437918",
|
||||
"location_start_lat": "1.2970849",
|
||||
"location_start_lng": "103.8925712",
|
||||
"location_start_tz": "1",
|
||||
"location_geocoding_date": "2019-10-11",
|
||||
"location_end": "1 Fusionopolis Way, #01-07 & #02-14, Connexis, Singapore 138632",
|
||||
"location_end_lat": "1.2987049",
|
||||
"location_end_lng": "103.7875699",
|
||||
"deeplink": "ComfortDelGroTaxi:\/\/\/?action=setBooking&endingLat=1.298705&endingLong=103.787570&startingLat=1.297085&startingLong=103.892571"
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
"id": 224583,
|
||||
"location_start": "97 Meyer Rd, 93, Singapore 437918",
|
||||
"location_end": "1 Fusionopolis Way, #01-07 & #02-14, Connexis, Singapore 138632",
|
||||
"cost_raw": "14.6",
|
||||
"trackedemail_item_id": 0,
|
||||
"cost": "14.60",
|
||||
"transport_provider_id": 4,
|
||||
"location_start_lat": "1.2970576",
|
||||
"location_start_lng": "103.8925856",
|
||||
"location_end_lat": "1.2987049",
|
||||
"location_end_lng": "103.7875699",
|
||||
"request_date": "2020-01-09T13:09:00.955Z",
|
||||
"started": "2020-01-09T13:09:04.203Z",
|
||||
"complete": "2020-01-09T13:09:04.743Z",
|
||||
"status": 0,
|
||||
"message": "android_automation_job_detail",
|
||||
"attempts": 0,
|
||||
"automation_id": 206441,
|
||||
"completed": "2020-01-09T13:09:04.743Z",
|
||||
"created": "2020-01-09 13:09:01.072305",
|
||||
"location_start_id": "4426",
|
||||
"location_end_id": "4386",
|
||||
"member_id": "13",
|
||||
"deeplink": "ComfortDelGroTaxi:\/\/\/?action=setBooking&endingLat=1.298705&endingLong=103.787570&startingLat=1.297085&startingLong=103.892571"
|
||||
}
|
||||
*/
|
||||
|
||||
public static function bartQuote($db, $origin, $destination, $option, $trip) {
|
||||
$quote = [];
|
||||
|
||||
$quote["cost_raw"] = NULL;
|
||||
$quote["transport_provider_id"] = $option["transport_provider_id"];
|
||||
$quote["deeplink"] = NULL;
|
||||
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
|
||||
|
||||
$leg = [
|
||||
"fare_raw" => 1
|
||||
];
|
||||
// Select leg
|
||||
//error_log(">>>>>>>>> ".json_encode($trip));
|
||||
foreach ($trip["options"]["legs"] as $leg) {
|
||||
break;
|
||||
}
|
||||
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
|
||||
|
||||
return [$option, $trip, $quote];
|
||||
}
|
||||
|
||||
public static function muniQuote($db, $origin, $destination, $option, $trip) {
|
||||
$quote = [];
|
||||
|
||||
$quote["cost_raw"] = NULL;
|
||||
$quote["transport_provider_id"] = $option["transport_provider_id"];
|
||||
$quote["deeplink"] = NULL;
|
||||
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
|
||||
|
||||
$leg = [
|
||||
"fare_raw" => 1
|
||||
];
|
||||
// Select leg
|
||||
//error_log(">>>>>>>>> ".json_encode($trip));
|
||||
foreach ($trip["options"]["legs"] as $leg) {
|
||||
break;
|
||||
}
|
||||
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
|
||||
|
||||
return [$option, $trip, $quote];
|
||||
}
|
||||
|
||||
public static function ltaQuote($db, $origin, $destination, $option, $trip) {
|
||||
$quote = [];
|
||||
|
||||
$cost = 0;
|
||||
|
||||
$quote["cost_raw"] = NULL;
|
||||
$quote["transport_provider_id"] = $option["transport_provider_id"];
|
||||
$quote["deeplink"] = NULL;
|
||||
|
||||
$legs = $trip["options"]["legs"];
|
||||
$leg_steps = $trip["options"]["leg_steps"];
|
||||
$leg_fare = $trip["options"]["leg_fare"];
|
||||
|
||||
foreach ($legs as $i=>$leg) {
|
||||
$leg_id = $leg["id"];
|
||||
if (array_key_exists($leg_id,$leg_fare) && $leg_fare[$leg_id]>0) {
|
||||
if ($cost==0) $cost = $leg["fare_raw"];
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
|
||||
if ($cost==0) $cost = $leg["fare_raw"];
|
||||
continue;
|
||||
}
|
||||
list ($leg, $leg_steps) = Quote::legStepsQuote($db, $origin, $destination, $option, $trip, $leg, $leg_steps);
|
||||
$leg_fare[$leg_id] = $leg["fare_raw"];
|
||||
if ($cost==0 && $leg["fare_raw"]>0) $cost = $leg["fare_raw"];
|
||||
$legs[$i] = $leg;
|
||||
}
|
||||
$trip["options"]["legs"] = $legs;
|
||||
$trip["options"]["leg_steps"] = $leg_steps;
|
||||
$trip["options"]["leg_fare"] = $leg_fare;
|
||||
|
||||
$quote["cost"] = $cost;
|
||||
//$quote["leg_fare"] = $leg_fare;
|
||||
|
||||
// Update model to save the quote
|
||||
Quote::updateQuotes($db, $trip);
|
||||
|
||||
return [$option, $trip, $quote];
|
||||
}
|
||||
|
||||
public static function martaQuote($db, $origin, $destination, $option, $trip) {
|
||||
$quote = [];
|
||||
|
||||
$quote["cost_raw"] = NULL;
|
||||
$quote["transport_provider_id"] = $option["transport_provider_id"];
|
||||
$quote["deeplink"] = NULL;
|
||||
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
|
||||
|
||||
$leg = [
|
||||
"fare_raw" => 1
|
||||
];
|
||||
// Select leg
|
||||
//error_log(">>>>>>>>> ".json_encode($trip));
|
||||
foreach ($trip["options"]["legs"] as $leg) {
|
||||
break;
|
||||
}
|
||||
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
|
||||
|
||||
return [$option, $trip, $quote];
|
||||
}
|
||||
|
||||
public static function updateQuotes($db, $trip) {
|
||||
$legs = $trip["options"]["legs"];
|
||||
$leg_steps = $trip["options"]["leg_steps"];
|
||||
$leg_fare = $trip["options"]["leg_fare"];
|
||||
foreach ($legs as $leg) {
|
||||
$leg_id = $leg["id"];
|
||||
$steps = $leg_steps[$leg_id];
|
||||
foreach ($steps as $step) {
|
||||
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
|
||||
$q = "SELECT id FROM leg_step_quote WHERE google_directions_leg_step_id=".$step["id"];
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
|
||||
$q = "UPDATE leg_step_quote SET fare_raw=".((int)$step["fare_raw"])." WHERE id=".$f[0];
|
||||
$r = pg_query($db, $q);
|
||||
} else {
|
||||
$q = "INSERT INTO leg_step_quote (google_directions_leg_step_id,name,service,board,alight,distance,fare,fare_raw,distance_raw) VALUES(";
|
||||
$q.= $step["id"].",'".pg_escape_string($step["short_line"])."','".pg_escape_string($step["line"])."',";
|
||||
$q.= "'".pg_escape_string($step["departure_stop"])."','".pg_escape_string($step["arrival_stop"])."',";
|
||||
$q.= "'".sprintf("%0.02f",$step["distance"]/100.0)."','".sprintf("%0.02f",$step["fare_raw"]/100.0)."',";
|
||||
$q.= ((int)$step["fare_raw"]).",".((int)$step["distance"]).")";
|
||||
$r = pg_query($db, $q);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
|
||||
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$leg["fare_raw"])." WHERE id=${leg_id}";
|
||||
$r = pg_query($db, $q);
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($leg_id,$leg_fare) && $leg_fare[$leg_id]>0) {
|
||||
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$leg["fare_raw"])." WHERE id=${leg_id}";
|
||||
$r = pg_query($db, $q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function legStepsQuote($db, $origin, $destination, $option, $trip, $leg, $leg_steps) {
|
||||
$leg_id = $leg["id"];
|
||||
$steps = $leg_steps[$leg_id];
|
||||
|
||||
$fare_raw = 0;
|
||||
foreach ($steps as $i=>$step) {
|
||||
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
|
||||
$fare_raw += $step["fare_raw"];
|
||||
continue;
|
||||
}
|
||||
$step = Quote::stepQuote($db, $origin, $destination, $option, $trip, $step);
|
||||
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
|
||||
$fare_raw += $step["fare_raw"];
|
||||
}
|
||||
$steps[$i] = $step;
|
||||
}
|
||||
$leg["fare_raw"] = $fare_raw;
|
||||
$leg_steps[$leg_id] = $steps;
|
||||
return [$leg, $leg_steps];
|
||||
}
|
||||
|
||||
public static function stepQuote($db, $origin, $destination, $option, $trip, $step) {
|
||||
switch ($step["travel_mode"]) {
|
||||
case "": break;
|
||||
case "BIKE":
|
||||
$bike_time = $step['duration'];
|
||||
$country = $origin['country'];
|
||||
list($price,$err) = SGBikeApi::quote($db, $bike_time, $country,$step);
|
||||
if (!empty($price) && $price>0) {
|
||||
$step["fare_raw"] = $price;
|
||||
}
|
||||
break;
|
||||
case "WALKING": break;
|
||||
case "NUS_TRANSIT":
|
||||
// NUS Internal Shuttle Buses are free of charge
|
||||
// http://www.nus.edu.sg/celc/symposium/Symposium%20Documents/Symposium%202016%20Visitors'%20Guide.pdf
|
||||
break;
|
||||
case "PARK": break;
|
||||
case "SCOOTER":
|
||||
// No scooter quotes right now
|
||||
// list($res,$err) = ScooterApi::quote($db, $scooter_time, $country);
|
||||
break;
|
||||
case "TAXI":
|
||||
// We do not know how to quote "TAXI" in another countries
|
||||
if ($origin["country"]=="SG") {
|
||||
// Reverse geocode the "TAXI" step
|
||||
$input = [
|
||||
"latitude" => $step["location_start_lat"],
|
||||
"longitude" => $step["location_start_lng"],
|
||||
"country" => $origin["country"]
|
||||
];
|
||||
list($originTaxi, $body) = Quote::putServiceCall("/trips/api/geocode",$input);
|
||||
$input = [
|
||||
"latitude" => $step["location_end_lat"],
|
||||
"longitude" => $step["location_end_lng"],
|
||||
"country" => $destination["country"]
|
||||
];
|
||||
list($destinationTaxi, $body) = Quote::putServiceCall("/trips/api/geocode",$input);
|
||||
if (!is_array($originTaxi) || !array_key_exists("address",$originTaxi)
|
||||
|| !is_array($destinationTaxi) || !array_key_exists("address",$destinationTaxi)) {
|
||||
break;
|
||||
}
|
||||
$option["transport_provider_id"] = 4; // ComfortDelGro
|
||||
list($optionTaxi, $tripTaxi, $quoteTaxi) =
|
||||
Quote::rideshareQuote($db, $originTaxi, $destinationTaxi, $option, $trip);
|
||||
if (is_array($quoteTaxi) && array_key_exists("cost",$quoteTaxi) && $quoteTaxi["cost"]>0) {
|
||||
$step["fare_raw"] = (int)(100.0*$quoteTaxi["cost"]); // fare_raw is in cents
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TRANSIT":
|
||||
// We do not know how to quote "TRANSIT" in another countries from here (must be specific)
|
||||
if ($origin["country"]=="SG") {
|
||||
$step_id = $step["id"];
|
||||
$result = Quote::mytansportsgServiceStepQuote($step_id);
|
||||
error_log('***********************************************************************');
|
||||
error_log(json_encode($result));
|
||||
error_log('***********************************************************************');
|
||||
// {"code":0,"data":{"307575":{"total":92}}}
|
||||
if (is_array($result) && array_key_exists("code",$result) && $result["code"]===0 && array_key_exists("data",$result)) {
|
||||
$data = $result["data"];
|
||||
if (is_array($data) && array_key_exists($step_id,$data) && is_array($data[$step_id]) && array_key_exists('total',$data[$step_id]) && $data[$step_id]['total']>0) {
|
||||
// Conversion ???
|
||||
$step["fare_raw"] = (int)$data[$step_id]['total']; // fare_raw is in cents
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $step;
|
||||
}
|
||||
/*
|
||||
BIKE | 4743
|
||||
NUS_TRANSIT | 367
|
||||
PARK | 1796
|
||||
SCOOTER | 1023
|
||||
TAXI | 2060
|
||||
TRANSIT | 88781
|
||||
WALKING | 154766
|
||||
| 3
|
||||
*/
|
||||
|
||||
public static function postServiceCall($endpoint, $payload) {
|
||||
global $savvyext;
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$api_url = $savvyext->cfgReadChar('system.api_url');
|
||||
|
||||
$encrypted_payload = bin2hex(
|
||||
openssl_encrypt(
|
||||
$payload, $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
|
||||
));
|
||||
|
||||
$postdata = "{\"encrypted_payload\": \"${encrypted_payload}\"}";
|
||||
|
||||
$url = $api_url . $endpoint;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Server-Token ' . $httpAuthToken)
|
||||
);
|
||||
|
||||
$body = curl_exec($ch);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
$payload = openssl_decrypt(
|
||||
hex2bin(
|
||||
$result['payload']
|
||||
), $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
|
||||
);
|
||||
$payload = json_decode($payload, true);
|
||||
return [$payload, $body];
|
||||
}
|
||||
|
||||
public static function putServiceCall($endpoint, $payload) {
|
||||
global $savvyext;
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$api_url = $savvyext->cfgReadChar('system.api_url');
|
||||
|
||||
/* $encrypted_payload = bin2hex(
|
||||
openssl_encrypt(
|
||||
$payload, $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
|
||||
));
|
||||
$postdata = "{\"encrypted_payload\": \"${encrypted_payload}\"}"; */
|
||||
$postdata = http_build_query($payload);
|
||||
|
||||
$url = $api_url . $endpoint;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'Authorization: Server-Token ' . $httpAuthToken)
|
||||
);
|
||||
|
||||
$body = curl_exec($ch);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
$payload = openssl_decrypt(
|
||||
hex2bin(
|
||||
$result['payload']
|
||||
), $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
|
||||
);
|
||||
$payload = json_decode($payload, true);
|
||||
return [$payload, $body];
|
||||
}
|
||||
|
||||
public static function getServiceCall($endpoint, $shouldDecrypt = true) {
|
||||
global $savvyext;
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$api_url = $savvyext->cfgReadChar('system.api_url');
|
||||
|
||||
$url = $api_url . $endpoint;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Server-Token ' . $httpAuthToken)
|
||||
);
|
||||
|
||||
$body = curl_exec($ch);
|
||||
if ($shouldDecrypt) {
|
||||
$result = json_decode($body,true);
|
||||
$payload = openssl_decrypt(
|
||||
hex2bin(
|
||||
$result['payload']
|
||||
),
|
||||
$encryptionAlg,
|
||||
$encryptionKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$encryptionIV
|
||||
);
|
||||
} else {
|
||||
$payload = $body;
|
||||
}
|
||||
$payload = json_decode($payload, true);
|
||||
return [$payload, $body];
|
||||
}
|
||||
|
||||
public static function mytansportsgServiceStepQuote($id) {
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
$url = $oauth2_url."mytransportsg/?step_id=" . $id;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$advice = json_decode($body,true);
|
||||
return $advice;
|
||||
}
|
||||
|
||||
public static function averagePrice($db, $origin, $destination, $providerId) {
|
||||
|
||||
if (!array_key_exists('id', $origin) || !isset($origin['id'])
|
||||
|| !array_key_exists('country', $origin) || !isset($origin['country'])
|
||||
|| !array_key_exists('timeZoneId', $origin) || !isset($origin['timeZoneId'])
|
||||
|| !array_key_exists('id', $destination) || !isset($destination['id']) ) {
|
||||
throw new Exception('Quote::averagePrices. Invalid input origin or destination');
|
||||
}
|
||||
|
||||
$result = ['cost'=>0];
|
||||
$timeZoneId = $origin['timeZoneId'];
|
||||
$country = $origin['country'];
|
||||
$originId = $origin['id'];
|
||||
$destinationId = $destination['id'];
|
||||
|
||||
$endpoint = "/geofencearea/api/addresstoarea/?country=${country}&address_id=";
|
||||
list($res,$body) = Quote::getServiceCall($endpoint.$originId, false);
|
||||
if(count($res) == 0 || array_key_exists('error', $res) && isset($res['error'])) {
|
||||
return $result;
|
||||
}
|
||||
$originAreaId = $res['areas'][0]['id'];
|
||||
list($res,$body) = Quote::getServiceCall($endpoint.$destinationId, false);
|
||||
if(count($res) == 0 || array_key_exists('error', $res) && isset($res['error'])) {
|
||||
return $result;
|
||||
}
|
||||
$destinationAreaId = $res['areas'][0]['id'];
|
||||
|
||||
$dt = new DateTime('now', new DateTimeZone($timeZoneId));
|
||||
$currentHour = $dt->format('G');
|
||||
|
||||
$q = "SELECT average_cost, hour, last_quotes_id, last_updated FROM geofence_area_average_quotes
|
||||
WHERE transport_provider_id=${providerId}
|
||||
AND area_start_id=${originAreaId}
|
||||
AND area_end_id=${destinationAreaId}
|
||||
AND hour=${currentHour}
|
||||
";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$result['cost'] = $f['average_cost'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
class QuoteApi extends Api
|
||||
{
|
||||
public $apiName = 'quote';
|
||||
|
||||
protected $hasPrice = true;
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
$this->cacheWhitelist = [
|
||||
"createAction" => ['ttl' => 300] // 300 sec. = 5 min.
|
||||
];
|
||||
parent::__construct($requestUri, $encryption);
|
||||
}
|
||||
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/remove/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
//id must be the first parameter after /remove/x
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
$message = "Unexpected options error";
|
||||
|
||||
$origin = $this->requestParams["origin"] ?? [];
|
||||
$destination = $this->requestParams["destination"] ?? [];
|
||||
$option = $this->requestParams["option"] ?? [];
|
||||
$trip = $this->requestParams["trip"] ?? [];
|
||||
$route = $this->requestParams["route"] ?? [];
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
|
||||
try {
|
||||
$db = new Db();
|
||||
|
||||
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
|
||||
Options::checkValidOption($db->getConnect(), $option);
|
||||
Options::checkValidTrip($db->getConnect(), $trip);
|
||||
Route::checkValidRoute($db->getConnect(), $route);
|
||||
|
||||
$option["member_id"] = $member_id;
|
||||
$trip["member_id"] = $member_id;
|
||||
list ($option, $trip, $quote) = Quote::getTransportQuote($db->getConnect(), $origin, $destination, $option, $trip, $route);
|
||||
if (is_array($quote)) {
|
||||
$this->hasPrice = array_key_exists('cost', $quote) && isset($quote['cost']) && intval($quote['cost']) > 0;
|
||||
return $this->response([
|
||||
'option' => $option,
|
||||
'trip' => $trip,
|
||||
'quote' => $quote,
|
||||
'route' => $route
|
||||
], 200);
|
||||
} else {
|
||||
throw new Exception("Failed to get transport quote for origin");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
|
||||
protected function storeInCache() {
|
||||
return http_response_code() === 200 && $this->hasPrice;
|
||||
}
|
||||
|
||||
protected function getCacheKey() {
|
||||
$origin = json_encode($this->requestParams["origin"]);
|
||||
$destination = json_encode($this->requestParams["destination"]);
|
||||
$option = $this->requestParams["option"];
|
||||
$transport_provider_id = $option["transport_provider_id"];
|
||||
return hash('md5', $this->method.'|'.$this->apiName.'|'.$this->action.'|'.$origin.'|'.$destination.'|'.$transport_provider_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
<?php
|
||||
|
||||
class Route {
|
||||
|
||||
/*
|
||||
savvy=> select id,name from transport_providers;
|
||||
id | name
|
||||
----+---------------
|
||||
1 | Uber
|
||||
2 | Lyft
|
||||
3 | Grab
|
||||
4 | ComfortDelGro
|
||||
5 | GOJEK
|
||||
6 | Turo
|
||||
7 | Getaround
|
||||
10 | GrabWheels
|
||||
11 | BART
|
||||
12 | Muni
|
||||
13 | LTA
|
||||
(11 rows)
|
||||
*/
|
||||
public static function getTransportRoute($db, $origin, $destination, $option, $trip) {
|
||||
$route = [];
|
||||
switch ($option["transport_provider_id"]) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 8:
|
||||
list($option, $trip, $route) = Route::drivingDirections($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 11: // BART
|
||||
list($option, $trip, $route) = Route::bartDirections($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 12: // Muni
|
||||
list($option, $trip, $route) = Route::muniDirections($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 13: // LTA
|
||||
list($option, $trip, $route) = Route::ltaDirections($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
case 14: // MARTA
|
||||
list($option, $trip, $route) = Route::martaDirections($db, $origin, $destination, $option, $trip);
|
||||
break;
|
||||
}
|
||||
error_log(json_encode($trip));
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function drivingDirections($db, $origin, $destination, $option, $trip) {
|
||||
list($option, $trip, $route) = Route::processRideshareOption($db, $option, $origin, $destination, $trip);
|
||||
$option["travel_time"] = (int)$trip["duration"];
|
||||
$option["travel_distance"] = (int)$trip["distance"];
|
||||
$trip["source"] = strtolower($option["name"]);
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function drivingDirectionsOriginal($db, $origin, $destination, $option, $trip) {
|
||||
$options = [];
|
||||
$trip = [];
|
||||
$option["travel_time"] = 0;
|
||||
$option["travel_distance"] = 0;
|
||||
$fromLat = $origin['lat'];
|
||||
$fromLng = $origin['lng'];
|
||||
$toLat = $destination['lat'];
|
||||
$toLng = $destination['lng'];
|
||||
// This is paranoid redudant check, but since the function is "public"...
|
||||
if ($fromLat==null || $fromLng==null || $toLat==null || $toLng==null
|
||||
|| ($fromLat==0 && $fromLng==0) || ($toLat==0 && $toLng==0)) {
|
||||
throw new Exception('Invalid origin and/or destination coordinates');
|
||||
}
|
||||
list($res, $err) = Route::serviceRoute($db, $fromLat, $fromLng, $toLat, $toLng);
|
||||
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
|
||||
$options['error'] = $err!=NULL ? $err : 'No available routes';
|
||||
return [$option, $trip, $options];
|
||||
}
|
||||
$options["route_overlay"] = [];
|
||||
$travel_time = PHP_INT_MAX;
|
||||
$travel_distance = PHP_INT_MAX;
|
||||
foreach ($res["routes"] as $route) {
|
||||
$r = Route::processRoute($route);
|
||||
if ($travel_time>$r['duration']) {
|
||||
$travel_time = $r['duration'];
|
||||
$travel_distance = $r['distance'];
|
||||
}
|
||||
$options["route_overlay"][] = $r;
|
||||
}
|
||||
$options["travel_time"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
|
||||
$options["travel_distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
|
||||
|
||||
$option["travel_time"] = (int)$options["travel_time"];
|
||||
$option["travel_distance"] = (int)$options["travel_distance"];
|
||||
|
||||
//https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
//overview_polyline
|
||||
//bounds contains the viewport bounding box of the overview_polyline.
|
||||
return [$option, $trip, $options];
|
||||
}
|
||||
|
||||
public static function bartDirections($db, $origin, $destination, $option, $trip) {
|
||||
$route = [];
|
||||
|
||||
$option["travel_time"] = 0;
|
||||
$option["travel_distance"] = 0;
|
||||
|
||||
$route["travel_time"] = 0;
|
||||
$route["travel_distance"] = 0;
|
||||
$route["route_overlay"] = [];
|
||||
|
||||
$country = $origin["country"];
|
||||
$member_id = $option["member_id"];
|
||||
|
||||
// New directions
|
||||
$tripService = Route::tripService($trip["id"], $country, 'BART');
|
||||
|
||||
//error_log(json_encode($tripService));
|
||||
if (is_array($tripService) && isset($tripService["travel_date"])) {
|
||||
$tripService["member_id"] = $member_id; // Pass along
|
||||
$tripService["country"] = $country;
|
||||
$tripService = MultiModalFilter::byDuration($tripService);
|
||||
//$tripService = MultiModalFilter::byCost($tripService);
|
||||
$trip = $tripService;
|
||||
|
||||
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
|
||||
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
|
||||
|
||||
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
|
||||
}
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function muniDirections($db, $origin, $destination, $option, $trip) {
|
||||
$route = [];
|
||||
|
||||
$option["travel_time"] = 0;
|
||||
$option["travel_distance"] = 0;
|
||||
$country = $origin["country"];
|
||||
$member_id = $option["member_id"];
|
||||
|
||||
// New directions
|
||||
$tripService = Route::tripService($trip["id"], $country, 'MUNI');
|
||||
|
||||
//error_log(json_encode($tripService));
|
||||
if (is_array($tripService) && isset($tripService["travel_date"])) {
|
||||
$tripService["member_id"] = $member_id; // Pass along
|
||||
$tripService["country"] = $country;
|
||||
|
||||
$tripService = MultiModalFilter::byDuration($tripService);
|
||||
//$tripService = MultiModalFilter::byCost($tripService);
|
||||
$trip = $tripService;
|
||||
|
||||
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
|
||||
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
|
||||
|
||||
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
|
||||
}
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function ltaDirections($db, $origin, $destination, $option, $trip) {
|
||||
$route = [];
|
||||
|
||||
$option["travel_time"] = 0;
|
||||
$option["travel_distance"] = 0;
|
||||
$country = $origin["country"];
|
||||
$member_id = $option["member_id"];
|
||||
|
||||
// New directions
|
||||
$tripService = Route::tripService($trip["id"], $country, 'LTA');
|
||||
|
||||
//error_log(json_encode($tripService));
|
||||
if (is_array($tripService) && isset($tripService["travel_date"])) {
|
||||
$tripService["member_id"] = $member_id; // Pass along
|
||||
$tripService["country"] = $country;
|
||||
|
||||
$tripService = MultiModalFilter::byDuration($tripService);
|
||||
//$tripService = MultiModalFilter::byCost($tripService);
|
||||
$trip = $tripService;
|
||||
|
||||
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
|
||||
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
|
||||
|
||||
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
|
||||
}
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function martaDirections($db, $origin, $destination, $option, $trip) {
|
||||
$route = [];
|
||||
|
||||
$option["travel_time"] = 0;
|
||||
$option["travel_distance"] = 0;
|
||||
$country = $origin["country"];
|
||||
$member_id = $option["member_id"];
|
||||
|
||||
// New directions
|
||||
$tripService = Route::tripService($trip["id"], $country, 'MARTA');
|
||||
|
||||
//error_log(json_encode($tripService));
|
||||
if (is_array($tripService) && isset($tripService["travel_date"])) {
|
||||
$tripService["member_id"] = $member_id; // Pass along
|
||||
$tripService["country"] = $country;
|
||||
|
||||
$tripService = MultiModalFilter::byDuration($tripService);
|
||||
$trip = $tripService;
|
||||
|
||||
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
|
||||
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
|
||||
|
||||
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
|
||||
}
|
||||
return [$option, $trip, $route];
|
||||
}
|
||||
|
||||
public static function routeFromTrip($db, $origin, $destination, $option, $trip) {
|
||||
|
||||
$leg = [
|
||||
"distance" => 0,
|
||||
"duration" => 0,
|
||||
"polyline" => NULL
|
||||
];
|
||||
// Select leg
|
||||
foreach ($trip["options"]["legs"] as $leg) {
|
||||
break;
|
||||
}
|
||||
|
||||
$route["travel_time"] = (int)$leg["duration"];
|
||||
$route["travel_distance"] = (int)$leg["distance"];
|
||||
|
||||
// TODO: BART fix polyline!
|
||||
|
||||
$route["route_overlay"][] = [
|
||||
"polyline" => [],
|
||||
"distance" => $leg["distance"],
|
||||
"duration" => $leg["duration"],
|
||||
"summary" => "Generated option",
|
||||
"overview_polyline" => [
|
||||
"points" => Route::processPolyline($leg["polyline"])
|
||||
],
|
||||
"bounds" => [
|
||||
"northeast" => [
|
||||
"lat" => $origin["lat"],
|
||||
"lng" => $origin["lng"]
|
||||
],
|
||||
"southwest" => [
|
||||
"lat" => $destination["lat"],
|
||||
"lng" => $destination["lng"]
|
||||
]
|
||||
]
|
||||
];
|
||||
return $route;
|
||||
}
|
||||
|
||||
public static function checkValidRoute($db, $route) {
|
||||
// Basic input sanity check: route
|
||||
if (!is_array($route) || !array_key_exists('travel_time',$route) || !is_numeric($route['travel_time'])
|
||||
|| !array_key_exists('travel_distance',$route) || !is_numeric($route['travel_distance'])
|
||||
|| !array_key_exists('route_overlay',$route) || !is_array($route['route_overlay'])) {
|
||||
throw new Exception('Invalid transport route');
|
||||
}
|
||||
}
|
||||
|
||||
public static function serviceRoute($db, $fromLat, $fromLng, $toLat, $toLng, $mode='driving',$waypoints=[]) {
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
error_log("Route::serviceRoute(\$db, $fromLat, $fromLng, $toLat, $toLng, $mode, \$waypoints=[])");
|
||||
// Call geocoding service
|
||||
$data = http_build_query(
|
||||
array(
|
||||
'gps' => implode(",",array($fromLat, $fromLng, $toLat, $toLng)),
|
||||
'mode' => $mode,
|
||||
'waypoints' => implode(",",$waypoints)
|
||||
)
|
||||
);
|
||||
$url = $oauth2_url."route?" . $data;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$geocoded = json_decode($body,true);
|
||||
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
|
||||
return array($geocoded["data"], NULL);
|
||||
} else if (is_array($geocoded) && isset($geocoded["error"])) {
|
||||
$body = $geocoded["error"];
|
||||
}
|
||||
return array(NULL, "Routing service call error: ".$body);
|
||||
}
|
||||
|
||||
public static function processRoute($route) {
|
||||
require_once('../common/Polyline.php');
|
||||
$result = [
|
||||
'polyline' => array(),
|
||||
'distance' => NULL,
|
||||
'duration' => NULL,
|
||||
'summary' => $route["summary"],
|
||||
'overview_polyline' => $route["overview_polyline"],
|
||||
'bounds' => $route["bounds"]
|
||||
];
|
||||
$distance = 0;
|
||||
$duration = 0;
|
||||
foreach ($route["legs"] as $leg) {
|
||||
$distance += $leg["distance"]["value"];
|
||||
$duration += $leg["duration"]["value"];
|
||||
foreach ($leg["steps"] as $step) {
|
||||
$points = Polyline::decode($step["polyline"]["points"]);
|
||||
$result['polyline'] = array_merge($result['polyline'],Polyline::pair($points));
|
||||
}
|
||||
}
|
||||
$result['distance'] = $distance;
|
||||
$result['duration'] = $duration;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function processRideshareOption($db, $option, $origin, $destination, $trip) {
|
||||
error_log("Options::processRideshareOption(\$db, \$option, \$origin, \$destination, \$trip)");
|
||||
$route = [];
|
||||
$route["travel_time"] = 0;
|
||||
$route["travel_distance"] = 0;
|
||||
$route["route_overlay"] = [];
|
||||
$member_id = $option["member_id"];
|
||||
$country = $origin["country"];
|
||||
$rideshare = $option["name"];
|
||||
$transport_provider_id = $option["transport_provider_id"];
|
||||
if (array_key_exists("timeZoneId",$origin) && $origin["timeZoneId"]!="") {
|
||||
$tz = $origin["timeZoneId"];
|
||||
} else if (array_key_exists("timeZoneId",$destination) && $destination["timeZoneId"]!="") {
|
||||
$tz = $destination["timeZoneId"];
|
||||
} else {
|
||||
$tz = Options::getTimezoneById($db, $origin["timezone"], "Asia/Singapore");
|
||||
}
|
||||
//"trips"=>$trips;
|
||||
if (!is_array($trip) && count($trip)<1) {
|
||||
syslog(LOG_WARNING,"Invalid trip");
|
||||
return [$option, $trip, $route]; // No idea how to handle it
|
||||
}
|
||||
/*if (!array_key_exists('trip',$trip) || !is_array($trip['trip']) || count($trip['trip'])<10) {
|
||||
syslog(LOG_WARNING,"Invalid trip[0]!");
|
||||
return $trips; // No idea how to handle it
|
||||
} //*/
|
||||
$oldTrip = $trip; //['trip'];
|
||||
|
||||
$origin['latitude'] = $origin['lat'];
|
||||
$origin['longitude'] = $origin['lng'];
|
||||
$destination['latitude'] = $destination['lat'];
|
||||
$destination['longitude'] = $destination['lng'];
|
||||
list($res, $err) = Route::serviceRoute($db,
|
||||
$origin['lat'], $origin['lng'],
|
||||
$destination['lat'], $destination['lng'], 'driving'
|
||||
);
|
||||
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
|
||||
error_log('No available routes!');
|
||||
error_log($err);
|
||||
return [$option, $trip, $route]; // We cannot route driving!
|
||||
}
|
||||
$route_overlay = [];
|
||||
$travel_time = PHP_INT_MAX;
|
||||
$travel_distance = PHP_INT_MAX;
|
||||
foreach ($res["routes"] as $item) {
|
||||
$r = Route::processRoute($item);
|
||||
if ($travel_time>$r['duration']) {
|
||||
$travel_time = $r['duration'];
|
||||
$travel_distance = $r['distance'];
|
||||
}
|
||||
$route_overlay[] = $r;
|
||||
}
|
||||
$oldTrip["duration"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
|
||||
$oldTrip["distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
|
||||
|
||||
$route["travel_time"] = (int)$oldTrip["duration"];
|
||||
$route["travel_distance"] = (int)$oldTrip["distance"];
|
||||
$route["route_overlay"] = $route_overlay;
|
||||
/*
|
||||
$result = [
|
||||
'polyline' => array(),
|
||||
'distance' => NULL,
|
||||
'duration' => NULL,
|
||||
'summary' => $route["summary"],
|
||||
'overview_polyline' => $route["overview_polyline"],
|
||||
'bounds' => $route["bounds"]
|
||||
];
|
||||
*/
|
||||
//syslog(LOG_WARNING,json_encode($res));
|
||||
$route_0 = $res["routes"][0];
|
||||
$route_leg = $route_0["legs"][0];
|
||||
$route_leg_steps = $route_leg["steps"];
|
||||
$overview_polyline = Route::processPolyline($route_0["overview_polyline"]["points"]);
|
||||
|
||||
$leg_steps = [];
|
||||
$distance = 0;
|
||||
$duration = 0;
|
||||
$location_start_lat = 0;
|
||||
$location_start_lng = 0;
|
||||
$location_end_lat = 0;
|
||||
$location_end_lng = 0;
|
||||
$polyline = [];
|
||||
$i = 0; $j=1; $n = count($route_leg_steps);
|
||||
for (; $i<$n; $i++) {
|
||||
$step = $route_leg_steps[$i];
|
||||
if ($step["travel_mode"] == "DRIVING") {
|
||||
$location_start_lat = $step["start_location"]["lat"];
|
||||
$location_start_lng = $step["start_location"]["lng"];
|
||||
break;
|
||||
}
|
||||
$leg_steps[] = [
|
||||
'distance' => $step["distance"]["value"],
|
||||
'duration' => $step["duration"]["value"],
|
||||
'html_instructions' => $step["html_instructions"],
|
||||
'location_end_lat' => $step["end_location"]["lat"],
|
||||
'location_end_lng' => $step["end_location"]["lng"],
|
||||
'location_start_lat' => $step["start_location"]["lat"],
|
||||
'location_start_lng' => $step["start_location"]["lng"],
|
||||
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
|
||||
'sid' => $j++,
|
||||
'travel_mode' => $step["travel_mode"]
|
||||
];
|
||||
}
|
||||
for (;$i<$n; $i++) {
|
||||
$step = $route_leg_steps[$i];
|
||||
if ($step["travel_mode"] != "DRIVING") {
|
||||
break;
|
||||
}
|
||||
$distance += $step["distance"]["value"];
|
||||
$duration += $step["duration"]["value"];
|
||||
$location_end_lat = $step["end_location"]["lat"];
|
||||
$location_end_lng = $step["end_location"]["lng"];
|
||||
$polyline[] = $step["polyline"]["points"];
|
||||
}
|
||||
$leg_steps[] = [
|
||||
'distance' => $distance,
|
||||
'duration' => $duration,
|
||||
'fare_quote' => null,
|
||||
'fare_raw' => null,
|
||||
'html_instructions' => "Take ".$rideshare." to ".$destination["address"],
|
||||
'location_end_lat' => $location_end_lat,
|
||||
'location_end_lng' => $location_end_lng,
|
||||
'location_start_lat' => $location_start_lat,
|
||||
'location_start_lng' => $location_start_lng,
|
||||
'polyline' => Route::processPolyline($polyline),
|
||||
'quote_group_id' => null,
|
||||
'sid' => $j++,
|
||||
'travel_mode' => strtoupper($rideshare)
|
||||
];
|
||||
for (;$i<$n; $i++) {
|
||||
$step = $route_leg_steps[$i];
|
||||
$leg_steps[] = [
|
||||
'distance' => $step["distance"]["value"],
|
||||
'duration' => $step["duration"]["value"],
|
||||
'html_instructions' => $step["html_instructions"],
|
||||
'location_end_lat' => $step["end_location"]["lat"],
|
||||
'location_end_lng' => $step["end_location"]["lng"],
|
||||
'location_start_lat' => $step["start_location"]["lat"],
|
||||
'location_start_lng' => $step["start_location"]["lng"],
|
||||
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
|
||||
'sid' => $j++,
|
||||
'travel_mode' => $step["travel_mode"]
|
||||
];
|
||||
}
|
||||
|
||||
$cost = null;
|
||||
|
||||
$rideshare = [
|
||||
'cost' => $cost,
|
||||
'cost_raw' => $cost,
|
||||
'count' => 1,
|
||||
'country' => $country,
|
||||
'distance' => $oldTrip['distance'],
|
||||
'dup_id' => null,
|
||||
'duration' => $oldTrip['duration'],
|
||||
'id' => $oldTrip['id'],
|
||||
'location_end' => $destination['address'],
|
||||
'location_end_id' => $oldTrip['location_end_id'],
|
||||
'location_end_lat' => $destination['latitude'],
|
||||
'location_end_lng' => $destination['longitude'],
|
||||
'location_end_tz' => $destination['timezone'],
|
||||
'location_start' => $origin['address'],
|
||||
'location_start_id' => $oldTrip['location_start_id'],
|
||||
'location_start_lat' => $origin['latitude'],
|
||||
'location_start_lng' => $origin['longitude'],
|
||||
'location_start_tz' => $origin['timezone'],
|
||||
'member_id' => $member_id,
|
||||
'options' => [],
|
||||
'parsedemail_item_id' => null, /* $trip['id'] ??? */
|
||||
'private' => 't',
|
||||
'scheduled' => null,
|
||||
'trackedemail_item_id' => null,
|
||||
'transport_provider_id' => $transport_provider_id,
|
||||
'travel_date' => $oldTrip['travel_date'],
|
||||
'travel_date_end' => $oldTrip['travel_date_end'],
|
||||
'updated' => $oldTrip['updated']
|
||||
];
|
||||
// Create trip advice
|
||||
$rideshare["source"] = strtolower($option["name"]);
|
||||
$parsedemail_item_advice_google_id = Options::saveTripAdvice($db, $rideshare);
|
||||
error_log(json_encode($parsedemail_item_advice_google_id));
|
||||
|
||||
$leg_fare = $cost;
|
||||
// Get departure and arrival times...
|
||||
$date = new DateTime("now", new DateTimeZone($tz));
|
||||
$date->add(new DateInterval('PT15M'));
|
||||
$departure_time = $date->getTimestamp();
|
||||
$duration = Route::getDuration(array_key_exists('duration',$oldTrip)?$oldTrip['duration']:900);
|
||||
$date->add(new DateInterval('PT'.$duration.'M'));
|
||||
$arrival_time = $date->getTimestamp();
|
||||
|
||||
$leg = [
|
||||
'id' => NULL,
|
||||
'parsedemail_item_advice_google_id' => $parsedemail_item_advice_google_id,
|
||||
'arrival_time' => $arrival_time,
|
||||
'arrival_time_zone' => $destination['timezone'],
|
||||
'arrival_timezone' => $tz,
|
||||
'departure_time' => $departure_time,
|
||||
'departure_time_zone' => $origin['timezone'],
|
||||
'departure_timezone' => $tz,
|
||||
'distance' => $oldTrip['distance'],
|
||||
'duration' => $oldTrip['duration'],
|
||||
'fare_raw' => $cost,
|
||||
'polyline' => $overview_polyline,
|
||||
'quote' => $cost,
|
||||
'steps' => count($leg_steps),
|
||||
];
|
||||
// Create leg
|
||||
$google_directions_leg_id = Options::saveTripAdviceLeg($db, $rideshare, $leg);
|
||||
$leg['id'] = $google_directions_leg_id;
|
||||
foreach ($leg_steps as $i=>$step) {
|
||||
$step['google_directions_leg_id'] = $google_directions_leg_id;
|
||||
$google_directions_leg_step_id = Options::saveTripAdviceLegStep($db, $rideshare, $leg, $step);
|
||||
$step['id'] = $google_directions_leg_step_id;
|
||||
$leg_steps[$i] = $step;
|
||||
}
|
||||
$options = [
|
||||
'gid' => $google_directions_leg_id, /* ??? */
|
||||
'leg_fare' => [$google_directions_leg_id => $leg_fare],
|
||||
'leg_steps' => [$google_directions_leg_id => $leg_steps],
|
||||
'legs' => [$leg],
|
||||
'routes' => 1
|
||||
];
|
||||
$rideshare['options'] = $options;
|
||||
return [$option, $rideshare, $route];
|
||||
}
|
||||
|
||||
protected static function getDuration($duration=900) {
|
||||
if ($duration==NULL) {
|
||||
return 15;
|
||||
}
|
||||
if ($duration>60) {
|
||||
return (int)($duration/60);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static function processPolyline($data) {
|
||||
if (!is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
require_once('../common/Polyline.php');
|
||||
$points = [];
|
||||
foreach ($data as $item) {
|
||||
$points = array_merge($points, Polyline::decode($item));
|
||||
//$points = array_merge(Polyline::decode($item),$points);
|
||||
break;
|
||||
}
|
||||
return Polyline::encode($points);
|
||||
}
|
||||
|
||||
public static function tripService($id, $country, $mode) {
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
error_log("Route::tripService($id)");
|
||||
if ($country!='SG' && $country!='US') {
|
||||
throw new Exception('Unsupported country: '.$country);
|
||||
}
|
||||
// curl -d 'id=44254' -d 'mode=BART' -d 'country=US' -H 'Authorization: Server-Token 99dfe35fcb7de1ee' -X POST {$oauth2_url}modetrips
|
||||
$postdata = http_build_query(
|
||||
array(
|
||||
'id'=> $id,
|
||||
'mode' => $mode,
|
||||
'country' => $country
|
||||
)
|
||||
);
|
||||
$url = $oauth2_url."modetrips";
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "POST",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
'content' => $postdata
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
error_log("started trips service call");
|
||||
$t = time();
|
||||
$body = file_get_contents($url, false, $context);
|
||||
error_log("complete trips service call: ".(time()-$t)." seconds");
|
||||
$trip = json_decode($body,true);
|
||||
return $trip;
|
||||
}
|
||||
}
|
||||
/*
|
||||
["id"]=>
|
||||
string(1) "5"
|
||||
["city_id"]=>
|
||||
string(1) "2"
|
||||
["name"]=>
|
||||
string(4) "Uber"
|
||||
["transport_provider_id"]=>
|
||||
string(1) "1"
|
||||
["custom"]=>
|
||||
NULL
|
||||
*/
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
class RouteApi extends Api
|
||||
{
|
||||
public $apiName = 'route';
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/remove/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
//id must be the first parameter after /remove/x
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction()
|
||||
{
|
||||
$message = "Unexpected options error";
|
||||
|
||||
$origin = $this->requestParams["origin"] ?? [];
|
||||
$destination = $this->requestParams["destination"] ?? [];
|
||||
$option = $this->requestParams["option"] ?? [];
|
||||
$trip = $this->requestParams["trip"] ?? [];
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
|
||||
try {
|
||||
$db = new Db();
|
||||
|
||||
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
|
||||
Options::checkValidOption($db->getConnect(), $option);
|
||||
Options::checkValidTrip($db->getConnect(), $trip);
|
||||
|
||||
$option["member_id"] = $member_id;
|
||||
$trip["member_id"] = $member_id;
|
||||
list ($res, $trip, $route) = Route::getTransportRoute($db->getConnect(), $origin, $destination, $option, $trip);
|
||||
if (is_array($res)) {
|
||||
return $this->response([
|
||||
'option' => $res,
|
||||
'trip' => $trip,
|
||||
'route' => $route
|
||||
], 200);
|
||||
} else {
|
||||
throw new Exception("Failed to get transport route for origin");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
<?php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
class Autocab {
|
||||
|
||||
const TIMEOUT = 60;
|
||||
const DEBUG = false;
|
||||
const SSL_VERIFY = false;
|
||||
|
||||
const REASON_NOT_REQUIRED = "NotRequired";
|
||||
const REASON_PRICE_CHANGED = "PriceChanged";
|
||||
|
||||
private $httpClient;
|
||||
private $agentId;
|
||||
private $agentPassword;
|
||||
private $currency;
|
||||
private $vendorId;
|
||||
private $templatesDir;
|
||||
private $config = [];
|
||||
private $templates = [];
|
||||
|
||||
|
||||
public function __construct($config) {
|
||||
if (empty($config)) {
|
||||
throw new Exception("Configuration is not provided");
|
||||
}
|
||||
$this->setConfig($config);
|
||||
if (!isset($config["baseUrl"]) || empty($config["baseUrl"])) {
|
||||
throw new Exception("The API URL is not specified");
|
||||
}
|
||||
|
||||
$this->httpClient = new Client(['base_uri' => $config["baseUrl"]]);
|
||||
}
|
||||
|
||||
public function setConfig($config) {
|
||||
global $savvyext;
|
||||
$this->config = $config;
|
||||
$this->agentId = $config["agentId"] ;
|
||||
$this->agentPassword = $config["agentPassword"];
|
||||
$this->currency = $config["currency"];
|
||||
$this->templatesDir = $config["templatesDir"];
|
||||
$this->loadTemplates($config);
|
||||
}
|
||||
|
||||
public function loadTemplates($config) {
|
||||
if ( !array_key_exists('templatesDir', $config) || empty($config["templatesDir"])) {
|
||||
throw new Exception("The templates dir is not specified");
|
||||
}
|
||||
$files = scandir($this->templatesDir);
|
||||
if ($files) {
|
||||
foreach ($files as $templateFile) {
|
||||
$template = file_get_contents($this->templatesDir."/".$templateFile);
|
||||
if ($template) {
|
||||
$this->templates[$templateFile] = $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($this->templates) == 0) {
|
||||
throw new Exception("Any one template is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
public function getTemplate($name, $oldRootTag=NULL, $newRootTag=NULL) {
|
||||
if (!isset($this->templates[$name])) {
|
||||
return false;
|
||||
}
|
||||
$template = $this->templates[$name];
|
||||
if (isset($oldRootTag) && isset($newRootTag)) {
|
||||
$template = str_replace($oldRootTag, $newRootTag, $template);
|
||||
}
|
||||
$xmlElement = new SimpleXMLElement($template);
|
||||
$xmlElement->Agent->attributes()->Id = $this->config["agentId"];
|
||||
$xmlElement->Agent->Password = $this->config["agentPassword"];
|
||||
return $xmlElement;
|
||||
}
|
||||
|
||||
public function getHeaders($extraHeaders = []) {
|
||||
$headers = [
|
||||
'content-type' => 'text/xml',
|
||||
];
|
||||
return array_merge($headers,$extraHeaders);
|
||||
}
|
||||
|
||||
public function getAvailability($source, $destination) {
|
||||
if (!isset($this->templates['autocab_agent_bid_request.xml'])) {
|
||||
throw new Exception("Bid request template is not initialized");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_bid_request.xml');
|
||||
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->BidParameters->Journey->From->Data = $source["address"];
|
||||
$xmlElement->BidParameters->Journey->From->Coordinate->Latitude = $source["latitude"];
|
||||
$xmlElement->BidParameters->Journey->From->Coordinate->Longitude = $source["longitude"];
|
||||
$xmlElement->BidParameters->Journey->To->Data = $destination["address"];
|
||||
$xmlElement->BidParameters->Journey->To->Coordinate->Latitude = $destination["latitude"];
|
||||
$xmlElement->BidParameters->Journey->To->Coordinate->Longitude = $destination["longitude"];
|
||||
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
|
||||
}
|
||||
$costs = [];
|
||||
foreach ($xmlResponse->Offers->Offer as $offer) {
|
||||
$item = self::processBidOffer($offer);
|
||||
array_push($costs, $item);
|
||||
}
|
||||
return array("error"=>0, "data"=>$costs, "raw_data"=>$xmlResponse, "request"=>$xmlElement);
|
||||
}
|
||||
|
||||
public function getBookingAvailability($source, $destination, $vendor) {
|
||||
|
||||
if (!isset($this->templates['autocab_agent_booking_availability_request.xml'])) {
|
||||
throw new Exception("Booking Availability request template is not initialized");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_booking_availability_request.xml');
|
||||
$xmlElement->Vendor->attributes()->Id = $vendor;
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->BookingParameters->Journey->From->Data = $source["address"] ?? "No Address";
|
||||
$xmlElement->BookingParameters->Journey->From->Coordinate->Latitude = $source["latitude"];
|
||||
$xmlElement->BookingParameters->Journey->From->Coordinate->Longitude = $source["longitude"];
|
||||
$xmlElement->BookingParameters->Journey->To->Data = $destination["address"] ?? "No Address";
|
||||
$xmlElement->BookingParameters->Journey->To->Coordinate->Latitude = $destination["latitude"];
|
||||
$xmlElement->BookingParameters->Journey->To->Coordinate->Longitude = $destination["longitude"];
|
||||
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
$cost = null;
|
||||
|
||||
if(isset($xmlResponse->Pricing)) {
|
||||
$cost = [
|
||||
'currency' => (string)$xmlResponse->Pricing->Currency,
|
||||
'availability_ref' => (string)$xmlResponse->AvailabilityReference,
|
||||
'display_name' => (string)$xmlResponse->VendorDetails->Name,
|
||||
'high_estimate' => (int)$xmlResponse->Pricing->Price,
|
||||
'low_estimate' => (int)$xmlResponse->Pricing->Price,
|
||||
'distance_estimate' => (int) $xmlResponse->EstimatedJourney->Distance,
|
||||
'duration_estimate' => (int) $xmlResponse->EstimatedJourney->Duration * 60,
|
||||
'vendor_id' => (string) $xmlResponse->Vendor->attributes()->Id,
|
||||
];
|
||||
}
|
||||
|
||||
return ["error"=>0, "data"=>$cost, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
public function cancelAvailability($availabilityRef, $vendor, $reason = self::REASON_NOT_REQUIRED) {
|
||||
|
||||
if (!isset($this->templates['autocab_agent_booking_not_authorized_request.xml'])) {
|
||||
throw new Exception("BookingNotAuthorizedRequest template is not initialized");
|
||||
}
|
||||
|
||||
if (!isset($availabilityRef)) {
|
||||
throw new Exception("Invalid availability reference");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_booking_not_authorized_request.xml');
|
||||
$xmlElement->Vendor->attributes()->Id = $vendor;
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->AvailabilityReference = $availabilityRef;
|
||||
$xmlElement->Reason = $reason;
|
||||
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
|
||||
}
|
||||
return ["error"=>0, "data"=>[], "raw_data"=>$xmlResponse, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
public function booking($availabilityRef, $passengers=[], $vendor) {
|
||||
|
||||
if (!isset($this->templates['autocab_agent_booking_athorization_request.xml'])) {
|
||||
throw new Exception("BookingAuthorization template is not initialized");
|
||||
}
|
||||
|
||||
if (!isset($availabilityRef)) {
|
||||
throw new Exception("Invalid availability reference");
|
||||
}
|
||||
|
||||
if (!is_array($passengers) || count($passengers) < 1
|
||||
|| !array_key_exists("name", $passengers[0]) || empty($passengers[0]["name"])
|
||||
) {
|
||||
throw new Exception("Invalid passengers information");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_booking_athorization_request.xml');
|
||||
$xmlElement->Vendor->attributes()->Id = $vendor;
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->AvailabilityReference = $availabilityRef;
|
||||
foreach ($passengers as $key => $passenger) {
|
||||
$xmlPassenger = $xmlElement->Passengers->addChild("PassengerDetails");
|
||||
$isLead = ($key === array_keys($passengers)[0]) ? "true" : "false";
|
||||
$xmlPassenger->addAttribute("IsLead", "$isLead");
|
||||
$xmlPassenger->addChild("Name", $passenger["name"]);
|
||||
if (array_key_exists("phone", $passenger) && isset($passenger["phone"])) {
|
||||
$xmlPassenger->addChild( "TelephoneNumber", $passenger["phone"] );
|
||||
}
|
||||
if (array_key_exists("email", $passenger) && isset($passenger["email"])) {
|
||||
$xmlPassenger->addChild("EmailAddress", $passenger["email"]);
|
||||
}
|
||||
}
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error];
|
||||
}
|
||||
|
||||
$resp = [];
|
||||
if(isset($xmlResponse->AuthorizationReference)) {
|
||||
$resp = [
|
||||
'authorization_ref' => (string)$xmlResponse->AuthorizationReference,
|
||||
'booking_ref' => (string)$xmlResponse->BookingReference,
|
||||
];
|
||||
}
|
||||
|
||||
return ["error"=>0, "data"=>$resp, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
public function bookingStatus($authorizationRef, $vendor) {
|
||||
|
||||
if (!isset($this->templates['autocab_agent_booking_status_request.xml'])) {
|
||||
throw new Exception("BookingStatusRequest template is not initialized");
|
||||
}
|
||||
|
||||
if (!isset($authorizationRef)) {
|
||||
throw new Exception("Invalid authorization reference");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_booking_status_request.xml');
|
||||
$xmlElement->Vendor->attributes()->Id = $vendor;
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->AuthorizationReference = $authorizationRef;
|
||||
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
$resp = [
|
||||
'booking_ref' => (string)$xmlResponse->BookingReference,
|
||||
'status' => (string)$xmlResponse->Status,
|
||||
'cancellation_reason' => (string)$xmlResponse->CancellationReason
|
||||
];
|
||||
return ["error"=>0, "data"=>$resp, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
public function bookingCancellation($authorizationRef, $vendor) {
|
||||
|
||||
if (!isset($this->templates['autocab_agent_booking_cancellation_request.xml'])) {
|
||||
throw new Exception("BookingCancellationRequest template is not initialized");
|
||||
}
|
||||
|
||||
if (!isset($authorizationRef)) {
|
||||
throw new Exception("Invalid authorization reference");
|
||||
}
|
||||
|
||||
$uri = '/api/agent';
|
||||
$xmlElement = $this->getTemplate('autocab_agent_booking_cancellation_request.xml');
|
||||
$xmlElement->Vendor->attributes()->Id = $vendor;
|
||||
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
|
||||
$xmlElement->AuthorizationReference = $authorizationRef;
|
||||
|
||||
$xml = $xmlElement->asXML();
|
||||
|
||||
$result = $this->genericPost($uri, $xml);
|
||||
|
||||
if ($result["error"]) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($result["code"] != 200) {
|
||||
var_dump($result["data"]);
|
||||
}
|
||||
|
||||
$xmlResponse = new SimpleXMLElement($result["data"]);
|
||||
$error = self::processError($xmlResponse);
|
||||
if($error) {
|
||||
return ["error"=>1, "data" => $error, "code" => 500, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
$resp = [];
|
||||
return ["error"=>0, "data"=>$resp, "code" => 200, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
|
||||
}
|
||||
|
||||
|
||||
private function genericPost($uri, $xml, $extraHeaders=[]) {
|
||||
$code = 0;
|
||||
$data = [];
|
||||
$error = 0;
|
||||
try {
|
||||
$json = json_encode($data);
|
||||
$length = strlen($json);
|
||||
$response = $this->httpClient->request('POST', $uri, [
|
||||
'decode_content' => true,
|
||||
'connect_timeout' => SELF::TIMEOUT,
|
||||
'debug' => SELF::DEBUG,
|
||||
'verify' => SELF::SSL_VERIFY,
|
||||
'headers' => $this->getHeaders(),
|
||||
'body' => $xml
|
||||
]);
|
||||
$code = $response->getStatusCode();
|
||||
$data = $response->getBody();
|
||||
} catch (RequestException $e) {
|
||||
if (SELF::DEBUG) {
|
||||
echo Psr7\str($e->getRequest());
|
||||
if ($e->hasResponse()) {
|
||||
echo Psr7\str($e->getResponse());
|
||||
}
|
||||
}
|
||||
if ($e->hasResponse()) {
|
||||
$code = (int) $e->getResponse()->getStatusCode();
|
||||
}
|
||||
$data = ["error" => "exception", "error_description" => $e->getMessage()];
|
||||
$error = 1;
|
||||
} catch (ClientException $e) {
|
||||
if (SELF::DEBUG) {
|
||||
echo Psr7\str($e->getRequest());
|
||||
echo Psr7\str($e->getResponse());
|
||||
}
|
||||
$code = (int)$e->getResponse()->getStatusCode();
|
||||
$data = ["error" => "exception", "error_description" => $e->getMessage()];
|
||||
$error = 1;
|
||||
} catch (Exception $e) {
|
||||
if (SELF::DEBUG) {
|
||||
echo Psr7\str($e->getRequest());
|
||||
echo Psr7\str($e->getResponse());
|
||||
}
|
||||
$code = (int)$e->getResponse()->getStatusCode();
|
||||
$data = ["error" => "exception", "error_description" => $e->getMessage()];
|
||||
$error = 1;
|
||||
}
|
||||
return ["error" => $error, "data" => $data, "code" => $code];
|
||||
}
|
||||
|
||||
public static function processError($xmlResponse) {
|
||||
if ((string)$xmlResponse->Result->Success == "true") {
|
||||
return false;
|
||||
}
|
||||
return ["error"=>"API error", "error_description" => (string) $xmlResponse->Result->FailureReason, "error_code" => (string) $xmlResponse->Result->FailureCode];
|
||||
}
|
||||
|
||||
public static function processBidOffer($offer) {
|
||||
$item = [
|
||||
'currency' => (string)$offer->Pricing->Currency,
|
||||
'transport_product' => (string)$offer['Reference'],
|
||||
'display_name' => (string)$offer->VendorDetails->Name,
|
||||
'high_estimate' => (int)$offer->Pricing->Price,
|
||||
'low_estimate' => (int)$offer->Pricing->Price,
|
||||
'distance_estimate' => (int) $offer->EstimatedJourney->Distance,
|
||||
'duration_estimate' => (int) $offer->EstimatedJourney->Duration * 60,
|
||||
];
|
||||
return $item;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<AgentBidRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<BidParameters>
|
||||
<Pricing>
|
||||
<Currency>USD</Currency>
|
||||
<PaymentType>Cash</PaymentType>
|
||||
</Pricing>
|
||||
<Journey>
|
||||
<From>
|
||||
<Type>Address</Type>
|
||||
<Data>1563 Van Dyke Ave, San Francisco, CA 94124, USA</Data>
|
||||
<Coordinate>
|
||||
<Latitude>37.7286068</Latitude>
|
||||
<Longitude>-122.3919092</Longitude>
|
||||
</Coordinate>
|
||||
</From>
|
||||
<To>
|
||||
<Type>Address</Type>
|
||||
<Data>536 Edinburgh St, San Francisco, CA 94112, USA</Data>
|
||||
<Coordinate>
|
||||
<Latitude>37.7206606</Latitude>
|
||||
<Longitude>-122.4326099</Longitude>
|
||||
</Coordinate>
|
||||
</To>
|
||||
</Journey>
|
||||
<Ride Type="Passenger">
|
||||
</Ride>
|
||||
</BidParameters>
|
||||
</AgentBidRequest>
|
||||
@@ -0,0 +1,16 @@
|
||||
<AgentBookingAuthorizationRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<Vendor Id="{{vendor_id}}" />
|
||||
<AvailabilityReference>{{availability_reference}}</AvailabilityReference>
|
||||
<Passengers>
|
||||
</Passengers>
|
||||
<Notifications>
|
||||
<VendorEvents>BookingDispatched NoFare BookingCompleted BookingCancelled</VendorEvents>
|
||||
<AlertMethod>None</AlertMethod>
|
||||
<AgentEvents>BookingDispatched NoFare BookingCompleted BookingCancelled LocationUpdate VehicleArrived PassengerOnBoard</AgentEvents>
|
||||
</Notifications>
|
||||
</AgentBookingAuthorizationRequest>
|
||||
@@ -0,0 +1,34 @@
|
||||
<AgentBookingAvailabilityRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<Vendor Id="{{vendor_id}}" />
|
||||
<BookingParameters>
|
||||
<Pricing>
|
||||
<Currency>USD</Currency>
|
||||
<PaymentType>Cash</PaymentType>
|
||||
</Pricing>
|
||||
<Journey>
|
||||
<From>
|
||||
<Type>Address</Type>
|
||||
<Data>1563 Van Dyke Ave, San Francisco, CA 94124, USA</Data>
|
||||
<Coordinate>
|
||||
<Latitude>37.7286068</Latitude>
|
||||
<Longitude>-122.3919092</Longitude>
|
||||
</Coordinate>
|
||||
</From>
|
||||
<To>
|
||||
<Type>Address</Type>
|
||||
<Data>536 Edinburgh St, San Francisco, CA 94112, USA</Data>
|
||||
<Coordinate>
|
||||
<Latitude>37.7206606</Latitude>
|
||||
<Longitude>-122.4326099</Longitude>
|
||||
</Coordinate>
|
||||
</To>
|
||||
</Journey>
|
||||
<Ride Type="Passenger">
|
||||
</Ride>
|
||||
</BookingParameters>
|
||||
</AgentBookingAvailabilityRequest>
|
||||
@@ -0,0 +1,9 @@
|
||||
<AgentBookingCancellationRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<Vendor Id="{{vendor_id}}" />
|
||||
<AuthorizationReference>{{authorization_reference}}</AuthorizationReference>
|
||||
</AgentBookingCancellationRequest>
|
||||
@@ -0,0 +1,13 @@
|
||||
<AgentBookingCancelledEventResponse>
|
||||
<Vendor Id="{{vendor_id}}">
|
||||
<Reference>{{vendor_ref}}</Reference>
|
||||
<Time>{{vendor_time}}</Time>
|
||||
</Vendor>
|
||||
<Agent Id="{{agent_id}}"/>
|
||||
<Result>
|
||||
<Success>true</Success>
|
||||
<FailureCode/>
|
||||
<FailureReason/>
|
||||
</Result>
|
||||
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
|
||||
</AgentBookingCancelledEventResponse>
|
||||
@@ -0,0 +1,10 @@
|
||||
<AgentBookingNotAuthorizedRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<Vendor Id="{{vendor_id}}" />
|
||||
<AvailabilityReference>{{availability_reference}}</AvailabilityReference>
|
||||
<Reason>NotRequired</Reason>
|
||||
</AgentBookingNotAuthorizedRequest>
|
||||
@@ -0,0 +1,9 @@
|
||||
<AgentBookingStatusRequest>
|
||||
<Agent Id="{{agent_id}}">
|
||||
<Password>{{agent_password}}</Password>
|
||||
<Reference>AgentRef</Reference>
|
||||
<Time>{{agent_time}}</Time>
|
||||
</Agent>
|
||||
<Vendor Id="{{vendor_id}}" />
|
||||
<AuthorizationReference>{{authorization_reference}}</AuthorizationReference>
|
||||
</AgentBookingStatusRequest>
|
||||
@@ -0,0 +1,13 @@
|
||||
<AgentVehicleArrivedEventResponse>
|
||||
<Vendor Id="{{vendor_id}}">
|
||||
<Reference>{{vendor_ref}}</Reference>
|
||||
<Time>{{vendor_time}}</Time>
|
||||
</Vendor>
|
||||
<Agent Id="{{agent_id}}"/>
|
||||
<Result>
|
||||
<Success>true</Success>
|
||||
<FailureCode/>
|
||||
<FailureReason/>
|
||||
</Result>
|
||||
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
|
||||
</AgentVehicleArrivedEventResponse>
|
||||
@@ -0,0 +1,13 @@
|
||||
<AgentEventResponse>
|
||||
<Vendor Id="{{vendor_id}}">
|
||||
<Reference>{{vendor_ref}}</Reference>
|
||||
<Time>{{vendor_time}}</Time>
|
||||
</Vendor>
|
||||
<Agent Id="{{agent_id}}"/>
|
||||
<Result>
|
||||
<Success>true</Success>
|
||||
<FailureCode/>
|
||||
<FailureReason/>
|
||||
</Result>
|
||||
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
|
||||
</AgentEventResponse>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
$profile = getenv("SAVVY_PROFILE");
|
||||
if ($profile) {
|
||||
require_once("../../core/backend.${profile}.php");
|
||||
} else {
|
||||
require_once( '../../core/backend.php' );
|
||||
}
|
||||
|
||||
include_once '../config.php';
|
||||
require_once('../constants.php');
|
||||
|
||||
require_once('../common/vendor/autoload.php');
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
require_once('../common/GoogleKMS.php');
|
||||
require_once('../common/Logger.php');
|
||||
|
||||
require_once('../trips/MultiModalFilter.php');
|
||||
|
||||
require_once('Options.php');
|
||||
require_once('OptionsApi.php');
|
||||
require_once('Quote.php');
|
||||
require_once('QuoteApi.php');
|
||||
require_once('Route.php');
|
||||
require_once('RouteApi.php');
|
||||
require_once('AutocabApi.php');
|
||||
require_once('autocab/Autocab.php');
|
||||
require_once('Booking.php');
|
||||
require_once('../trips/SGBikeApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
if ($requestUri[0]=='options') {
|
||||
$api = new OptionsApi($requestUri);
|
||||
}
|
||||
else if ($requestUri[0]=='route') {
|
||||
$api = new RouteApi($requestUri);
|
||||
}
|
||||
else if ($requestUri[0]=='quote') {
|
||||
$api = new QuoteApi($requestUri);
|
||||
}
|
||||
else if ($requestUri[0]=='autocab') {
|
||||
$api = new AutocabApi($requestUri);
|
||||
}
|
||||
else {
|
||||
echo json_encode(Array('error' => 'Invalid API request'));
|
||||
}
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,60 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="swagger-ui-bundle.js"> </script>
|
||||
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "../swagger.php",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require('../../../adminsavvy/vendor/autoload.php');
|
||||
$openapi = \OpenApi\scan('.');
|
||||
header('Content-Type: application/json');
|
||||
echo $openapi->toJson();
|
||||
?>
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/callback/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
class AccountApi extends Api
|
||||
{
|
||||
public $apiName = 'account';
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get member record (by id)
|
||||
* http://DOMAIN/trips/1
|
||||
* @return string
|
||||
*/
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/SAVVY/callback/api/account/:id",
|
||||
* summary="Get member account data by its unique ID",
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* required=true,
|
||||
* @OA\Schema(ref="#/components/schemas/member_id")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Members data",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Schema(ref="#/components/schemas/member_data")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
//id must be the first parameter after /account/x
|
||||
$id = array_shift($this->requestUri);
|
||||
|
||||
if($id && (int)$id>0){
|
||||
$db = new Db();
|
||||
$member = Callback::getMemberById($db->getConnect(), (int)$id);
|
||||
if(is_array($member) && count($member)>0){
|
||||
return $this->response($member, 200);
|
||||
}
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
// * description = "curl -d '{\"member_id\":22,\"last_acct\":\"2019-05-06\",\"count_acct\":1}' -X POST https://svrsavvy.sworks.float.sg/SAVVY/callback/api/account",
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/SAVVY/callback/api/account",
|
||||
* summary="Save account data within members record",
|
||||
* @OA\Parameter(
|
||||
* name="",
|
||||
* description = "Account callback data",
|
||||
* in="body",
|
||||
* required=true,
|
||||
* @OA\Schema(ref="#/components/schemas/account_request")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Members data",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Schema(ref="#/components/schemas/member_data")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
$message = "Failed to save data";
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
$last_acct = $this->requestParams["last_acct"] ?? "";
|
||||
$count_acct = $this->requestParams["count_acct"] ?? 0;
|
||||
|
||||
if ($member_id>0 && $last_acct!="" && strtotime($last_acct)>0 && $count_acct>0) {
|
||||
$db = new Db();
|
||||
$member = Callback::getMemberById($db->getConnect(), (int)$member_id);
|
||||
if (isset($member["id"]) && $member["id"]>0) {
|
||||
$result = Callback::updateMember(
|
||||
$db->getConnect(),
|
||||
(int)$member_id,
|
||||
$last_acct,
|
||||
$count_acct);
|
||||
if (is_array($result) && count($result)>0) {
|
||||
return $this->response($result, 200);
|
||||
} else {
|
||||
$message = "Failed to update member";
|
||||
}
|
||||
} else {
|
||||
$message = "Invalid member id";
|
||||
}
|
||||
} else {
|
||||
$message = "Invalid input $member_id>0 && $last_acct!= $count_acct";
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
public function updateAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Update error"
|
||||
), 400);
|
||||
}
|
||||
|
||||
public function deleteAction()
|
||||
{
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
class Callback {
|
||||
|
||||
public function getMemberById($db, $id) {
|
||||
$result = array();
|
||||
$db_id = (int)$id;
|
||||
$q = "SELECT * FROM members WHERE id=${db_id}";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getMemberByEmail($db, $email) {
|
||||
$result = array();
|
||||
$db_email = pg_escape_string(strtolower($email));
|
||||
$q = "SELECT * FROM members WHERE lower(email)='${db_email}' OR lower(username)='${db_email}'";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$result = $f;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateMember($db, $member_id, $last_acct, $count_acct) {
|
||||
$result = array();
|
||||
$id = (int)$member_id;
|
||||
$db_last_acct = date("Y-m-d H:i:s",strtotime($last_acct));
|
||||
$db_count_acct = (int)$count_acct;
|
||||
$q = "UPDATE members SET last_acct='${db_last_acct}',count_acct=${db_count_acct} WHERE id=${id}";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_affected_rows($r)) {
|
||||
return Callback::getMemberById($db, $id);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
require_once('../../core/backend.php');
|
||||
require_once('../constants.php');
|
||||
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
require_once('Callback.php');
|
||||
require_once('AccountAPI.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
if ($requestUri[0]=='account') {
|
||||
$api = new AccountApi($requestUri);
|
||||
}
|
||||
else {
|
||||
echo json_encode(Array('error' => 'Invalid API request'));
|
||||
}
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* title="Call Backend Endpoint API",
|
||||
* version="0.1",
|
||||
* @OA\Contact(
|
||||
* email="support@float.sg"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="member_id",
|
||||
* type="integer",
|
||||
* format="int64",
|
||||
* description="The unique identifier of a member in our system"
|
||||
* )
|
||||
*/
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="account_request",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="member_id",
|
||||
* description="The unique identifier of a member in our system",
|
||||
* type="int",
|
||||
* format="int64",
|
||||
* example=22
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="last_acct",
|
||||
* description="Last date of the account connection",
|
||||
* type="string",
|
||||
* format="date-time",
|
||||
* example="2019-05-06"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="count_acct",
|
||||
* description="Count of the account items",
|
||||
* type="int",
|
||||
* format="int64",
|
||||
* example=1
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="member_data",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="id",
|
||||
* description="The unique identifier of a member in our system",
|
||||
* type="int",
|
||||
* format="int64",
|
||||
* example=22
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="last_acct",
|
||||
* description="Last date of the account connection",
|
||||
* type="string",
|
||||
* format="date-time",
|
||||
* example="2019-05-06"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="count_acct",
|
||||
* description="Count of the account items",
|
||||
* type="int",
|
||||
* format="int64",
|
||||
* example=1
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,60 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="swagger-ui-bundle.js"> </script>
|
||||
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "../swagger.php",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require('../../../adminsavvy/vendor/autoload.php');
|
||||
$openapi = \OpenApi\scan('.');
|
||||
header('Content-Type: application/json');
|
||||
echo $openapi->toJson();
|
||||
?>
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/carpool/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
class Carpool {
|
||||
|
||||
|
||||
public function getCarpoolStatus($db, $gpsdb, $data) {
|
||||
|
||||
$member_id = (int) $data['member_id'];
|
||||
if ($member_id < 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// get user email
|
||||
$q = "select username from members WHERE id=${member_id};";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
while ($f = pg_fetch_assoc($r)) {
|
||||
// get user email
|
||||
$user_email = "";
|
||||
// if not found return error, weird
|
||||
}
|
||||
}
|
||||
|
||||
// get the sent invites
|
||||
$q = "select c.id, c.member_id, c.card_id, c.pool, c.status, c.added, c.updated, cf.firstname, cf.lastname, cf.email, cf.status, cf.accept_status, cf.pool_status
|
||||
from members_carpool c
|
||||
join members_carpool_friends cf on c.id = cf.carpool_id
|
||||
WHERE member_id = ${member_id};";
|
||||
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
while ($f = pg_fetch_assoc($r)) {
|
||||
// get user sent invites
|
||||
$sent_invites = array();
|
||||
}
|
||||
}
|
||||
|
||||
// get the received invites
|
||||
$q = "select id, carpool_id,email, status, accept_status, pool_status, added, updated from members_carpool_friends where email = '${user_email}';";
|
||||
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
while ($f = pg_fetch_assoc($r)) {
|
||||
// get user received invites
|
||||
$received_invites = array();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return array(
|
||||
'sent_invites' => $sent_invites,
|
||||
'received_invites' => $received_invites,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*
|
||||
CARPOOL AND POINT
|
||||
*/
|
||||
include '../../core/backend.php';
|
||||
include_once '../config.php';
|
||||
include_once '../constants.php';
|
||||
include '../formarter.php';
|
||||
|
||||
|
||||
|
||||
$endpoints = array(
|
||||
'carpooltrack' => array('POST'),
|
||||
'another' => array('POST')
|
||||
);
|
||||
|
||||
|
||||
//*
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
//*/
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["authorization"]) || substr($headers["authorization"], -strlen($httpAuthToken)) != $httpAuthToken) &&
|
||||
(!isset($headers["Authorization"]) || substr($headers["Authorization"], -strlen($httpAuthToken)) != $httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
$endpoint = strtolower(str_replace('/SAVVY/carpool/', '', strtok($_SERVER['REQUEST_URI'], '?')));
|
||||
|
||||
$id = 0; // update, get & delete actions require ID
|
||||
if (substr($endpoint, 0, 19) == 'gettransportrequest' || substr($endpoint, 0, 13) == 'updateprofile') {
|
||||
$endpoint = strtok($endpoint, '/');
|
||||
$id = strtok('/');
|
||||
}
|
||||
|
||||
if (!isset($endpoints[$endpoint])) {
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
header('Status: 400 Bad Request');
|
||||
echo "{\"status\":\"Invalid endpoint url\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
$methods = $endpoints[$endpoint];
|
||||
|
||||
if (array_search($_SERVER['REQUEST_METHOD'], $methods) === false) {
|
||||
header('HTTP/1.1 405 Method Not Allowed');
|
||||
header('Status: 405 Method Not Allowed');
|
||||
echo "{\"status\":\"Invalid request method\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
include '../rest_api.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
if ($endpoint == "uploadfile") {
|
||||
upload_file_call();
|
||||
exit();
|
||||
} else {
|
||||
$raw_json = file_get_contents("php://input");
|
||||
$raw_array = json_decode($raw_json, true);
|
||||
$in = flatten($raw_array);
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "PUT") {
|
||||
parse_str(file_get_contents('php://input'), $in);
|
||||
}
|
||||
|
||||
// Decrypt the input
|
||||
if (isset($in['encrypted_payload'])) {
|
||||
$payload = openssl_decrypt(hex2bin($in['encrypted_payload']), $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV);
|
||||
unset($in['encrypted_payload']);
|
||||
$in = array_merge($in, json_decode($payload, true));
|
||||
}
|
||||
|
||||
// get who is connecting IP
|
||||
$in["loc"] = getRemoteIpAddress(); // Do not use $_SERVER["REMOTE_ADDR"]; it is INVALID!!!
|
||||
$in["pid"] = 100;
|
||||
// override session parameter(s) with the header value
|
||||
$in["session"] = $headers["x-session-id"];
|
||||
$in["sessionid"] = $headers["x-session-id"];
|
||||
|
||||
$out = array();
|
||||
|
||||
$extension_call = true; // by defualt unless specified at the gate
|
||||
switch ($endpoint) {
|
||||
|
||||
case 'carpooltrack': $in["action"] = SAVVY_CARPOOL_TRACK;
|
||||
$out["status"] = "Got here anyway";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$in["pid"] = 100;
|
||||
|
||||
//file_put_contents("in_debug.log", $in); // DEBUG
|
||||
//external_internal_call($in, $out);
|
||||
|
||||
function Fextension_call($in, &$out) {
|
||||
global $savvyext;
|
||||
foreach ($in as $key=>$val) {
|
||||
if ($val!="" && is_string($val)) {
|
||||
$in[$key] = pg_escape_string($val);
|
||||
}
|
||||
}
|
||||
$out = $savvyext->savvyext_api($in);
|
||||
return $out["retval"];
|
||||
}
|
||||
|
||||
if ($extension_call == true) {
|
||||
Fextension_call($in, $out);
|
||||
}
|
||||
|
||||
header("HTTP/1.1 200 OK");
|
||||
header("Status: 200 OK");
|
||||
//$out = array_merge($in, $out); // DEBUG
|
||||
$payload = json_encode(processOutJson($in, $out));
|
||||
echo $payload."\n";
|
||||
//$encrypted_payload = bin2hex(openssl_encrypt($payload, $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV));
|
||||
//echo "{\"payload\": \"${encrypted_payload}\"}";
|
||||
exit();
|
||||
|
||||
function flatten($data, $parentkey = "") {
|
||||
$result = array();
|
||||
foreach ($data as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$result = array_merge($result, flatten($val, $parentkey . $key . "_"));
|
||||
} else {
|
||||
$result[$parentkey . $key] = $val;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getRemoteIpAddress() {
|
||||
$ip = NULL;
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)) {
|
||||
$ip = trim($_SERVER['HTTP_CLIENT_IP']);
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)) {
|
||||
$ip = trim($_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
} else {
|
||||
// Will not make much sense since we are behind the WAF reverse proxy
|
||||
$ip = trim($_SERVER['REMOTE_ADDR']);
|
||||
}
|
||||
putenv("REMOTE_ADDR=${ip}");
|
||||
$_ENV["REMOTE_ADDR"] = $ip;
|
||||
return $ip;
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
@@ -0,0 +1,27 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/cityservices/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
class CityServiceApi extends Api {
|
||||
|
||||
public $apiName = 'cityservices';
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
$encryption = false; // We do not need encryption for this class
|
||||
parent::__construct($requestUri, $encryption);
|
||||
//$this->encryption = $encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/SAVVY/touristattraction/api/touristattraction?city={city}&country={country}",
|
||||
* security={{"token": {}}},
|
||||
* summary="Get geofenceareacity by city and country code",
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Found geofence area city",
|
||||
* @OA\JsonContent(
|
||||
* type="object"
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function indexAction() {
|
||||
$message = 'No locations found';
|
||||
|
||||
$city = $this->requestParams['city'] ?? '';
|
||||
$results = [];
|
||||
$res_status = 404;
|
||||
|
||||
try {
|
||||
if (empty($city)) {
|
||||
throw new Exception("Invalid city");
|
||||
}
|
||||
|
||||
$db = new Db();
|
||||
list($results, $err) = CityServiceModel::get( $db->getConnect(), $city );
|
||||
if (!empty($err)) {
|
||||
$result = [
|
||||
"services" => NULL,
|
||||
"error" => $err
|
||||
];
|
||||
} else {
|
||||
$result = [
|
||||
"services" => $results,
|
||||
];
|
||||
$res_status = 200;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
$result = [
|
||||
"services" => NULL,
|
||||
"error" => $message
|
||||
];
|
||||
}
|
||||
|
||||
return $this->response($result, $res_status);
|
||||
}
|
||||
|
||||
public function viewAction() {}
|
||||
|
||||
public function createAction() {}
|
||||
|
||||
public function updateAction() {}
|
||||
|
||||
public function deleteAction() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
class CityServiceModel {
|
||||
|
||||
public static function get($db, $city) {
|
||||
$city = (int)$city;
|
||||
|
||||
// Step 1: Load address
|
||||
$q = "SELECT *FROM city_services WHERE city_id = ${city}";
|
||||
$r = pg_query($db, $q);
|
||||
if ($r && pg_num_rows($r)) {
|
||||
$results = pg_fetch_all($r);
|
||||
} else {
|
||||
return [NULL, "Services not found"];
|
||||
}
|
||||
|
||||
return [$results, NULL];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
require_once '../../core/backend.php';
|
||||
|
||||
require_once '../common/Api.php';
|
||||
require_once '../common/Db.php';
|
||||
|
||||
require_once 'CityServiceModel.php';
|
||||
require_once 'CityServiceApi.php';
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"], -strlen($httpAuthToken)) != $httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"], -strlen($httpAuthToken)) != $httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'], '/api/') === false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
|
||||
if ($requestUri[0] == 'cityservices') {
|
||||
$api = new CityServiceApi($requestUri, false);
|
||||
}
|
||||
echo $api->run();
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(array('error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* title="Geofence Area City Endpoint API",
|
||||
* version="1.0",
|
||||
* @OA\Contact(
|
||||
* email="support@float.sg"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
/**
|
||||
* @OA\SecurityScheme(
|
||||
* securityScheme="token",
|
||||
* type="apiKey",
|
||||
* name="Authorization",
|
||||
* in="header"
|
||||
* )
|
||||
*/
|
||||
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
require_once('../common/vendor/autoload.php');
|
||||
|
||||
use Phpfastcache\CacheManager;
|
||||
use Phpfastcache\Drivers\Redis\Config;
|
||||
|
||||
abstract class Api
|
||||
{
|
||||
public $apiName = ''; //trips
|
||||
|
||||
protected $method = ''; //GET|POST|PUT|DELETE
|
||||
|
||||
public $requestUri = [];
|
||||
public $requestParams = [];
|
||||
public $requestHeaders = [];
|
||||
public $requestWhitelist = [];
|
||||
public $clientIP = null;
|
||||
|
||||
protected $action = '';
|
||||
//Method name to execute
|
||||
protected $encryption = true;
|
||||
|
||||
public $cacheWhitelist = [];
|
||||
public $cache;
|
||||
public $cacheEnabled = false;
|
||||
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
global $savvyext;
|
||||
header("Access-Control-Allow-Orgin: *");
|
||||
header("Access-Control-Allow-Methods: *");
|
||||
header("Content-Type: application/json");
|
||||
|
||||
//GET parameter array separated by slash
|
||||
//$this->requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
$this->requestUri = $requestUri;
|
||||
|
||||
if (is_array($requestUri) && count($requestUri)>1) {
|
||||
if (($pos=strpos($requestUri[1],'?'))!==false) {
|
||||
$requestUri[1] = substr($requestUri[1],1+$pos);
|
||||
}
|
||||
parse_str($requestUri[1],$this->requestParams);
|
||||
}
|
||||
|
||||
$this->encryption = $encryption;
|
||||
|
||||
//Define the request method
|
||||
$this->method = $_SERVER['REQUEST_METHOD'];
|
||||
if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
|
||||
if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
|
||||
$this->method = 'DELETE';
|
||||
}
|
||||
else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
|
||||
$this->method = 'PUT';
|
||||
}
|
||||
else {
|
||||
throw new Exception("Unexpected Header");
|
||||
}
|
||||
}
|
||||
|
||||
$this->cacheEnabled = ($savvyext->cfgReadLong('cache.enabled') != NULL && $savvyext->cfgReadLong('cache.enabled') == 1);
|
||||
if ($this->cacheEnabled ) {
|
||||
$hostsString = $savvyext->cfgReadChar( 'cache.servers' );
|
||||
$serverConf = [];
|
||||
if ( ! empty( $hostsString ) ) {
|
||||
$hostsInfo = explode( ",", $hostsString );
|
||||
foreach ( $hostsInfo as $hostInfo ) {
|
||||
$hostInfoItems = explode( ":", $hostInfo );
|
||||
if ( count( $hostInfoItems ) > 0 ) {
|
||||
$serverConf = [
|
||||
'host' => $hostInfoItems[0],
|
||||
'port' => $hostInfoItems[1] ? intval( $hostInfoItems[1] ) : 6379,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( count( $serverConf ) != 0 ) {
|
||||
$conf = new Config($serverConf);
|
||||
$this->cache = CacheManager::getInstance( 'redis', $conf );
|
||||
} else {
|
||||
$this->cacheEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->method=="POST") {
|
||||
$raw_json = file_get_contents("php://input");
|
||||
$this->requestParams = json_decode($raw_json, true);
|
||||
}
|
||||
|
||||
if ($this->method == "PUT") {
|
||||
// Do we do key=val&key=val ?
|
||||
$raw_json = file_get_contents("php://input");
|
||||
if (strpos($raw_json, 'encrypted_payload') !== false) {
|
||||
$this->requestParams = json_decode($raw_json, true);
|
||||
} else {
|
||||
parse_str($raw_json, $this->requestParams);
|
||||
}
|
||||
}
|
||||
|
||||
// We can inspect the headers later on
|
||||
$this->loadRequestHeaders();
|
||||
|
||||
// Decrypt the input
|
||||
if (isset($this->requestParams['encrypted_payload'])) {
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$payload = openssl_decrypt(
|
||||
hex2bin(
|
||||
$this->requestParams['encrypted_payload']
|
||||
),
|
||||
$encryptionAlg,
|
||||
$encryptionKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$encryptionIV
|
||||
);
|
||||
unset($this->requestParams['encrypted_payload']);
|
||||
if (is_array($this->requestParams) && count($this->requestParams)>0) {
|
||||
$this->requestParams = array_merge($this->requestParams, json_decode($payload, true));
|
||||
} else {
|
||||
$data = json_decode($payload, true);
|
||||
$this->requestParams = is_array($data) ? $data : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No action taken YET!
|
||||
// TODO: delegate the decision into controller, the default (unset) behaviour is to block
|
||||
protected function checkRequestHeaders($db, $action) {
|
||||
error_log('Checking '.$this->apiName.'::'.$action.'...');
|
||||
if (array_key_exists($action,$this->requestWhitelist)) {
|
||||
error_log('whitelisted!');
|
||||
return true;
|
||||
}
|
||||
$sessionID = null;
|
||||
$deviceToken = null;
|
||||
if (array_key_exists("x-session-id",$this->requestHeaders)) {
|
||||
$sessionID = $this->requestHeaders["x-session-id"];
|
||||
}
|
||||
if (array_key_exists("x-devicetoken",$this->requestHeaders)) {
|
||||
$deviceToken = $this->requestHeaders["x-devicetoken"];
|
||||
}
|
||||
error_log('X-Session-ID: '.$sessionID);
|
||||
error_log('X-DeviceToken: '.$deviceToken);
|
||||
// Step 1a: Get member_id by X-DeviceToken
|
||||
$header_member_id = 0;
|
||||
$conn = $db->getConnect();
|
||||
$q = "SELECT * FROM members_devices WHERE access_token='".pg_escape_string($deviceToken)."'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$header_member_id = $f['member_id'];
|
||||
$q = "UPDATE members_devices SET updated=now(), status=1 WHERE id=".((int)$f['id'])." RETURNING *";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
error_log('Status updated at: '.$f['updated']);
|
||||
}
|
||||
}
|
||||
if ($header_member_id<1) {
|
||||
//return false; //throw new RuntimeException('Invalid header member ID', 500);
|
||||
}
|
||||
// Step 1b: Get member_id by X-Session-ID
|
||||
$session_member_id = 0;
|
||||
$q = "SELECT * FROM members_session WHERE session='".pg_escape_string($sessionID)."'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$session_member_id = $f['member_id'];
|
||||
if ($header_member_id<1) {
|
||||
$q = "UPDATE members_devices SET updated=now(), status=1 WHERE id=".((int)$f['id'])." RETURNING *";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
error_log('Status updated at: '.$f['updated']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($session_member_id<1) {
|
||||
//return false; //throw new RuntimeException('Invalid session member ID', 500);
|
||||
}
|
||||
// Step 2: Get member_id from $this->requestParams
|
||||
$request_member_id = 0;
|
||||
if (array_key_exists('member_id',$this->requestParams)) {
|
||||
$request_member_id = (int)$this->requestParams['member_id'];
|
||||
}
|
||||
error_log('member_id[request] = '.$request_member_id);
|
||||
error_log('member_id[token] = '.$header_member_id);
|
||||
error_log('member_id[session] = '.$session_member_id);
|
||||
if ($request_member_id != $session_member_id && $session_member_id>0) {
|
||||
//$request_member_id = $session_member_id;
|
||||
$this->requestParams['member_id'] = $session_member_id;
|
||||
}
|
||||
// Step 3a: Match Step 1 and 2 result
|
||||
if ($request_member_id>0) {
|
||||
// Step 3b: Fallback to X-Session-ID?
|
||||
if ($request_member_id!=$header_member_id || $request_member_id!=$session_member_id) {
|
||||
//return false; //throw new RuntimeException('Invalid request member ID', 500);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadRequestHeaders() {
|
||||
$this->requestHeaders = [];
|
||||
foreach (getallheaders() as $key=>$val) {
|
||||
// https://cloud.google.com/load-balancing/docs/https/
|
||||
// After September 30, HTTP(S) Load Balancers will convert HTTP/1.1 header names to lowercase
|
||||
// in the request and response directions; header values will not be affected.
|
||||
//error_log('DEBUG: "'.$key.'" => "'.$val.'"');
|
||||
$this->requestHeaders[strtolower($key)] = $val;
|
||||
}
|
||||
return count($this->requestHeaders);
|
||||
}
|
||||
|
||||
protected function checkThrottling($db) {
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)) {
|
||||
$ip = pg_escape_string($_SERVER['HTTP_CLIENT_IP']);
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)) {
|
||||
$ip = pg_escape_string($_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
} else {
|
||||
// Will not make much sense since we are behind the reverse proxy
|
||||
$ip = pg_escape_string($_SERVER['REMOTE_ADDR']);
|
||||
}
|
||||
$this->clientIP = $ip;
|
||||
$lastRec = NULL;
|
||||
$rec = NULL;
|
||||
$conn = $db->getConnect();
|
||||
$q = "SELECT *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms FROM throttling_ip WHERE ip='${ip}'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$lastRec = $f;
|
||||
$q = "UPDATE throttling_ip SET total=total+1,time_last=NOW() WHERE ip='${ip}' RETURNING *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$rec = $f;
|
||||
}
|
||||
} else {
|
||||
$q = "INSERT INTO throttling_ip (ip) VALUES ('${ip}') RETURNING *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$rec = $f;
|
||||
}
|
||||
}
|
||||
if (!$lastRec && $rec && is_array($rec) && count($rec)>0) {
|
||||
error_log('New IP spotted!');
|
||||
return true; // New record - never seen this IP before...
|
||||
}
|
||||
if (!$lastRec && (!$rec || !is_array($rec) || !count($rec)<1)) {
|
||||
// Failed to insert new record
|
||||
error_log('Error: not throttling!');
|
||||
return true; // TODO: should we fail?
|
||||
}
|
||||
// Compare $lastRec to $rec
|
||||
if (($rec["last_ms"]-$lastRec["last_ms"]) > 10) {
|
||||
error_log('Not throttle!');
|
||||
return true;
|
||||
} else {
|
||||
error_log('Throttle: ' . $rec["last_ms"] . " - " . $lastRec["last_ms"] . " = ".($rec["last_ms"]-$lastRec["last_ms"]));
|
||||
return true; // Throttle if less than a second within
|
||||
}
|
||||
return true; // OK
|
||||
}
|
||||
|
||||
protected function isActionCached($action) {
|
||||
error_log('Check is it '.$this->apiName.'::'.$action.' could be cached...');
|
||||
if (array_key_exists($action,$this->cacheWhitelist) || in_array($action,$this->cacheWhitelist)) {
|
||||
$ttl = $this->cacheWhitelist[$action]['ttl'] ?? 60;
|
||||
if ($ttl == 0) {
|
||||
return false;
|
||||
}
|
||||
error_log('whitelisted!');
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
|
||||
//The first 2 elemets of URI array must by "api" and table name
|
||||
if(array_shift($this->requestUri) !== $this->apiName){
|
||||
throw new RuntimeException('API Not Found', 404);
|
||||
}
|
||||
//Select the action to execute
|
||||
$this->action = $this->getAction();
|
||||
|
||||
$db = new Db();
|
||||
// Inspect throttling
|
||||
if (!$this->checkThrottling($db)) {
|
||||
unset($db);
|
||||
throw new RuntimeException('Too Many Requests', 429);
|
||||
}
|
||||
|
||||
// Inspect header
|
||||
if (!$this->checkRequestHeaders($db, $this->action)) {
|
||||
unset($db);
|
||||
throw new RuntimeException('Request check failed', 500);
|
||||
}
|
||||
|
||||
unset($db);
|
||||
|
||||
//If the method (action) defined in the child API class
|
||||
if (method_exists($this, $this->action)) {
|
||||
|
||||
$result = null;
|
||||
|
||||
if( $this->cacheEnabled && $this->isActionCached($this->action)) {
|
||||
$key = $this->getCacheKey();
|
||||
$ttl = $this->cacheWhitelist[$this->action]['ttl'] ?? 300;
|
||||
$cachedString = $this->cache->getItem($key);
|
||||
$result = $cachedString->get();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
$result = $this->{
|
||||
$this->action
|
||||
}
|
||||
();
|
||||
if($this->storeInCache()) {
|
||||
$cachedString->set($result)->expiresAfter($ttl);
|
||||
$this->cache->save($cachedString);
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return $this->{
|
||||
$this->action
|
||||
}
|
||||
();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException('Invalid Method', 405);
|
||||
}
|
||||
}
|
||||
|
||||
protected function response($data, $status = 500) {
|
||||
global $savvyext;
|
||||
header("HTTP/1.1 " . $status . " " . $this->requestStatus($status));
|
||||
if ($this->encryption) {
|
||||
// encrypt data
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$payload = json_encode($data);
|
||||
$encrypted_payload = bin2hex(
|
||||
openssl_encrypt(
|
||||
$payload,
|
||||
$encryptionAlg,
|
||||
$encryptionKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$encryptionIV
|
||||
)
|
||||
);
|
||||
$data = array(); // Comment out to see the data
|
||||
$data["payload"] = $encrypted_payload;
|
||||
}
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
private function requestStatus($code) {
|
||||
$status = array(
|
||||
200 => 'OK',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
500 => 'Internal Server Error',
|
||||
);
|
||||
return ($status[$code])?$status[$code]:$status[500];
|
||||
}
|
||||
|
||||
protected function getAction()
|
||||
{
|
||||
$method = $this->method;
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$pos = strpos($this->requestUri[0],'?');
|
||||
if ($pos!==false && $pos==0) {
|
||||
// We want to get "all"
|
||||
$this->requestUri[0] = "all".$this->requestUri[0];
|
||||
}
|
||||
$tok = strtok($this->requestUri[0],'?');
|
||||
if($tok===false || $tok=='all'){
|
||||
return 'indexAction';
|
||||
} else {
|
||||
return 'viewAction';
|
||||
}
|
||||
break;
|
||||
case 'POST':
|
||||
return 'createAction';
|
||||
break;
|
||||
case 'PUT':
|
||||
return 'updateAction';
|
||||
break;
|
||||
case 'DELETE':
|
||||
return 'deleteAction';
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function indexAction();
|
||||
abstract protected function viewAction();
|
||||
abstract protected function createAction();
|
||||
abstract protected function updateAction();
|
||||
abstract protected function deleteAction();
|
||||
|
||||
protected function getCacheKey() {
|
||||
return hash('md5', $this->method.'|'.$this->apiName.'|'.$this->action.'|'.json_encode($this->requestParams));
|
||||
}
|
||||
|
||||
protected function storeInCache() {
|
||||
return http_response_code() === 200;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class Db {
|
||||
|
||||
private $conn;
|
||||
private $conn_gps;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function getConnect() {
|
||||
global $savvyext;
|
||||
if ($this->conn==NULL || !pg_version($this->conn)) {
|
||||
$db_host = $savvyext->cfgReadChar('database.host');
|
||||
$db_name = $savvyext->cfgReadChar('database.name');
|
||||
$db_user = $savvyext->cfgReadChar('database.user');
|
||||
$db_pass = $savvyext->cfgReadChar('database.pass');
|
||||
$db_port = $savvyext->cfgReadLong('database.port');
|
||||
$connstr = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}";
|
||||
$this->conn = pg_connect($connstr);
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
public function getConnectGPS() {
|
||||
global $savvyext;
|
||||
if ($this->conn_gps==NULL || !pg_version($this->conn_gps)) {
|
||||
$db_host = $savvyext->cfgReadChar('gpsdatabase.host');
|
||||
$db_name = $savvyext->cfgReadChar('gpsdatabase.name');
|
||||
$db_user = $savvyext->cfgReadChar('gpsdatabase.user');
|
||||
$db_pass = $savvyext->cfgReadChar('gpsdatabase.pass');
|
||||
$db_port = $savvyext->cfgReadLong('gpsdatabase.port');
|
||||
$connstr = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}";
|
||||
$this->conn_gps = pg_connect($connstr);
|
||||
}
|
||||
return $this->conn_gps;
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
if(!empty($this->conn)){
|
||||
pg_close($this->conn);
|
||||
}
|
||||
if(!empty($this->conn_gps)){
|
||||
pg_close($this->conn_gps);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
class Gis {
|
||||
|
||||
final public static function haversineDistanceBetweenTwoGpsCoordinates($lat1, $lon1, $lat2, $lon2, $unit="M") {
|
||||
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
$theta = $lon1 - $lon2;
|
||||
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
|
||||
$dist = acos($dist);
|
||||
$dist = rad2deg($dist);
|
||||
$miles = $dist * 60 * 1.1515;
|
||||
$unit = strtoupper($unit);
|
||||
|
||||
if ($unit == "K") {
|
||||
return ($miles * 1.609344);
|
||||
} else if ($unit == "N") {
|
||||
return ($miles * 0.8684);
|
||||
} else {
|
||||
return $miles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final public static function cosinesDistanceBetweenTwoGpsCoordinates($lat1, $lon1, $lat2, $lon2, $unit="M") {
|
||||
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
|
||||
return 0;
|
||||
}
|
||||
$dist = acos(sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($lon2 - $lon1))) * 6371;
|
||||
$unit = strtoupper($unit);
|
||||
if ($unit == "K") {
|
||||
return $dist;
|
||||
} else {
|
||||
return $dist/1.609344;
|
||||
}
|
||||
}
|
||||
|
||||
final public static function getCityServicesAvailableForCoordinates($db_conn ,$lat, $lng, $providers=[]) {
|
||||
return self::getServicesAvailableForCoordinates($db_conn, self::GET_CITY_PROVIDERS_IN_RADIUS, $lat, $lng, $providers);
|
||||
}
|
||||
|
||||
final public static function getCountryServicesAvailableForCoordinates($db_conn ,$lat, $lng, $providers=[]) {
|
||||
return self::getServicesAvailableForCoordinates($db_conn, self::GET_COUNTRY_PROVIDERS_IN_RADIUS, $lat, $lng, $providers);
|
||||
}
|
||||
|
||||
final protected static function getServicesAvailableForCoordinates($db_conn, $q, $lat, $lng, $providers=[]) {
|
||||
|
||||
$result = [];
|
||||
|
||||
$params = [ $lng, $lat];
|
||||
|
||||
if (count($providers)) {
|
||||
$params[]="{".implode(",", $providers). "}";
|
||||
$q = $q."AND transport_provider_id = ANY($3)";
|
||||
}
|
||||
$req_result = pg_query_params($db_conn, $q, $params);
|
||||
if (!$req_result) {
|
||||
return $result;
|
||||
}
|
||||
while ($row = pg_fetch_assoc($req_result)) {
|
||||
array_push($result, $row);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
const GET_CITY_PROVIDERS_IN_RADIUS = "
|
||||
SELECT cs.* FROM geofence_area_city gac
|
||||
LEFT JOIN city_services cs ON gac.id = cs.city_id
|
||||
WHERE
|
||||
ST_DistanceSphere(location::geometry, ST_SetSRID(ST_MakePoint($1,$2),4326)) <= radius
|
||||
";
|
||||
|
||||
const GET_COUNTRY_PROVIDERS_IN_RADIUS = "
|
||||
SELECT cs.* FROM geofence_area_country gac
|
||||
LEFT JOIN country_services cs ON gac.id = cs.country_id
|
||||
WHERE
|
||||
ST_DistanceSphere(location::geometry, ST_SetSRID(ST_MakePoint($1,$2),4326)) <= radius
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
# Imports the Google Cloud client libraries
|
||||
use Google\ApiCore\ApiException;
|
||||
use Google\Cloud\Kms\V1\CryptoKey;
|
||||
use Google\Cloud\Kms\V1\CryptoKey\CryptoKeyPurpose;
|
||||
use Google\Cloud\Kms\V1\KeyManagementServiceClient;
|
||||
use Google\Cloud\Kms\V1\KeyRing;
|
||||
|
||||
class GoogleKMS {
|
||||
|
||||
private $client;
|
||||
private $projectId;
|
||||
private $authFile;
|
||||
|
||||
private $keyRing = NULL;
|
||||
private $keyRingId = NULL;
|
||||
private $keyRingName = NULL;
|
||||
private $keyName = NULL;
|
||||
private $cryptoKey = NULL;
|
||||
private $location = 'global';
|
||||
|
||||
public function __construct($projectId, $authFile, $keyRingId=NULL, $keyId=NULL) {
|
||||
// Your Google Cloud Platform project ID
|
||||
$this->projectId = $projectId; // 'float-app-224118';
|
||||
// The file path to credentials JSON
|
||||
//error_log($authFile);
|
||||
putenv("GOOGLE_APPLICATION_CREDENTIALS=${authFile}");
|
||||
apache_setenv("GOOGLE_APPLICATION_CREDENTIALS",$authFile,true);
|
||||
$this->authFile = $authFile; // './float-app-224118-52ef1783d2c5.json';
|
||||
// Instantiates a client
|
||||
$this->client = new KeyManagementServiceClient([
|
||||
'projectId' => $projectId,
|
||||
'keyFile' => json_decode(file_get_contents($authFile), true)
|
||||
]);
|
||||
if ($keyRingId!=NULL) {
|
||||
$this->createKeyring($keyRingId);
|
||||
if ($keyId!=NULL) {
|
||||
$this->createCryptokey($keyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createKeyring($keyRingId) {
|
||||
try {
|
||||
$locationName = $this->client::locationName(
|
||||
$this->projectId,
|
||||
$this->location
|
||||
);
|
||||
$keyRingName = $this->client::keyRingName(
|
||||
$this->projectId,
|
||||
$this->location,
|
||||
$keyRingId
|
||||
);
|
||||
$this->keyRing = $this->client->getKeyRing($keyRingName);
|
||||
$this->keyRingId = $keyRingId;
|
||||
$this->keyRingName = $keyRingName;
|
||||
} catch (ApiException $e) {
|
||||
if ($e->getStatus() === 'NOT_FOUND') {
|
||||
$this->keyRing = new KeyRing();
|
||||
$this->keyRing->setName($keyRingName);
|
||||
$this->client->createKeyRing(
|
||||
$locationName,
|
||||
$keyRingId,
|
||||
$this->keyRing);
|
||||
$this->keyRingId = $keyRingId;
|
||||
$this->keyRingName = $keyRingName;
|
||||
}
|
||||
}
|
||||
return $this->keyRing;
|
||||
}
|
||||
|
||||
public function createCryptokey($keyId) {
|
||||
try {
|
||||
$keyName = $this->client::cryptoKeyName(
|
||||
$this->projectId,
|
||||
$this->location,
|
||||
$this->keyRingId,
|
||||
$keyId);
|
||||
$this->cryptoKey = $this->client->getCryptoKey($keyName);
|
||||
$this->keyName = $keyName;
|
||||
} catch (ApiException $e) {
|
||||
if ($e->getStatus() === 'NOT_FOUND') {
|
||||
$this->cryptoKey = new CryptoKey();
|
||||
$this->cryptoKey->setPurpose(CryptoKeyPurpose::ENCRYPT_DECRYPT);
|
||||
$this->cryptoKey = $this->client->createCryptoKey(
|
||||
$this->keyRingName,
|
||||
$keyId,
|
||||
$this->cryptoKey);
|
||||
$this->keyName = $keyName;
|
||||
}
|
||||
}
|
||||
return $this->cryptoKey;
|
||||
}
|
||||
|
||||
public function encrypt($secret) {
|
||||
$response = $this->client->encrypt($this->keyName, $secret);
|
||||
$cipherText = $response->getCiphertext();
|
||||
return $cipherText;
|
||||
}
|
||||
|
||||
public function decrypt($cipherText) {
|
||||
$response = $this->client->decrypt($this->keyName, $cipherText);
|
||||
$plainText = $response->getPlaintext();
|
||||
return $plainText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
class GooglePlacesClient
|
||||
{
|
||||
public function get($url)
|
||||
{
|
||||
$curl = curl_init();
|
||||
$options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
);
|
||||
curl_setopt_array($curl, $options);
|
||||
$response = curl_exec($curl);
|
||||
if ($error = curl_error($curl))
|
||||
{
|
||||
throw new \Exception('CURL Error: ' . $error);
|
||||
}
|
||||
curl_close($curl);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
class GooglePlaces
|
||||
{
|
||||
public $client = '';
|
||||
public $sleep = 3;
|
||||
private $key = '';
|
||||
private $base_url = 'https://maps.googleapis.com/maps/api/place';
|
||||
private $method = null;
|
||||
private $response = null;
|
||||
|
||||
public $keyword = null;
|
||||
public $language = 'en';
|
||||
public $location = null;
|
||||
public $output = 'json';
|
||||
public $name = null;
|
||||
public $pagetoken = null;
|
||||
public $radius = null;
|
||||
public $rankby = 'prominence';
|
||||
public $sensor = false;
|
||||
public $types = null;
|
||||
public $placeid = null;
|
||||
public $reference = null;
|
||||
public $opennow = null;
|
||||
public $input = null;
|
||||
|
||||
public $subradius = null;
|
||||
public $getmax = true;
|
||||
private $grid = null;
|
||||
|
||||
private $exceptions = array(
|
||||
'base_url', 'client', 'exceptions', 'getmax', 'grid', 'method',
|
||||
'output', 'pagetoken', 'response', 'sleep', 'subradius',
|
||||
);
|
||||
|
||||
public function __construct($key, $client = false)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->client = $client ? $client : new GooglePlacesClient();
|
||||
}
|
||||
|
||||
function __set($variable, $value)
|
||||
{
|
||||
// Compensates for mixed variable naming
|
||||
$variable = str_replace('_', '', strtolower($variable));
|
||||
$this->$variable = $value;
|
||||
}
|
||||
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
$this->output = strtolower($this->output);
|
||||
|
||||
if (!in_array($this->output, array('json', 'xml')))
|
||||
{
|
||||
throw new \Exception('Invalid output, please specify either "json" or "xml".');
|
||||
}
|
||||
|
||||
$method = $this->method = strtolower($method);
|
||||
$url = implode('/', array($this->base_url, $method, $this->output));
|
||||
$parameters = array();
|
||||
$parameters = $this->parameterBuilder($parameters);
|
||||
$parameters = $this->methodChecker($parameters, $method);
|
||||
|
||||
if (!empty($this->subradius))
|
||||
{
|
||||
return $this->subdivide($url, $parameters);
|
||||
}
|
||||
|
||||
if ($this->pagetoken !== null)
|
||||
{
|
||||
$parameters['pagetoken'] = $this->pagetoken;
|
||||
}
|
||||
|
||||
$result = $this->queryGoogle($url, $parameters);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through all of our variables to make a parameter list
|
||||
*/
|
||||
private function parameterBuilder($parameters)
|
||||
{
|
||||
foreach (get_object_vars($this) as $variable => $value)
|
||||
{
|
||||
// Except these variables
|
||||
if (!in_array($variable, $this->exceptions))
|
||||
{
|
||||
// Assuming it's not null
|
||||
if ($value !== null)
|
||||
{
|
||||
// Converts boolean to string
|
||||
if (is_bool($value))
|
||||
{
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
switch ($variable)
|
||||
{
|
||||
// Allows LatLng to be passed as an array
|
||||
case 'location':
|
||||
if (is_array($value))
|
||||
{
|
||||
// Just in case it's an associative array
|
||||
$value = array_values($value);
|
||||
$value = $value[0] . ',' . $value[1];
|
||||
}
|
||||
break;
|
||||
|
||||
// Checks that it's a value rank by value
|
||||
case 'rankby':
|
||||
$value = strtolower($value);
|
||||
|
||||
if (!in_array($value, array('prominence', 'distance')))
|
||||
{
|
||||
throw new \Exception('Invalid rank by value, please specify either "prominence" or "distance".');
|
||||
}
|
||||
break;
|
||||
|
||||
// Allows types to be passed as an array
|
||||
case 'types':
|
||||
if (is_array($value))
|
||||
{
|
||||
$value = implode('|', $value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$parameters[$variable] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* takes the parameters and method to throw exceptions or modify parameters as needed
|
||||
* @todo Method to sanity check passed types
|
||||
*/
|
||||
private function methodChecker($parameters, $method)
|
||||
{
|
||||
if (!isset($parameters['pagetoken']))
|
||||
{
|
||||
switch ($method)
|
||||
{
|
||||
case 'nearbysearch':
|
||||
if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling nearbysearch().');
|
||||
}
|
||||
elseif (isset($parameters['rankby']))
|
||||
{
|
||||
switch ($parameters['rankby'])
|
||||
{
|
||||
case 'distance':
|
||||
if (!isset($parameters['keyword'])
|
||||
&& !isset($parameters['name'])
|
||||
&& !isset($parameters['types']))
|
||||
{
|
||||
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
|
||||
}
|
||||
|
||||
if (isset($parameters['radius']))
|
||||
{
|
||||
unset($this->radius, $parameters['radius']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'prominence':
|
||||
if (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'radarsearch':
|
||||
if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling radarsearch().');
|
||||
}
|
||||
elseif (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
elseif (empty($parameters['keyword'])
|
||||
&& empty($parameters['name'])
|
||||
&& empty($parameters['types']))
|
||||
{
|
||||
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
|
||||
}
|
||||
|
||||
if (isset($parameters['rankby']))
|
||||
{
|
||||
unset($this->rankby, $parameters['rankby']);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
if (!(isset($parameters['reference'])
|
||||
^ isset($parameters['placeid'])))
|
||||
{
|
||||
throw new \Exception('You must specify either a "placeid" or a "reference" (but not both) before calling details().');
|
||||
}
|
||||
|
||||
if (isset($parameters['rankby']))
|
||||
{
|
||||
unset($this->rankby, $parameters['rankby']);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'autocomplete':
|
||||
/*if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling autocomplete().');
|
||||
}
|
||||
elseif (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
else*/if (empty($parameters['input']))
|
||||
{
|
||||
throw new \Exception('You much specify the user entered input string.');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Submits request via curl, sets the response, then returns the response
|
||||
*/
|
||||
private function queryGoogle($url, $parameters)
|
||||
{
|
||||
if ($this->pagetoken !== null)
|
||||
{
|
||||
$parameters['pagetoken'] = $this->pagetoken;
|
||||
sleep($this->sleep);
|
||||
}
|
||||
|
||||
// Couldn't seem to get http_build_query() to work right so...
|
||||
$querystring = '';
|
||||
|
||||
foreach ($parameters as $variable => $value)
|
||||
{
|
||||
if ($querystring != '')
|
||||
{
|
||||
$querystring .= '&';
|
||||
}
|
||||
|
||||
$querystring .= $variable . '=' . urlencode($value);
|
||||
}
|
||||
|
||||
$response = $this->client->get($url . '?' . $querystring);
|
||||
|
||||
if ($this->output == 'json')
|
||||
{
|
||||
$response = json_decode($response, true);
|
||||
|
||||
if ($response === null)
|
||||
{
|
||||
throw new \Exception('The returned JSON was malformed or nonexistent.');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \Exception('XML is terrible, don\'t use it, ever.');
|
||||
}
|
||||
|
||||
$this->response = $response;
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the longitude equal to a given distance (meters) at a given latitude
|
||||
*/
|
||||
public function meters2lng($meters, $latitude)
|
||||
{
|
||||
return $meters / (cos(deg2rad($latitude)) * 40075160 / 360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latitude equal to a given distance (meters)
|
||||
*/
|
||||
public function meters2lat($meters)
|
||||
{
|
||||
return $meters / (40075160 / 360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aggregated responses for a subdivided search
|
||||
*/
|
||||
private function subdivide($url, $parameters)
|
||||
{
|
||||
if ($this->subradius < 200)
|
||||
{
|
||||
throw new \Exception('Subradius should be at least 200 meters.');
|
||||
}
|
||||
|
||||
$quotient = $parameters['radius'] / $this->subradius;
|
||||
|
||||
if ($parameters['radius'] % $this->subradius || $quotient % 2)
|
||||
{
|
||||
throw new \Exception('Subradius should divide evenly into radius.');
|
||||
}
|
||||
|
||||
$center = explode(',', $parameters['location']);
|
||||
$centerlat = $center[0];
|
||||
$centerlng = $center[1];
|
||||
$count = $quotient;
|
||||
$lati = $this->meters2lat($this->subradius * 2);
|
||||
|
||||
$this->grid['results'] = array();
|
||||
|
||||
for ($i = $count / 2 * -1; $i <= $count / 2; $i++)
|
||||
{
|
||||
$lat = $centerlat + $i * $lati;
|
||||
$lngi = $this->meters2lng($this->subradius * 2, $lat);
|
||||
|
||||
for ($j = $count / 2 * -1; $j <= $count / 2; $j++)
|
||||
{
|
||||
$lng = $centerlng + $j * $lngi;
|
||||
$loc = $lat . ',' . $lng;
|
||||
|
||||
$parameters['location'] = $loc;
|
||||
$parameters['radius'] = $this->subradius;
|
||||
|
||||
$pagetoken = true;
|
||||
|
||||
while ($pagetoken)
|
||||
{
|
||||
$this->queryGoogle($url, $parameters);
|
||||
|
||||
$this->grid[$i][$j] = $this->response;
|
||||
|
||||
$this->grid['results'] = array_merge(
|
||||
$this->grid['results'],
|
||||
$this->response['results']
|
||||
);
|
||||
|
||||
if (isset($this->response['next_page_token']))
|
||||
{
|
||||
$this->pagetoken = $this->response['next_page_token'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->pagetoken = null;
|
||||
$pagetoken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->grid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
class Logger
|
||||
{
|
||||
protected static $instance;
|
||||
private static $logger;
|
||||
|
||||
const ERROR = 0;
|
||||
const WARNING = 1;
|
||||
const INFO = 2;
|
||||
const DEBUG = 3;
|
||||
const DEBUG1 = 4;
|
||||
const DEBUG2 = 5;
|
||||
const DEBUG3 = 6;
|
||||
const DEBUG4 = 7;
|
||||
const SQL = 8;
|
||||
const FLOG_MAX = 9;
|
||||
|
||||
const LEVELS = [
|
||||
'ERROR', 'WARNING', 'INFO', 'DEBUG', 'DEBUG1', 'DEBUG2', 'DEBUG3', 'DEBUG4', 'SQL', 'FLOG_MAX'
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getLogger()
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::initLogger();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function initLogger()
|
||||
{
|
||||
global $savvyext;
|
||||
$fluent_host = $savvyext->cfgReadChar('phplogger.host');
|
||||
$fluent_port = $savvyext->cfgReadLong('phplogger.port');
|
||||
$log_enabled = $savvyext->cfgReadLong('phplogger.enabled');
|
||||
|
||||
if ($log_enabled == 1) {
|
||||
self::$logger = new Fluent\Logger\FluentLogger($fluent_host, $fluent_port);
|
||||
}
|
||||
self::$instance = self::$logger;
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arguments){
|
||||
if(is_array($arguments) && count($arguments)>0 && in_array(strtoupper($name), Logger::LEVELS)) {
|
||||
$data = $arguments[0];
|
||||
$tag = NULL;
|
||||
if (count($arguments)>1) {
|
||||
$tag = $arguments[1];
|
||||
}
|
||||
if (count($arguments)>2) {
|
||||
$name = $arguments[1];
|
||||
$tag = $arguments[2];
|
||||
}
|
||||
$level = Logger::levelByName($name);
|
||||
return Logger::log($level, $data, $name, $tag);
|
||||
}
|
||||
error_log("Logger::__callStatic($name, \$arguments) => Invalid method name!");
|
||||
}
|
||||
|
||||
public static function levelByName($name) {
|
||||
$val = strtoupper($name);
|
||||
if(in_array($val, Logger::LEVELS)) {
|
||||
return array_search($val, Logger::LEVELS, false);
|
||||
} else {
|
||||
return Logger::FLOG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
public static function nameByLevel($level) {
|
||||
$level = (int)$level;
|
||||
return Logger::ERROR < $level || $level > Logger::FLOG_MAX ? Logger::LEVELS[Logger::FLOG_MAX] : Logger::LEVELS[$level];
|
||||
}
|
||||
|
||||
public static function log($level, $data = [], $name = NULL, $tag = NULL)
|
||||
{
|
||||
global $savvyext;
|
||||
$clevel = $savvyext->cfgReadLong('phplogger.level');
|
||||
$ilevel = (int)$level;
|
||||
if ($ilevel > $clevel) {
|
||||
error_log("Logger::log() invalid log level! '$level' => '$ilevel' > '$clevel'");
|
||||
return;
|
||||
}
|
||||
$tag = $tag ?? $savvyext->cfgReadChar('phplogger.tag');
|
||||
$name = $name ?? $savvyext->cfgReadChar('phplogger.name');
|
||||
$data = [
|
||||
"log" => $name,
|
||||
"level" => Logger::nameByLevel($level),
|
||||
"pid" => getmypid(),
|
||||
"zz" => is_array($data) || is_object($data) ? json_encode($data) : $data,
|
||||
];
|
||||
if (self::getLogger()) {
|
||||
self::getLogger()->post($tag, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Polyline
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* A simple class to handle polyline-encoding for Google Maps
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Mapping
|
||||
* @package Polyline
|
||||
* @author E. McConville <emcconville@emcconville.com>
|
||||
* @copyright 2009-2015 E. McConville
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
|
||||
* @version GIT: $Id$
|
||||
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
|
||||
*/
|
||||
|
||||
/**
|
||||
* Polyline encoding & decoding class
|
||||
*
|
||||
* Convert list of points to encoded string following Google's Polyline
|
||||
* Algorithm.
|
||||
*
|
||||
* @category Mapping
|
||||
* @package Polyline
|
||||
* @author E. McConville <emcconville@emcconville.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
|
||||
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
|
||||
*/
|
||||
class Polyline
|
||||
{
|
||||
/**
|
||||
* Default precision level of 1e-5.
|
||||
*
|
||||
* Overwrite this property in extended class to adjust precision of numbers.
|
||||
* !!!CAUTION!!!
|
||||
* 1) Adjusting this value will not guarantee that third party
|
||||
* libraries will understand the change.
|
||||
* 2) Float point arithmetic IS NOT real number arithmetic. PHP's internal
|
||||
* float precision may contribute to undesired rounding.
|
||||
*
|
||||
* @var int $precision
|
||||
*/
|
||||
protected static $precision = 5;
|
||||
|
||||
/**
|
||||
* Apply Google Polyline algorithm to list of points.
|
||||
*
|
||||
* @param array $points List of points to encode. Can be a list of tuples,
|
||||
* or a flat, one-dimensional array.
|
||||
*
|
||||
* @return string encoded string
|
||||
*/
|
||||
final public static function encode( $points )
|
||||
{
|
||||
$points = self::flatten($points);
|
||||
$encodedString = '';
|
||||
$index = 0;
|
||||
$previous = array(0,0);
|
||||
foreach ( $points as $number ) {
|
||||
$number = (float)($number);
|
||||
$number = (int)round($number * pow(10, static::$precision));
|
||||
$diff = $number - $previous[$index % 2];
|
||||
$previous[$index % 2] = $number;
|
||||
$number = $diff;
|
||||
$index++;
|
||||
$number = ($number < 0) ? ~($number << 1) : ($number << 1);
|
||||
$chunk = '';
|
||||
while ( $number >= 0x20 ) {
|
||||
$chunk .= chr((0x20 | ($number & 0x1f)) + 63);
|
||||
$number >>= 5;
|
||||
}
|
||||
$chunk .= chr($number + 63);
|
||||
$encodedString .= $chunk;
|
||||
}
|
||||
return $encodedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse Google Polyline algorithm on encoded string.
|
||||
*
|
||||
* @param string $string Encoded string to extract points from.
|
||||
*
|
||||
* @return array points
|
||||
*/
|
||||
final public static function decode( $string )
|
||||
{
|
||||
$points = array();
|
||||
$index = $i = 0;
|
||||
$previous = array(0,0);
|
||||
while ($i < strlen($string)) {
|
||||
$shift = $result = 0x00;
|
||||
do {
|
||||
$bit = ord(substr($string, $i++)) - 63;
|
||||
$result |= ($bit & 0x1f) << $shift;
|
||||
$shift += 5;
|
||||
} while ($bit >= 0x20);
|
||||
|
||||
$diff = ($result & 1) ? ~($result >> 1) : ($result >> 1);
|
||||
$number = $previous[$index % 2] + $diff;
|
||||
$previous[$index % 2] = $number;
|
||||
$index++;
|
||||
$points[] = $number * 1 / pow(10, static::$precision);
|
||||
}
|
||||
return $points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce multi-dimensional to single list
|
||||
*
|
||||
* @param array $array Subject array to flatten.
|
||||
*
|
||||
* @return array flattened
|
||||
*/
|
||||
final public static function flatten( $array )
|
||||
{
|
||||
$flatten = array();
|
||||
array_walk_recursive(
|
||||
$array, // @codeCoverageIgnore
|
||||
function ($current) use (&$flatten) {
|
||||
$flatten[] = $current;
|
||||
}
|
||||
);
|
||||
return $flatten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concat list into pairs of points
|
||||
*
|
||||
* @param array $list One-dimensional array to segment into list of tuples.
|
||||
*
|
||||
* @return array pairs
|
||||
*/
|
||||
final public static function pair( $list )
|
||||
{
|
||||
return is_array($list) ? array_chunk($list, 2) : array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
//namespace Utils;
|
||||
|
||||
/**
|
||||
* Class RandomStringGenerator
|
||||
* @package Utils
|
||||
*
|
||||
* Solution taken from here:
|
||||
* http://stackoverflow.com/a/13733588/1056679
|
||||
*/
|
||||
class RandomStringGenerator
|
||||
{
|
||||
/** @var string */
|
||||
protected $alphabet;
|
||||
|
||||
/** @var int */
|
||||
protected $alphabetLength;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $alphabet
|
||||
*/
|
||||
public function __construct($alphabet = '')
|
||||
{
|
||||
if ('' !== $alphabet) {
|
||||
$this->setAlphabet($alphabet);
|
||||
} else {
|
||||
$this->setAlphabet(
|
||||
implode(range('a', 'z'))
|
||||
. implode(range('A', 'Z'))
|
||||
. implode(range(0, 9))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alphabet
|
||||
*/
|
||||
public function setAlphabet($alphabet)
|
||||
{
|
||||
$this->alphabet = $alphabet;
|
||||
$this->alphabetLength = strlen($alphabet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public function generate($length)
|
||||
{
|
||||
$token = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$randomKey = $this->getRandomInteger(0, $this->alphabetLength);
|
||||
$token .= $this->alphabet[$randomKey];
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return int
|
||||
*/
|
||||
protected function getRandomInteger($min, $max)
|
||||
{
|
||||
$range = ($max - $min);
|
||||
|
||||
if ($range < 0) {
|
||||
// Not so random...
|
||||
return $min;
|
||||
}
|
||||
|
||||
$log = log($range, 2);
|
||||
|
||||
// Length in bytes.
|
||||
$bytes = (int) ($log / 8) + 1;
|
||||
|
||||
// Length in bits.
|
||||
$bits = (int) $log + 1;
|
||||
|
||||
// Set all lower bits to 1.
|
||||
$filter = (int) (1 << $bits) - 1;
|
||||
|
||||
do {
|
||||
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
|
||||
|
||||
// Discard irrelevant bits.
|
||||
$rnd = $rnd & $filter;
|
||||
|
||||
} while ($rnd >= $range);
|
||||
|
||||
return ($min + $rnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
class Utilities
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function convertUtcToLocal($datetime, $timezone, $format = 'Y-m-d H:i:s')
|
||||
{
|
||||
if (!empty($datetime) && !empty($timezone)) {
|
||||
$datetime = date($format, strtotime($datetime));
|
||||
$utc_date = DateTime::createFromFormat(
|
||||
$format,
|
||||
$datetime,
|
||||
new DateTimeZone('UTC')
|
||||
);
|
||||
if ($utc_date) {
|
||||
$utc_date->setTimeZone(new DateTimeZone($timezone));
|
||||
return $utc_date->format($format);
|
||||
}
|
||||
}
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
public static function getClientIpAddress()
|
||||
{
|
||||
$ip="";
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
//ip from share internet
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
//ip pass from proxy
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
} else {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
public static function startBenchmark(&$benchmark=[]){
|
||||
$benchmark['start'] = microtime(true) * 1000;
|
||||
}
|
||||
|
||||
public static function finishBenchmark(&$benchmark){
|
||||
$benchmark['finish'] = microtime(true) * 1000;
|
||||
$benchmark['duration'] = $benchmark['finish'] - $benchmark['start'];
|
||||
error_log('benchmark'.json_encode($benchmark));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to validate the email address
|
||||
*
|
||||
* @author CodexWorld.com <contact@codexworld.com>
|
||||
* @copyright Copyright (c) 2018, CodexWorld.com
|
||||
* @url https://www.codexworld.com
|
||||
*/
|
||||
class VerifyEmail {
|
||||
|
||||
protected $stream = false;
|
||||
|
||||
/**
|
||||
* SMTP port number
|
||||
* @var int
|
||||
*/
|
||||
protected $port = 25;
|
||||
|
||||
/**
|
||||
* Email address for request
|
||||
* @var string
|
||||
*/
|
||||
protected $from = 'root@localhost';
|
||||
|
||||
/**
|
||||
* The connection timeout, in seconds.
|
||||
* @var int
|
||||
*/
|
||||
protected $max_connection_timeout = 30;
|
||||
|
||||
/**
|
||||
* Timeout value on stream, in seconds.
|
||||
* @var int
|
||||
*/
|
||||
protected $stream_timeout = 5;
|
||||
|
||||
/**
|
||||
* Wait timeout on stream, in seconds.
|
||||
* * 0 - not wait
|
||||
* @var int
|
||||
*/
|
||||
protected $stream_timeout_wait = 0;
|
||||
|
||||
/**
|
||||
* Whether to throw exceptions for errors.
|
||||
* @type boolean
|
||||
* @access protected
|
||||
*/
|
||||
protected $exceptions = false;
|
||||
|
||||
/**
|
||||
* The number of errors encountered.
|
||||
* @type integer
|
||||
* @access protected
|
||||
*/
|
||||
protected $error_count = 0;
|
||||
|
||||
/**
|
||||
* class debug output mode.
|
||||
* @type boolean
|
||||
*/
|
||||
public $Debug = false;
|
||||
|
||||
/**
|
||||
* How to handle debug output.
|
||||
* Options:
|
||||
* * `echo` Output plain-text as-is, appropriate for CLI
|
||||
* * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
|
||||
* * `log` Output to error log as configured in php.ini
|
||||
* @type string
|
||||
*/
|
||||
public $Debugoutput = 'echo';
|
||||
|
||||
/**
|
||||
* SMTP RFC standard line ending.
|
||||
*/
|
||||
const CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Holds the most recent error message.
|
||||
* @type string
|
||||
*/
|
||||
public $ErrorInfo = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param boolean $exceptions Should we throw external exceptions?
|
||||
*/
|
||||
public function __construct($exceptions = false) {
|
||||
$this->exceptions = (boolean) $exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set email address for SMTP request
|
||||
* @param string $email Email address
|
||||
*/
|
||||
public function setEmailFrom($email) {
|
||||
if (!self::validate($email)) {
|
||||
$this->set_error('Invalid address : ' . $email);
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
}
|
||||
$this->from = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout, in seconds.
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function setConnectionTimeout($seconds) {
|
||||
if ($seconds > 0) {
|
||||
$this->max_connection_timeout = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout value on stream, expressed in the seconds
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function setStreamTimeout($seconds) {
|
||||
if ($seconds > 0) {
|
||||
$this->stream_timeout = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
public function setStreamTimeoutWait($seconds) {
|
||||
if ($seconds >= 0) {
|
||||
$this->stream_timeout_wait = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email address.
|
||||
* @param string $email
|
||||
* @return boolean True if valid.
|
||||
*/
|
||||
public static function validate($email) {
|
||||
return (boolean) filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of MX records for host. Sort by weight information.
|
||||
* @param string $hostname The Internet host name.
|
||||
* @return array Array of the MX records found.
|
||||
*/
|
||||
public function getMXrecords($hostname) {
|
||||
$mxhosts = array();
|
||||
$mxweights = array();
|
||||
if (getmxrr($hostname, $mxhosts, $mxweights) === FALSE) {
|
||||
$this->set_error('MX records not found or an error occurred');
|
||||
$this->edebug($this->ErrorInfo);
|
||||
} else {
|
||||
array_multisort($mxweights, $mxhosts);
|
||||
}
|
||||
/**
|
||||
* Add A-record as last chance (e.g. if no MX record is there).
|
||||
* Thanks Nicht Lieb.
|
||||
* @link http://www.faqs.org/rfcs/rfc2821.html RFC 2821 - Simple Mail Transfer Protocol
|
||||
*/
|
||||
if (empty($mxhosts)) {
|
||||
$mxhosts[] = $hostname;
|
||||
}
|
||||
return $mxhosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses input string to array(0=>user, 1=>domain)
|
||||
* @param string $email
|
||||
* @param boolean $only_domain
|
||||
* @return string|array
|
||||
* @access private
|
||||
*/
|
||||
public static function parse_email($email, $only_domain = TRUE) {
|
||||
sscanf($email, "%[^@]@%s", $user, $domain);
|
||||
return ($only_domain) ? $domain : array($user, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error message to the error container.
|
||||
* @access protected
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
protected function set_error($msg) {
|
||||
$this->error_count++;
|
||||
$this->ErrorInfo = $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error occurred.
|
||||
* @access public
|
||||
* @return boolean True if an error did occur.
|
||||
*/
|
||||
public function isError() {
|
||||
return ($this->error_count > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output debugging info
|
||||
* Only generates output if debug output is enabled
|
||||
* @see verifyEmail::$Debugoutput
|
||||
* @see verifyEmail::$Debug
|
||||
* @param string $str
|
||||
*/
|
||||
protected function edebug($str) {
|
||||
if (!$this->Debug) {
|
||||
return;
|
||||
}
|
||||
switch ($this->Debugoutput) {
|
||||
case 'log':
|
||||
//Don't output, just log
|
||||
error_log($str);
|
||||
break;
|
||||
case 'html':
|
||||
//Cleans up output a bit for a better looking, HTML-safe output
|
||||
echo htmlentities(
|
||||
preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8'
|
||||
)
|
||||
. "<br>\n";
|
||||
break;
|
||||
case 'echo':
|
||||
default:
|
||||
//Normalize line breaks
|
||||
$str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
|
||||
echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
|
||||
"\n", "\n \t ", trim($str)
|
||||
) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email
|
||||
* @param string $email Email address
|
||||
* @return boolean True if the valid email also exist
|
||||
*/
|
||||
public function check($email) {
|
||||
$result = FALSE;
|
||||
|
||||
if (!self::validate($email)) {
|
||||
$this->set_error("{$email} incorrect e-mail");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
$this->error_count = 0; // Reset errors
|
||||
$this->stream = FALSE;
|
||||
|
||||
$mxs = $this->getMXrecords(self::parse_email($email));
|
||||
$timeout = ceil($this->max_connection_timeout / count($mxs));
|
||||
foreach ($mxs as $host) {
|
||||
/**
|
||||
* suppress error output from stream socket client...
|
||||
* Thanks Michael.
|
||||
*/
|
||||
$this->stream = @stream_socket_client("tcp://" . $host . ":" . $this->port, $errno, $errstr, $timeout);
|
||||
if ($this->stream === FALSE) {
|
||||
if ($errno == 0) {
|
||||
$this->set_error("Problem initializing the socket");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
} else {
|
||||
$this->edebug($host . ":" . $errstr);
|
||||
}
|
||||
} else {
|
||||
stream_set_timeout($this->stream, $this->stream_timeout);
|
||||
stream_set_blocking($this->stream, 1);
|
||||
|
||||
if ($this->_streamCode($this->_streamResponse()) == '220') {
|
||||
$this->edebug("Connection success {$host}");
|
||||
break;
|
||||
} else {
|
||||
fclose($this->stream);
|
||||
$this->stream = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->stream === FALSE) {
|
||||
$this->set_error("All connection fails");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$this->_streamQuery("HELO " . self::parse_email($this->from));
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("MAIL FROM: <{$this->from}>");
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("RCPT TO: <{$email}>");
|
||||
$code = $this->_streamCode($this->_streamResponse());
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("RSET");
|
||||
$this->_streamResponse();
|
||||
$code2 = $this->_streamCode($this->_streamResponse());
|
||||
$this->_streamQuery("QUIT");
|
||||
fclose($this->stream);
|
||||
|
||||
$code = !empty($code2)?$code2:$code;
|
||||
switch ($code) {
|
||||
case '250':
|
||||
/**
|
||||
* http://www.ietf.org/rfc/rfc0821.txt
|
||||
* 250 Requested mail action okay, completed
|
||||
* email address was accepted
|
||||
*/
|
||||
case '450':
|
||||
case '451':
|
||||
case '452':
|
||||
/**
|
||||
* http://www.ietf.org/rfc/rfc0821.txt
|
||||
* 450 Requested action not taken: the remote mail server
|
||||
* does not want to accept mail from your server for
|
||||
* some reason (IP address, blacklisting, etc..)
|
||||
* Thanks Nicht Lieb.
|
||||
* 451 Requested action aborted: local error in processing
|
||||
* 452 Requested action not taken: insufficient system storage
|
||||
* email address was greylisted (or some temporary error occured on the MTA)
|
||||
* i believe that e-mail exists
|
||||
*/
|
||||
return TRUE;
|
||||
case '550':
|
||||
return FALSE;
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the contents of string to the file stream pointed to by handle
|
||||
* If an error occurs, returns FALSE.
|
||||
* @access protected
|
||||
* @param string $string The string that is to be written
|
||||
* @return string Returns a result code, as an integer.
|
||||
*/
|
||||
protected function _streamQuery($query) {
|
||||
$this->edebug($query);
|
||||
return stream_socket_sendto($this->stream, $query . self::CRLF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the line long the answer and analyze it.
|
||||
* If an error occurs, returns FALSE
|
||||
* @access protected
|
||||
* @return string Response
|
||||
*/
|
||||
protected function _streamResponse($timed = 0) {
|
||||
$reply = stream_get_line($this->stream, 1);
|
||||
$status = stream_get_meta_data($this->stream);
|
||||
|
||||
if (!empty($status['timed_out'])) {
|
||||
$this->edebug("Timed out while waiting for data! (timeout {$this->stream_timeout} seconds)");
|
||||
}
|
||||
|
||||
if ($reply === FALSE && $status['timed_out'] && $timed < $this->stream_timeout_wait) {
|
||||
return $this->_streamResponse($timed + $this->stream_timeout);
|
||||
}
|
||||
|
||||
|
||||
if ($reply !== FALSE && $status['unread_bytes'] > 0) {
|
||||
$reply .= stream_get_line($this->stream, $status['unread_bytes'], self::CRLF);
|
||||
}
|
||||
$this->edebug($reply);
|
||||
return $reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Response code from Response
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _streamCode($str) {
|
||||
preg_match('/^(?<code>[0-9]{3})(\s|-)(.*)$/ims', $str, $matches);
|
||||
$code = isset($matches['code']) ? $matches['code'] : false;
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* verifyEmail exception handler
|
||||
*/
|
||||
class verifyEmailException extends Exception {
|
||||
|
||||
/**
|
||||
* Prettify error message output
|
||||
* @return string
|
||||
*/
|
||||
public function errorMessage() {
|
||||
$errorMsg = $this->getMessage();
|
||||
return $errorMsg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require": {
|
||||
"fluent/logger": "^1.0",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"google/apiclient": "^2.4",
|
||||
"google/cloud-kms": "^1.9",
|
||||
"ramsey/uuid": "^3.8",
|
||||
"phpfastcache/phpfastcache": "^7.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
$base_url = 'https://' . str_replace("extlayer", "svrlayer", $_SERVER["SERVER_NAME"]);
|
||||
$local_url = 'https://' . $_SERVER["SERVER_NAME"];
|
||||
$target_url = $base_url . "/internal.php";
|
||||
|
||||
$tracklocation_dir = $savvyext->cfgReadChar('system.tracklocation_dir');
|
||||
$gearmanServers = $savvyext->cfgReadChar('gearman.servers');
|
||||
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
global $pgconn, $pgconn_gps;
|
||||
//PostgreSQL
|
||||
$gps_db_host = $savvyext->cfgReadChar('gpsdatabase.host');
|
||||
$gps_db_port = $savvyext->cfgReadLong('gpsdatabase.port');
|
||||
$gps_db_name = $savvyext->cfgReadChar('gpsdatabase.name');
|
||||
$gps_db_user = $savvyext->cfgReadChar('gpsdatabase.user');
|
||||
$gps_db_pass = $savvyext->cfgReadChar('gpsdatabase.pass');
|
||||
$conn_string_gps = "host=${gps_db_host} port=${gps_db_port} dbname=${gps_db_name} user=${gps_db_user} password=${gps_db_pass}";
|
||||
$pgconn_gps = pg_connect($conn_string_gps);
|
||||
|
||||
$db_host = $savvyext->cfgReadChar('database.host');
|
||||
$db_port = $savvyext->cfgReadLong('database.port');
|
||||
$db_name = $savvyext->cfgReadChar('database.name');
|
||||
$db_user = $savvyext->cfgReadChar('database.user');
|
||||
$db_pass = $savvyext->cfgReadChar('database.pass');
|
||||
$conn_string = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}";
|
||||
$pgconn = pg_connect($conn_string);
|
||||
|
||||
if ($pgconn) {
|
||||
// echo "Okay";
|
||||
} else {
|
||||
//echo pg_last_error($pgconn);
|
||||
}
|
||||
|
||||
$autocabApiConfig = [
|
||||
'baseUrl' => $savvyext->cfgReadChar('autocab.baseUrl'),
|
||||
'agentId' => $savvyext->cfgReadChar('autocab.agentId'),
|
||||
'agentPassword' => $savvyext->cfgReadChar('autocab.agentPassword'),
|
||||
'currency' => $savvyext->cfgReadChar('autocab.currency'),
|
||||
'vendorId' => $savvyext->cfgReadChar('autocab.vendorId'),
|
||||
'templatesDir' => $savvyext->cfgReadChar('autocab.templatesDir')
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
$target_url = $base_url . "/internal.php";
|
||||
|
||||
if (defined('SITE_NAME')) {
|
||||
// Inclided more than once?
|
||||
} else {
|
||||
define('SITE_NAME', 'Savvy');
|
||||
define('SITE_EMAIL', 'info@savvy.com');
|
||||
define('SITE_PHONE', '+1 911 9110');
|
||||
define('SITE_FAX', '+1 9FX 9110');
|
||||
|
||||
define('PHP_API_OK', 0);
|
||||
define('MAX_ADMIN_SESSION', 1200);
|
||||
|
||||
define('MOBIDELIV_UPLOADS', 4505);
|
||||
define('MOBIDELIV_DOWNLOAD', 4506);
|
||||
|
||||
define('SAVVY_USER_CREATEACCOUNT', 22010);
|
||||
define('SAVVY_USER_PSERSONALITY', 22011);
|
||||
|
||||
define('SAVVY_USER_RESETPASS', 22013);
|
||||
define('SAVVY_USER_LOGINACCOUNT', 22015);
|
||||
define('SAVVY_USER_TRACKLOCATION', 22020);
|
||||
define('SAVVY_USER_SAVECARDPAYMT', 22025);
|
||||
define('SAVVY_USER_GETCARDPMYLIST', 22030);
|
||||
define('SAVVY_USER_MANAGEPAYLIST', 22035);
|
||||
define('SAVVY_USER_LOADUSERPROFILE', 22040);
|
||||
define('SAVVY_USER_ENABLEFEATURE', 22042);
|
||||
|
||||
define('SAVVY_USER_UPDATEUSERPROFILE', 22045);
|
||||
define('SAVVY_USER_TRANSPORTLIST', 22047);
|
||||
define('SAVVY_USER_TRANSPORTPROFILE', 22048);
|
||||
define('SAVVY_USER_SAVEBUDGET', 22049);
|
||||
|
||||
define('SAVVY_USERSUSC_LOAD', 22051);
|
||||
define('SAVVY_USERSUSC_STATUS', 22052);
|
||||
define('SAVVY_USERSAPP_GETLIST', 22053);
|
||||
|
||||
define('SAVVY_USER_GETSAVEDTRIPS', 22070);
|
||||
define('SAVVY_USER_SAVEUPDTTRIP', 22071);
|
||||
|
||||
define('SAVVY_USER_REFRESHSESSION', 22997);
|
||||
|
||||
define('SAVVY_USER_VERIFYSESSION', 22999);
|
||||
|
||||
define('SAVVY_USERSAPP_SLIDECARD', 44054);
|
||||
define('SAVVY_USERSAPP_DEALSUB', 44056);
|
||||
|
||||
define('SAVVY_USERSAPP_CARPOOL', 44057);
|
||||
|
||||
define('SAVVY_USERSAPP_SURVEY', 44060);
|
||||
define('SAVVY_USERSAPP_POINTSDEATAIL', 44061);
|
||||
define('SAVVY_USERSAPP_LOADREDEEM', 44062);
|
||||
define('SAVVY_USERSAPP_REDEEMPPOINTS', 44064);
|
||||
|
||||
define('SAVVY_USERSAPP_TRACKCARDCLICK', 44067);
|
||||
|
||||
define('SAVVY_USERCARD_DASHCARD', 40100);
|
||||
define('SAVVY_USERSAVE_DASHCARD', 40110);
|
||||
define('SAVVY_USERLOAD_SAVEDCARDS', 40115);
|
||||
define('SAVVY_USERPROP_SETTINGSARRAY', 40007);
|
||||
|
||||
define('SAVVY_CARPOOL_SUBSCRIBE', 60010);
|
||||
define('SAVVY_CARPOOL_STATUS', 60015);
|
||||
define('SAVVY_CARPOOL_TRACK', 60020);
|
||||
|
||||
//cards
|
||||
define('PHP_LOGIN_OK', 0);
|
||||
define('PHP_API_BAD_PARAM', -1);
|
||||
define('CARD_LOCATION_DEFAULT', 0);
|
||||
define('CARD_ADD_DENIED', 0);
|
||||
define('CARD_LOCATION_MAINFEED', 100);
|
||||
define('CARD_ADD_ALLOWED', 100);
|
||||
define('CARD_LOCATION_ACTIVITIES', 200);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/email/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
class Email {
|
||||
|
||||
static function createEmail($db, $to, $subject, $htmlBody) {
|
||||
$result = array();
|
||||
|
||||
$status = '0';
|
||||
|
||||
$values = array(
|
||||
"to_emails" => NULL,
|
||||
"subject" => NULL,
|
||||
"html_body" => NULL,
|
||||
"status"=>"0",
|
||||
"created_at"=>(new DateTime())->format('c'),
|
||||
"updated_at"=>(new DateTime())->format('c')
|
||||
);
|
||||
|
||||
if(isset($to)) {
|
||||
$values["to_emails"] = Email::prepareEmailsQueryString($to);
|
||||
}
|
||||
|
||||
if(isset($subject)) {
|
||||
$values["subject"] = pg_escape_string($subject);
|
||||
}
|
||||
|
||||
if(isset($htmlBody)) {
|
||||
$values["html_body"] = pg_escape_string($htmlBody);
|
||||
}
|
||||
|
||||
$values = array_filter($values, 'strlen');
|
||||
|
||||
$q = "INSERT INTO emails";
|
||||
|
||||
// implode keys of $values...
|
||||
$q .= " (".implode(", ", array_keys($values)).")";
|
||||
|
||||
// implode values of $values...
|
||||
$q .= " VALUES ('".implode("', '", $values)."') ";
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
if (!$rs) {
|
||||
return array("success" => false, "message" => "Something went wrong!");
|
||||
exit;
|
||||
}
|
||||
return array("success" => (boolean)pg_affected_rows($rs), "message" => "Successfully create new email.");
|
||||
}
|
||||
|
||||
static function getEmailById($db, $id) {
|
||||
$result = [];
|
||||
$q = "SELECT * from emails where id = '" . pg_escape_string($id) . "'";
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
while ($row = pg_fetch_assoc($rs)) {
|
||||
array_push($result, Email::parseData($row));
|
||||
}
|
||||
|
||||
return array("success" => true, "data" => $result[0]);
|
||||
}
|
||||
|
||||
static function findEmails($db, $filter, $limit=0)
|
||||
{
|
||||
$results = array();
|
||||
$condition = [];
|
||||
$q = "SELECT * FROM emails";
|
||||
|
||||
if (count($filter)) {
|
||||
$q .= " where ";
|
||||
if (isset($filter['status'])) {
|
||||
$condition[] = "status = '" . pg_escape_string($filter['status']) . "'";
|
||||
}
|
||||
if (isset($filter['retries'])) {
|
||||
$condition[] = "retries < ".((int)$filter['retries']);
|
||||
}
|
||||
}
|
||||
|
||||
$q .= implode(" AND ", $condition);
|
||||
|
||||
if ($limit>0) {
|
||||
$q.= " LIMIT ".$limit;
|
||||
}
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
while ($row = pg_fetch_assoc($rs)) {
|
||||
array_push($results, Email::parseData($row));
|
||||
}
|
||||
|
||||
return $results ?? NULL;
|
||||
}
|
||||
|
||||
public static function updateEmail($db, $id, $to, $subject, $htmlBody, $status) {
|
||||
if (!isset($id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$q = "update emails set ";
|
||||
$values = [
|
||||
"updated_at = '" . (new DateTime())->format('c') ."'"
|
||||
];
|
||||
|
||||
if (isset($to)) {
|
||||
// $q .= "to_email = " . Email::prepareEmailsQueryString($to);
|
||||
$values[] = "to_emails = '" . Email::prepareEmailsQueryString($to) . "'";
|
||||
}
|
||||
|
||||
if (isset($subject)) {
|
||||
$values[] = "subject = '" . pg_escape_string($subject) . "'";
|
||||
}
|
||||
|
||||
if (isset($htmlBody)) {
|
||||
$values[] = "html_body = '" . pg_escape_string($htmlBody) . "'";
|
||||
}
|
||||
|
||||
if (isset($status)) {
|
||||
$values[] = "status = '" . pg_escape_string($status) . "'";
|
||||
}
|
||||
|
||||
$q .= implode(", ", $values);
|
||||
$q .= " where id = " . pg_escape_string((string)$id);
|
||||
|
||||
$r = pg_query($db, $q);
|
||||
|
||||
if (!$r) {
|
||||
return array("success" => false, "message" => "Something went wrong!");
|
||||
exit;
|
||||
}
|
||||
|
||||
return array("success" => true, "message" => "Successfully update email.");
|
||||
}
|
||||
|
||||
private static function parseData($obj) {
|
||||
$emails = $obj['to_emails'];
|
||||
$emails = str_replace('{', '', $emails);
|
||||
$emails = str_replace('}', '', $emails);
|
||||
|
||||
$emails = explode(',', $emails);
|
||||
|
||||
$obj['to_emails'] = $emails;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private static function prepareEmailsQueryString($to) {
|
||||
$result = "{";
|
||||
$result .= join(',', $to);
|
||||
$result .= "}";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
require '../../core/backend.php';
|
||||
|
||||
class EmailApi extends Api
|
||||
{
|
||||
public $apiName = 'email';
|
||||
public $sg;
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
parent::__construct($requestUri, $encryption);
|
||||
|
||||
global $savvyext;
|
||||
|
||||
$apiKey = $savvyext->cfgReadChar('mailsend.api_key');
|
||||
$sg = new \SendGrid($apiKey);
|
||||
|
||||
$this->sg = $sg;
|
||||
}
|
||||
|
||||
public function indexAction() {
|
||||
$results = $this->findNewEmails();
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => true,
|
||||
"data" => $results
|
||||
), 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/v1/email/api/email/new",
|
||||
* summary="Send email to users",
|
||||
* @OA\Parameter(
|
||||
* name="name",
|
||||
* description = "Name of email template",
|
||||
* in="body",
|
||||
* required=true,
|
||||
* @OA\Schema(ref="#/components/schemas/create_params")
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
$db = new Db();
|
||||
$message = "Failed to send email";
|
||||
|
||||
$subject = $this->requestParams['subject'] ?? NULL;
|
||||
$receivers = $this->requestParams["to"] ?? NULL;
|
||||
$htmlBody = $this->requestParams['html_body'] ?? NULL;
|
||||
|
||||
if (!isset($subject) || !isset($receivers) || !isset($htmlBody)) {
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => false,
|
||||
"message" => 'Missing params'
|
||||
), 400);
|
||||
}
|
||||
|
||||
$result = Email::createEmail(
|
||||
$db->getConnect(),
|
||||
$receivers,
|
||||
$subject,
|
||||
$htmlBody
|
||||
);
|
||||
|
||||
if (!isset($result)) {
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => false,
|
||||
"message" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
return $this->response($result, 200);
|
||||
}
|
||||
|
||||
public function viewAction() {}
|
||||
|
||||
public function updateAction() {}
|
||||
|
||||
public function deleteAction() {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
|
||||
class MailchimpAPI
|
||||
{
|
||||
private static function handleResponse($response)
|
||||
{
|
||||
$res = json_decode($response->getBody());
|
||||
|
||||
if (!isset($res)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge((array) $res);
|
||||
}
|
||||
|
||||
public static function get($url, $params)
|
||||
{
|
||||
require_once('../../core/backend.php');
|
||||
global $savvyext;
|
||||
$token = $savvyext->cfgReadChar('mailchimp.api_key');
|
||||
$baseUri = $savvyext->cfgReadChar('mailchimp.domain');
|
||||
|
||||
$client = new Client(['base_uri' => $baseUri]);
|
||||
$header = ['Authorization' => "apikey $token"];
|
||||
|
||||
try {
|
||||
$response = $client->request('GET', $url, [
|
||||
'headers' => $header,
|
||||
'query' => $params,
|
||||
]);
|
||||
} catch (ServerException $e) {
|
||||
$response = $e->getResponse();
|
||||
} catch (ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
|
||||
return self::handleResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
use \SendGrid\Mail\To;
|
||||
use \SendGrid\Mail\Mail;
|
||||
use \SendGrid\Mail\From;
|
||||
use \SendGrid\Mail\HtmlContent;
|
||||
use \SendGrid\Mail\Personalization;
|
||||
|
||||
class SendGridService {
|
||||
public static function sendEmail($email) {
|
||||
global $savvyext;
|
||||
|
||||
$apiKey = $savvyext->cfgReadChar('mailsend.api_key');
|
||||
$sg = new \SendGrid($apiKey);
|
||||
|
||||
$response = $sg->send($email);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function prepareSengridToObject($receiver = [], $substitutions = NULL) {
|
||||
return new \SendGrid\Mail\To(
|
||||
$receiver['email'],
|
||||
$receiver['firstname'] ?? '',
|
||||
$substitutions ?? NULL
|
||||
);
|
||||
}
|
||||
|
||||
public static function prepareEmail($receivers, $subject, $template, $globalSubstitutions) {
|
||||
global $savvyext;
|
||||
|
||||
$fromEmail = $savvyext->cfgReadChar('mailsend.from');
|
||||
$fromName = $savvyext->cfgReadChar('mailsend.name');
|
||||
|
||||
$email = new Mail(
|
||||
new From($fromEmail, $fromName),
|
||||
NULL,
|
||||
$subject, // or array of subjects, these take precendence
|
||||
$plainTextContent ?? NULL,
|
||||
new HtmlContent($template),
|
||||
$globalSubstitutions ?? NULL
|
||||
);
|
||||
|
||||
foreach($receivers as $receiver) {
|
||||
$personalization = SendGridService::createPersonalize($receiver);
|
||||
$email->addPersonalization($personalization);
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
public static function createPersonalize($to) {
|
||||
$personalize = new Personalization();
|
||||
$personalize->addTo($to);
|
||||
|
||||
return $personalize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "vendor/email",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jack",
|
||||
"email": "jack@goldenowl.asia"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "~6.0",
|
||||
"sendgrid/sendgrid": "~7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
require_once('../../core/backend.php');
|
||||
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
require_once('Email.php');
|
||||
require_once('EmailApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'], '/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
|
||||
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
|
||||
if ($requestUri[0] == 'email') {
|
||||
$api = new EmailApi($requestUri, false);
|
||||
} else {
|
||||
echo json_encode(Array("message" => 'Invalid API request'));
|
||||
}
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array("message" => $e->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* title="Email Endpoint API",
|
||||
* version="1.0",
|
||||
* @OA\Contact(
|
||||
* email="support@float.sg"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="receivers",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="email",
|
||||
* description="Email of receiver",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="jack@goldenowl.asia"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="name",
|
||||
* description="Name of receiver",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="Jack Nguyen",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="substitutions",
|
||||
* description="Receiver's personal information for email"
|
||||
* type="object"
|
||||
* example="{ "{{key}}": "value" }"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="create_params",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="name",
|
||||
* description="Name of email template",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="welcome"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* description="Global dynamic data for the template",
|
||||
* type="object",
|
||||
* properties:
|
||||
* key:
|
||||
* type: string,
|
||||
* format: "{{key_name}}"
|
||||
* value:
|
||||
* type: string
|
||||
* example= {
|
||||
* "{{contactus}}": "https://float.sg"
|
||||
* }
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="receivers",
|
||||
* description="List email's receivers",
|
||||
* type="array",
|
||||
* items="$ref: '#/components/schemas/receivers' "
|
||||
*
|
||||
* )
|
||||
* )
|
||||
*/
|
||||