2014-01-25 22:32:46 +01:00

316 lines
10 KiB
PHP

<?php
namespace okapi\services\caches\formatters\gpx;
use okapi\Okapi;
use okapi\OkapiRequest;
use okapi\OkapiHttpResponse;
use okapi\OkapiInternalRequest;
use okapi\OkapiServiceRunner;
use okapi\BadRequest;
use okapi\ParamMissing;
use okapi\OkapiAccessToken;
use okapi\InvalidParam;
use okapi\services\caches\search\SearchAssistant;
use okapi\OkapiInternalConsumer;
use okapi\Db;
use okapi\Settings;
use okapi\services\attrs\AttrHelper;
class WebService
{
public static function options()
{
return array(
'min_auth_level' => 1
);
}
/** Maps OKAPI cache type codes to Geocaching.com GPX cache types. */
public static $cache_GPX_types = array(
'Traditional' => 'Traditional Cache',
'Multi' => 'Multi-Cache',
'Quiz' => 'Unknown Cache',
'Event' => 'Event Cache',
'Virtual' => 'Virtual Cache',
'Webcam' => 'Webcam Cache',
'Moving' => 'Unknown Cache',
'Math/Physics' => 'Unknown Cache',
'Drive-In' => 'Traditional Cache',
'Own' => 'Unknown Cache',
'Other' => 'Unknown Cache'
);
/** Maps OKAPI's 'size2' values to geocaching.com size codes. */
public static $cache_GPX_sizes = array(
'none' => 'Virtual',
'nano' => 'Micro',
'micro' => 'Micro',
'small' => 'Small',
'regular' => 'Regular',
'large' => 'Large',
'xlarge' => 'Large',
'other' => 'Other',
);
public static function call(OkapiRequest $request)
{
$vars = array();
# Validating arguments. We will also assign some of them to the
# $vars variable which we will use later in the GPS template.
$cache_codes = $request->get_parameter('cache_codes');
if ($cache_codes === null) throw new ParamMissing('cache_codes');
# Issue 106 requires us to allow empty list of cache codes to be passed into this method.
# All of the queries below have to be ready for $cache_codes to be empty!
$langpref = $request->get_parameter('langpref');
if (!$langpref) $langpref = "en";
foreach (array('ns_ground', 'ns_gsak', 'ns_ox', 'latest_logs', 'alt_wpts', 'mark_found') as $param)
{
$val = $request->get_parameter($param);
if (!$val) $val = "false";
elseif (!in_array($val, array("true", "false")))
throw new InvalidParam($param);
$vars[$param] = ($val == "true");
}
if ($vars['latest_logs'] && (!$vars['ns_ground']))
throw new BadRequest("In order for 'latest_logs' to work you have to also include 'ns_ground' extensions.");
$tmp = $request->get_parameter('my_notes');
$vars['my_notes'] = array();
if ($tmp && $tmp != 'none') {
$tmp = explode('|', $tmp);
foreach ($tmp as $elem) {
if ($elem == 'none') {
/* pass */
} elseif (in_array($elem, array('desc:text', 'gc:personal_note'))) {
if (in_array('none', $tmp)) {
throw new InvalidParam(
'my_notes', "You cannot mix 'none' and '$elem'"
);
}
if ($request->token == null) {
throw new BadRequest(
"Level 3 Authentication is required to access my_notes data."
);
}
$vars['my_notes'][] = $elem;
} else {
throw new InvalidParam('my_notes', "Invalid list entry: '$elem'");
}
}
}
$images = $request->get_parameter('images');
if (!$images) $images = 'descrefs:nonspoilers';
if (!in_array($images, array('none', 'descrefs:thumblinks', 'descrefs:nonspoilers', 'descrefs:all', 'ox:all')))
throw new InvalidParam('images', "'$images'");
$vars['images'] = $images;
$tmp = $request->get_parameter('attrs');
if (!$tmp) $tmp = 'desc:text';
$tmp = explode("|", $tmp);
$vars['attrs'] = array();
foreach ($tmp as $elem)
{
if ($elem == 'none') {
/* pass */
} elseif (in_array($elem, array('desc:text', 'ox:tags', 'gc:attrs', 'gc_ocde:attrs'))) {
if ($elem == 'gc_ocde:attrs' && Settings::get('OC_BRANCH') != 'oc.de')
$vars['attrs'][] = 'gc:attrs';
else
$vars['attrs'][] = $elem;
} else {
throw new InvalidParam('attrs', "Invalid list entry: '$elem'");
}
}
$protection_areas = $request->get_parameter('protection_areas');
if (!$protection_areas || $protection_areas == 'desc:auto')
{
if (Settings::get('OC_BRANCH') == 'oc.de') $protection_areas = 'desc:text';
else $protection_areas = 'none';
}
if (!in_array($protection_areas, array('none', 'desc:text')))
throw new InvalidParam('protection_areas',"'$protection_areas'");
$vars['protection_areas'] = $protection_areas;
$tmp = $request->get_parameter('trackables');
if (!$tmp) $tmp = 'none';
if (!in_array($tmp, array('none', 'desc:list', 'desc:count')))
throw new InvalidParam('trackables', "'$tmp'");
$vars['trackables'] = $tmp;
$tmp = $request->get_parameter('recommendations');
if (!$tmp) $tmp = 'none';
if (!in_array($tmp, array('none', 'desc:count')))
throw new InvalidParam('recommendations', "'$tmp'");
$vars['recommendations'] = $tmp;
$lpc = $request->get_parameter('lpc');
if ($lpc === null) $lpc = 10; # will be checked in services/caches/geocaches call
$user_uuid = $request->get_parameter('user_uuid');
# Which fields of the services/caches/geocaches method do we need?
$fields = 'code|name|location|date_created|url|type|status|size|size2|oxsize'.
'|difficulty|terrain|description|hint2|rating|owner|url|internal_id'.
'|protection_areas';
if ($vars['images'] != 'none')
$fields .= "|images";
if (count($vars['attrs']) > 0)
$fields .= "|attrnames|attr_acodes";
if ($vars['trackables'] == 'desc:list')
$fields .= "|trackables";
elseif ($vars['trackables'] == 'desc:count')
$fields .= "|trackables_count";
if ($vars['alt_wpts'] == 'true')
$fields .= "|alt_wpts";
if ($vars['recommendations'] != 'none')
$fields .= "|recommendations|founds";
if (count($vars['my_notes']) > 0)
$fields .= "|my_notes";
if ($vars['latest_logs'])
$fields .= "|latest_logs";
if ($vars['mark_found'])
$fields .= "|is_found";
$vars['caches'] = OkapiServiceRunner::call(
'services/caches/geocaches', new OkapiInternalRequest(
$request->consumer, $request->token, array(
'cache_codes' => $cache_codes,
'langpref' => $langpref,
'fields' => $fields,
'lpc' => $lpc,
'user_uuid' => $user_uuid,
'log_fields' => 'uuid|date|user|type|comment|internal_id|was_recommended'
)
)
);
# Get all the other data need.
$vars['installation'] = OkapiServiceRunner::call(
'services/apisrv/installation', new OkapiInternalRequest(
new OkapiInternalConsumer(), null, array()
)
);
$vars['cache_GPX_types'] = self::$cache_GPX_types;
$vars['cache_GPX_sizes'] = self::$cache_GPX_sizes;
if (count($vars['attrs']) > 0)
{
/* The user asked for some kind of attribute output. We'll fetch all
* the data we MAY need. This is often far too much, but thanks to
* caching, it will work fast. */
$vars['attr_index'] = OkapiServiceRunner::call(
'services/attrs/attribute_index', new OkapiInternalRequest(
$request->consumer, $request->token, array(
'only_locally_used' => 'true',
'langpref' => $langpref,
'fields' => 'name|gc_equivs'
)
)
);
# prepare GS attribute data
$vars['gc_attrs'] = in_array('gc:attrs', $vars['attrs']);
$vars['gc_ocde_attrs'] = in_array('gc_ocde:attrs', $vars['attrs']);
if ($vars['gc_attrs'] || $vars['gc_ocde_attrs'])
{
if ($vars['gc_ocde_attrs'])
{
# As this is an OCDE compatibility feature, we use the same Pseudo-GS
# attribute names here as OCDE. Note that this code is specific to OCDE
# database; OCPL stores attribute names in a different way and may use
# different names for equivalent attributes.
$ocde_attrnames = Db::select_group_by('id',"
select id, name
from cache_attrib
");
$attr_dict = AttrHelper::get_attrdict();
}
foreach ($vars['caches'] as &$cache)
{
$cache['gc_attrs'] = array();
foreach ($cache['attr_acodes'] as $acode)
{
$has_gc_equivs = false;
foreach ($vars['attr_index'][$acode]['gc_equivs'] as $gc)
{
# The assignment via GC-ID as array key will prohibit duplicate
# GC attributes, which can result from
# - assigning the same GC ID to multiple A-Codes,
# - contradicting attributes in one OC listing, e.g. 24/4 + not 24/7.
$cache['gc_attrs'][$gc['id']] = $gc;
$has_gc_equivs = true;
}
if (!$has_gc_equivs && $vars['gc_ocde_attrs'])
{
# Generate an OCDE pseudo-GS attribute;
# see http://code.google.com/p/opencaching-api/issues/detail?id=190 and
# http://code.google.com/p/opencaching-api/issues/detail?id=271.
#
# Groundspeak uses ID 1..65 (as of June, 2013), and OCDE makeshift
# IDs start at 106, so there is space for 40 new GS attributes.
$internal_id = $attr_dict[$acode]['internal_id'];
$cache['gc_attrs'][100 + $internal_id] = array(
'inc' => 1,
'name' => $ocde_attrnames[$internal_id][0]['name'],
);
}
}
}
}
}
/* OC sites always used internal user_ids in their generated GPX files.
* This might be considered an error in itself (Groundspeak's XML namespace
* doesn't allow that), but it very common (Garmin's OpenCaching.COM
* also does that). Therefore, for backward-compatibility reasons, OKAPI
* will do it the same way. See issue 174.
*
* Currently, the caches method does not expose "owner.internal_id" and
* "latest_logs.user.internal_id" fields, we will read them manually
* from the database here. */
$dict = array();
foreach ($vars['caches'] as &$cache_ref)
{
$dict[$cache_ref['owner']['uuid']] = true;
if (isset($cache_ref['latest_logs']))
foreach ($cache_ref['latest_logs'] as &$log_ref)
$dict[$log_ref['user']['uuid']] = true;
}
$rs = Db::query("
select uuid, user_id
from user
where uuid in ('".implode("','", array_map('mysql_real_escape_string', array_keys($dict)))."')
");
while ($row = mysql_fetch_assoc($rs))
$dict[$row['uuid']] = $row['user_id'];
$vars['user_uuid_to_internal_id'] = &$dict;
unset($dict);
$response = new OkapiHttpResponse();
$response->content_type = "application/gpx; charset=utf-8";
$response->content_disposition = 'attachment; filename="results.gpx"';
ob_start();
Okapi::gettext_domain_init(explode("|", $langpref)); # Consumer gets properly localized GPX file.
include 'gpxfile.tpl.php';
Okapi::gettext_domain_restore();
$response->body = ob_get_clean();
return $response;
}
}