OKAPI Project update (r1055)
This commit is contained in:
parent
3a24b647f4
commit
3f713f6d79
@ -741,15 +741,17 @@ class OkapiHttpResponse
|
||||
header("ETag: $this->etag");
|
||||
|
||||
# Make sure that gzip is supported by the client.
|
||||
$try_gzip = $this->allow_gzip;
|
||||
$use_gzip = $this->allow_gzip;
|
||||
if (empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") === false))
|
||||
$try_gzip = false;
|
||||
$use_gzip = false;
|
||||
|
||||
# We will gzip the data ourselves, while disabling gziping by Apache. This way, we can
|
||||
# set the Content-Length correctly which is handy in some scenarios.
|
||||
|
||||
if ($try_gzip && is_string($this->body))
|
||||
if ($use_gzip && is_string($this->body))
|
||||
{
|
||||
# Apache won't gzip a response which is already gzipped.
|
||||
|
||||
header("Content-Encoding: gzip");
|
||||
$gzipped = gzencode($this->body, 5);
|
||||
header("Content-Length: ".strlen($gzipped));
|
||||
@ -757,6 +759,12 @@ class OkapiHttpResponse
|
||||
}
|
||||
else
|
||||
{
|
||||
# We don't want Apache to gzip this response. Tell it so.
|
||||
|
||||
if (function_exists('apache_setenv')) {
|
||||
@apache_setenv('no-gzip', 1);
|
||||
}
|
||||
|
||||
$length = $this->get_length();
|
||||
if ($length)
|
||||
header("Content-Length: ".$length);
|
||||
@ -776,6 +784,41 @@ class OkapiRedirectResponse extends OkapiHttpResponse
|
||||
}
|
||||
}
|
||||
|
||||
class OkapiZIPHttpResponse extends OkapiHttpResponse
|
||||
{
|
||||
public $zip;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
require_once ($GLOBALS['rootpath'].'okapi/lib/tbszip.php');
|
||||
|
||||
$this->zip = new \clsTbsZip();
|
||||
$this->zip->CreateNew();
|
||||
}
|
||||
|
||||
public function print_body()
|
||||
{
|
||||
$this->zip->Flush(TBSZIP_DOWNLOAD|TBSZIP_NOHEADER);
|
||||
}
|
||||
|
||||
public function get_body()
|
||||
{
|
||||
$this->zip->Flush(TBSZIP_STRING);
|
||||
return $this->zip->OutputSrc;
|
||||
}
|
||||
|
||||
public function get_length()
|
||||
{
|
||||
return $this->zip->_EstimateNewArchSize();
|
||||
}
|
||||
|
||||
public function display()
|
||||
{
|
||||
$this->allow_gzip = false;
|
||||
parent::display();
|
||||
}
|
||||
}
|
||||
|
||||
class OkapiLock
|
||||
{
|
||||
private $lockfile;
|
||||
@ -847,7 +890,7 @@ class Okapi
|
||||
{
|
||||
public static $data_store;
|
||||
public static $server;
|
||||
public static $revision = 1031; # This gets replaced in automatically deployed packages
|
||||
public static $revision = 1055; # This gets replaced in automatically deployed packages
|
||||
private static $okapi_vars = null;
|
||||
|
||||
/** Get a variable stored in okapi_vars. If variable not found, return $default. */
|
||||
|
142
htdocs/okapi/lib/ocpl_access_logs.php
Normal file
142
htdocs/okapi/lib/ocpl_access_logs.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?
|
||||
|
||||
namespace okapi;
|
||||
|
||||
/**
|
||||
* This is a (hopefully temporary) class which holds all funtionality related
|
||||
* to OCPL's "access logging" feature. OCPL admins use this feature to track
|
||||
* suspicious activity of some certain users.
|
||||
*
|
||||
* Maintainer: boguslaw.szczepanowski@gmail.com
|
||||
*/
|
||||
class OCPLAccessLogs
|
||||
{
|
||||
/**
|
||||
* Return method name (without full path) which was originally called against OKAPI.
|
||||
*/
|
||||
public static function get_original_caller()
|
||||
{
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$org_caller = null;
|
||||
$break_next = false;
|
||||
// traverse PHP call stack to find out who originally called us
|
||||
// first, find first service_runner.php invocation
|
||||
// then, find previous class invocation
|
||||
for($i = count($trace)-1; $i >= 0; $i--)
|
||||
{
|
||||
$frame = $trace[$i];
|
||||
if ($break_next && isset($frame['class']))
|
||||
{
|
||||
$class_elems = explode('\\', $frame['class']);
|
||||
if (count($class_elems) >= 2)
|
||||
$org_caller = $class_elems[count($class_elems)-2];
|
||||
break;
|
||||
}
|
||||
if (isset($frame['file']) &&
|
||||
// test if file ends with service_runner.php
|
||||
substr($frame['file'], -strlen('service_runner.php')) === 'service_runner.php')
|
||||
{
|
||||
$break_next = true;
|
||||
}
|
||||
}
|
||||
return $org_caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log detailed geocache data access
|
||||
* @param OkapiRequest $request
|
||||
* @param mixed $cache_ids An index based array of geocache ids, or a single geocache id.
|
||||
* The parameter MUST contain only valid, non duplicated geocache ids.
|
||||
*/
|
||||
public static function log_geocache_access(OkapiRequest $request, $cache_ids)
|
||||
{
|
||||
if (Settings::get('OCPL_ENABLE_GEOCACHE_ACCESS_LOGS') !== true)
|
||||
return ;
|
||||
|
||||
if (Settings::get('OC_BRANCH') == 'oc.pl')
|
||||
{
|
||||
// TODO: can we use the _SERVER global here? or should we make them abstract, and
|
||||
// pass along with request object?
|
||||
$remote_addr_escaped = "'" . mysql_real_escape_string($_SERVER['REMOTE_ADDR']) . "'";
|
||||
$user_agent_escaped = isset($_SERVER['HTTP_USER_AGENT']) ?
|
||||
"'" . mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']) . "'" : "null";
|
||||
$forwarded_for_escaped = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
|
||||
"'" . mysql_real_escape_string($_SERVER['HTTP_X_FORWARDED_FOR']) . "'" : "null";
|
||||
|
||||
$consumer_key_escaped = "'" . mysql_real_escape_string($request->consumer->key) . "'";
|
||||
$original_caller_escaped = "'" . mysql_real_escape_string(self::get_original_caller()) . "'";
|
||||
|
||||
$user_id = null;
|
||||
if ($request->token != null)
|
||||
$user_id = $request->token->user_id;
|
||||
$user_id_escaped = $user_id === null ? "null" : "'" . mysql_real_escape_string($user_id) . "'";
|
||||
if (is_array($cache_ids)){
|
||||
if (count($cache_ids) == 1)
|
||||
$cache_ids_where = "= '" . mysql_real_escape_string($cache_ids[0]) . "'";
|
||||
else
|
||||
$cache_ids_where = "in ('" . implode("','", array_map('mysql_real_escape_string', $cache_ids)) . "')";
|
||||
} else {
|
||||
$cache_ids_where = "= '" . mysql_real_escape_string($cache_ids) . "'";
|
||||
}
|
||||
|
||||
$sql = "
|
||||
select cache_id
|
||||
from CACHE_ACCESS_LOGS cal
|
||||
where
|
||||
cache_id $cache_ids_where" .
|
||||
($user_id === null ? " and cal.user_id is null" : " and cal.user_id = $user_id_escaped") . "
|
||||
and cal.source = 'O'
|
||||
and cal.event = $original_caller_escaped
|
||||
and cal.okapi_consumer_key = $consumer_key_escaped
|
||||
and date_sub(now(), interval 1 hour) < cal.event_date ";
|
||||
if ($user_id === null) {
|
||||
$sql .= " and cal.ip_addr = $remote_addr_escaped ";
|
||||
$sql .= isset($_SERVER['HTTP_USER_AGENT']) ? " and cal.user_agent = $user_agent_escaped " : " and cal.user_agent is null ";
|
||||
}
|
||||
$already_logged_cache_ids = Db::select_column($sql);
|
||||
unset($cache_ids_where);
|
||||
unset($sql);
|
||||
|
||||
// check, if all the geocaches has already been logged
|
||||
if (is_array($cache_ids) && count($already_logged_cache_ids) == count($cache_ids)
|
||||
|| !is_array($cache_ids) && count($already_logged_cache_ids) == 1)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
if (is_array($cache_ids)){
|
||||
$tmp = array();
|
||||
foreach ($cache_ids as $cache_id)
|
||||
$tmp[$cache_id] = true;
|
||||
foreach ($already_logged_cache_ids as $cache_id)
|
||||
unset($tmp[$cache_id]);
|
||||
if (count($tmp) <= 0)
|
||||
return ;
|
||||
$cache_ids_filterd = array_keys($tmp);
|
||||
unset($tmp);
|
||||
} else {
|
||||
$cache_ids_filterd = $cache_ids;
|
||||
}
|
||||
|
||||
if (is_array($cache_ids_filterd)){
|
||||
if (count($cache_ids_filterd) == 1)
|
||||
$cache_ids_where = "= '" . mysql_real_escape_string($cache_ids_filterd[0]) . "'";
|
||||
else
|
||||
$cache_ids_where = "in ('" . implode("','", array_map('mysql_real_escape_string', $cache_ids_filterd)) . "')";
|
||||
} else {
|
||||
$cache_ids_where = "= '" . mysql_real_escape_string($cache_ids_filterd) . "'";
|
||||
}
|
||||
|
||||
Db::execute("
|
||||
insert into CACHE_ACCESS_LOGS (event_date, cache_id, user_id, source, event, ip_addr,
|
||||
user_agent, forwarded_for, okapi_consumer_key)
|
||||
select
|
||||
now(), cache_id, $user_id_escaped, 'O',
|
||||
$original_caller_escaped, $remote_addr_escaped, $user_agent_escaped, $forwarded_for_escaped,
|
||||
$consumer_key_escaped
|
||||
from caches
|
||||
where cache_id $cache_ids_where
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
1009
htdocs/okapi/lib/tbszip.php
Normal file
1009
htdocs/okapi/lib/tbszip.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: OKAPI\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-23 16:05+0100\n"
|
||||
"PO-Revision-Date: 2014-08-09 03:47+0100\n"
|
||||
"PO-Revision-Date: 2014-11-01 00:36+0100\n"
|
||||
"Last-Translator: Harrie Klomp <harrie@harrieklomp.be>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: nl_NL\n"
|
||||
@ -114,8 +114,8 @@ msgstr[1] "%d keren gevonden"
|
||||
#, php-format
|
||||
msgid "%d trackable"
|
||||
msgid_plural "%d trackables"
|
||||
msgstr[0] "%d trackable"
|
||||
msgstr[1] "%d trackables"
|
||||
msgstr[0] "%d GeoKret (of TravelBug)"
|
||||
msgstr[1] "%d GeoKrets (of TravelBugs)"
|
||||
|
||||
#: okapi/services/caches/formatters/gpxfile.tpl.php:72
|
||||
msgid "Personal notes"
|
||||
|
@ -41,6 +41,7 @@ class OkapiServiceRunner
|
||||
'services/caches/save_personal_notes',
|
||||
'services/caches/formatters/gpx',
|
||||
'services/caches/formatters/garmin',
|
||||
'services/caches/formatters/ggz',
|
||||
'services/caches/map/tile',
|
||||
'services/logs/entries',
|
||||
'services/logs/entry',
|
||||
|
@ -185,14 +185,14 @@ class WebService
|
||||
if (($include_list === null) && (count($exclude_list) == 0))
|
||||
{
|
||||
$arg['description'] = "<i>Inherited from <a href='".$referenced_method_info['ref_url'].
|
||||
"'>".$referenced_method_info['name']."</a> method.</i>";
|
||||
"#arg_". $arg['name'] . "'>".$referenced_method_info['name']."</a> method.</i>";
|
||||
}
|
||||
elseif (
|
||||
(($include_list === null) || in_array($arg['name'], $include_list))
|
||||
&& (!in_array($arg['name'], $exclude_list))
|
||||
) {
|
||||
$arg['description'] = "<i>Same as in the <a href='".$referenced_method_info['ref_url'].
|
||||
"'>".$referenced_method_info['name']."</a> method.</i>";
|
||||
"#arg_". $arg['name'] . "'>".$referenced_method_info['name']."</a> method.</i>";
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace okapi\services\caches\formatters\garmin;
|
||||
|
||||
|
||||
use okapi\Okapi;
|
||||
use okapi\Cache;
|
||||
use okapi\Settings;
|
||||
@ -9,20 +10,18 @@ use okapi\OkapiRequest;
|
||||
use okapi\OkapiHttpResponse;
|
||||
use okapi\OkapiInternalRequest;
|
||||
use okapi\OkapiServiceRunner;
|
||||
use okapi\OkapiZIPHttpResponse;
|
||||
use okapi\BadRequest;
|
||||
use okapi\ParamMissing;
|
||||
use okapi\InvalidParam;
|
||||
use okapi\OkapiAccessToken;
|
||||
use okapi\services\caches\search\SearchAssistant;
|
||||
|
||||
use \ZipArchive;
|
||||
use \Exception;
|
||||
use \clsTbsZip;
|
||||
|
||||
class WebService
|
||||
{
|
||||
private static $shutdown_function_registered = false;
|
||||
private static $files_to_unlink = array();
|
||||
|
||||
public static function options()
|
||||
{
|
||||
return array(
|
||||
@ -44,29 +43,37 @@ class WebService
|
||||
if (!$images) $images = "all";
|
||||
if (!in_array($images, array("none", "all", "spoilers", "nonspoilers")))
|
||||
throw new InvalidParam('images');
|
||||
$format = $request->get_parameter('caches_format');
|
||||
if (!$format) $format = "gpx";
|
||||
if (!in_array($format, array("gpx", "ggz")))
|
||||
throw new InvalidParam('format');
|
||||
|
||||
$location_source = $request->get_parameter('location_source');
|
||||
$location_change_prefix = $request->get_parameter('location_change_prefix');
|
||||
|
||||
# Start creating ZIP archive.
|
||||
$response = new OkapiZIPHttpResponse();
|
||||
|
||||
$tempfilename = Okapi::get_var_dir()."/garmin".time().rand(100000,999999).".zip";
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($tempfilename, ZIPARCHIVE::CREATE) !== true)
|
||||
throw new Exception("ZipArchive class could not create temp file $tempfilename. Check permissions!");
|
||||
|
||||
# Create basic structure
|
||||
|
||||
$zip->addEmptyDir("Garmin");
|
||||
$zip->addEmptyDir("Garmin/GPX");
|
||||
$zip->addEmptyDir("Garmin/GeocachePhotos");
|
||||
|
||||
# Include a GPX file compatible with Garmin devices. It should include all
|
||||
# Include a GPX/GGZ file compatible with Garmin devices. It should include all
|
||||
# Geocaching.com (groundspeak:) and Opencaching.com (ox:) extensions. It will
|
||||
# also include image references (actual images will be added as separate files later)
|
||||
# and personal data (if the method was invoked using Level 3 Authentication).
|
||||
|
||||
$zip->addFromString("Garmin/GPX/opencaching".time().rand(100000,999999).".gpx",
|
||||
OkapiServiceRunner::call('services/caches/formatters/gpx', new OkapiInternalRequest(
|
||||
switch($format) {
|
||||
case 'gpx' :
|
||||
$data_filename = "Garmin/GPX/opencaching".time().rand(100000,999999).".gpx";
|
||||
$data_method = 'services/caches/formatters/gpx';
|
||||
$data_use_compression = true;
|
||||
break;
|
||||
case 'ggz' :
|
||||
$data_filename = "Garmin/GGZ/opencaching".time().rand(100000,999999).".ggz";
|
||||
$data_method = 'services/caches/formatters/ggz';
|
||||
$data_use_compression = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$response->zip->FileAdd($data_filename,
|
||||
OkapiServiceRunner::call($data_method, new OkapiInternalRequest(
|
||||
$request->consumer, $request->token, array(
|
||||
'cache_codes' => $cache_codes,
|
||||
'langpref' => $langpref,
|
||||
@ -82,7 +89,7 @@ class WebService
|
||||
'my_notes' => ($request->token != null) ? "desc:text" : "none",
|
||||
'location_source' => $location_source,
|
||||
'location_change_prefix' => $location_change_prefix
|
||||
)))->get_body());
|
||||
)))->get_body(), TBSZIP_STRING, $data_use_compression);
|
||||
|
||||
# Then, include all the images.
|
||||
|
||||
@ -100,11 +107,8 @@ class WebService
|
||||
if (count($imgs) == 0)
|
||||
continue;
|
||||
$dir = "Garmin/GeocachePhotos/".$cache_code[strlen($cache_code) - 1];
|
||||
$zip->addEmptyDir($dir); # fails silently if it already exists
|
||||
$dir .= "/".$cache_code[strlen($cache_code) - 2];
|
||||
$zip->addEmptyDir($dir);
|
||||
$dir .= "/".$cache_code;
|
||||
$zip->addEmptyDir($dir);
|
||||
foreach ($imgs as $no => $img)
|
||||
{
|
||||
if ($images == 'spoilers' && (!$img['is_spoiler']))
|
||||
@ -124,7 +128,6 @@ class WebService
|
||||
continue; # unsupported file extension
|
||||
|
||||
if ($img['is_spoiler']) {
|
||||
$zip->addEmptyDir($dir."/Spoilers");
|
||||
$zippath = $dir."/Spoilers/".$img['unique_caption'].".jpg";
|
||||
} else {
|
||||
$zippath = $dir."/".$img['unique_caption'].".jpg";
|
||||
@ -140,9 +143,7 @@ class WebService
|
||||
$syspath = Settings::get('IMAGES_DIR')."/".$img['uuid'].".jpg";
|
||||
if (file_exists($syspath))
|
||||
{
|
||||
$file = file_get_contents($syspath);
|
||||
if ($file)
|
||||
$zip->addFromString($zippath, $file);
|
||||
$response->zip->FileAdd($zippath, $syspath, TBSZIP_FILE, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -180,42 +181,21 @@ class WebService
|
||||
}
|
||||
}
|
||||
if ($jpeg_contents) # This can be "null" *or* "false"!
|
||||
$zip->addFromString($zippath, $jpeg_contents);
|
||||
$response->zip->FileAdd($zippath, $jpeg_contents, TBSZIP_STRING, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
# The result could be big. Bigger than our memory limit. We will
|
||||
# return an open file stream instead of a string. We also should
|
||||
# set a higher time limit, because downloading this response may
|
||||
# take some time over slow network connections (and I'm not sure
|
||||
# The result could be big, but it's created and streamed right
|
||||
# to the browser, so it shouldn't hit our memory limit. We also
|
||||
# should set a higher time limit, because downloading this response
|
||||
# may take some time over slow network connections (and I'm not sure
|
||||
# what is the PHP's default way of handling such scenario).
|
||||
|
||||
set_time_limit(600);
|
||||
$response = new OkapiHttpResponse();
|
||||
$response->content_type = "application/zip";
|
||||
$response->content_disposition = 'attachment; filename="results.zip"';
|
||||
$response->stream_length = filesize($tempfilename);
|
||||
$response->body = fopen($tempfilename, "rb");
|
||||
$response->allow_gzip = false;
|
||||
self::add_file_to_unlink($tempfilename);
|
||||
return $response;
|
||||
}
|
||||
|
||||
private static function add_file_to_unlink($filename)
|
||||
{
|
||||
if (!self::$shutdown_function_registered)
|
||||
register_shutdown_function(array("okapi\\services\\caches\\formatters\\garmin\\WebService", "unlink_temporary_files"));
|
||||
self::$files_to_unlink[] = $filename;
|
||||
}
|
||||
|
||||
public static function unlink_temporary_files()
|
||||
{
|
||||
foreach (self::$files_to_unlink as $filename)
|
||||
@unlink($filename);
|
||||
self::$files_to_unlink = array();
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
this method to level 2 or 3. You should be prepared for that. Use the highest authentication
|
||||
level you've got. Also, if you want the GPX file to include <b>personal data (like user's notes)</b>,
|
||||
you have to use Level 3 Authentication anyway.</p>
|
||||
<p><b>Note:</b> All non-JPEG images will be skipped. Currently OKAPI does not convert
|
||||
other types of images to JPEG.</p>
|
||||
</desc>
|
||||
<req name='cache_codes'>
|
||||
<p>Pipe-separated list of cache codes which you are interested in.
|
||||
@ -40,6 +38,25 @@
|
||||
<li><b>nonspoilers</b> - only non-spoiler images will be included in the result.</li>
|
||||
</ul>
|
||||
</opt>
|
||||
<opt name='caches_format' default='gpx'>
|
||||
<p><b>Important note:</b> This is a <b>BETA</b> parameter. It may change
|
||||
in a backward-incompatible manner, or it may even be removed.</p>
|
||||
|
||||
<p>This parameter allows you to change the format of the geocache files
|
||||
embedded within the returned archive. One of the following values:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>gpx</b> - (default) use GPX files. The safest choice - these
|
||||
will work with all Garmin devices, but you might want to switch to
|
||||
the GGZ format to get a better performance.</li>
|
||||
|
||||
<li><b>ggz</b> - if set, GGZ files will be used instead of GPX files.
|
||||
Fewer devices support GGZ format, but if your device does, then using
|
||||
them can give you a much better performance. See
|
||||
<a href="%OKAPI:methodref:services/caches/formatters/ggz%">services/caches/formatters/ggz</a>
|
||||
method for more information on the GGZ format.</li>
|
||||
</ul>
|
||||
</opt>
|
||||
<opt name="location_source" default='default-coords'>
|
||||
Same as in the <a href="%OKAPI:methodargref:services/caches/formatters/gpx#location_source%">
|
||||
services/caches/formatters/gpx</a> method.
|
||||
|
70
htdocs/okapi/services/caches/formatters/ggz.php
Normal file
70
htdocs/okapi/services/caches/formatters/ggz.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace okapi\services\caches\formatters\ggz;
|
||||
|
||||
use okapi\Okapi;
|
||||
use okapi\Cache;
|
||||
use okapi\Settings;
|
||||
use okapi\OkapiRequest;
|
||||
use okapi\OkapiHttpResponse;
|
||||
use okapi\OkapiInternalRequest;
|
||||
use okapi\OkapiServiceRunner;
|
||||
use okapi\BadRequest;
|
||||
use okapi\ParamMissing;
|
||||
use okapi\InvalidParam;
|
||||
use okapi\OkapiAccessToken;
|
||||
use okapi\OkapiZIPHttpResponse;
|
||||
use okapi\services\caches\search\SearchAssistant;
|
||||
|
||||
use \ZipArchive;
|
||||
use \Exception;
|
||||
|
||||
require_once($GLOBALS['rootpath']."okapi/services/caches/formatters/gpx.php");
|
||||
|
||||
class WebService
|
||||
{
|
||||
public static function options()
|
||||
{
|
||||
return array(
|
||||
'min_auth_level' => 1
|
||||
);
|
||||
}
|
||||
|
||||
public static function call(OkapiRequest $request)
|
||||
{
|
||||
$gpx_result = \okapi\services\caches\formatters\gpx\WebService::create_gpx(
|
||||
$request,
|
||||
\okapi\services\caches\formatters\gpx\WebService::FLAG_CREATE_GGZ_IDX
|
||||
);
|
||||
|
||||
$response = new OkapiZIPHttpResponse();
|
||||
|
||||
# Include a GPX file compatible with Garmin devices. It should include all
|
||||
# Geocaching.com (groundspeak:) and Opencaching.com (ox:) extensions. It will
|
||||
# also include personal data (if the method was invoked using Level 3 Authentication).
|
||||
|
||||
$file_item_name = "data_".time()."_".rand(100000,999999).".gpx";
|
||||
$ggz_file = array(
|
||||
'name' => $file_item_name,
|
||||
'crc32' => sprintf('%08X', crc32($gpx_result['gpx'])),
|
||||
'caches' => $gpx_result['ggz_entries']
|
||||
);
|
||||
|
||||
$vars = array();
|
||||
$vars['files'] = array($ggz_file);
|
||||
|
||||
ob_start();
|
||||
include 'ggzindex.tpl.php';
|
||||
$index_content = ob_get_clean();
|
||||
|
||||
$response->zip->FileAdd("index/com/garmin/geocaches/v0/index.xml", $index_content);
|
||||
$response->zip->FileAdd("data/".$file_item_name, $gpx_result['gpx']);
|
||||
|
||||
unset($gpx_result);
|
||||
unset($index_content);
|
||||
|
||||
$response->content_type = "application/x-ggz";
|
||||
$response->content_disposition = 'attachment; filename="geocaches.ggz"';
|
||||
return $response;
|
||||
}
|
||||
}
|
21
htdocs/okapi/services/caches/formatters/ggz.xml
Normal file
21
htdocs/okapi/services/caches/formatters/ggz.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<xml>
|
||||
<brief>Retrieve GGZ file for newer Garmin devices</brief>
|
||||
<issue-id>323</issue-id>
|
||||
<desc>
|
||||
<p><b>Important note:</b> This is a <b>BETA</b> method. It's interface may change
|
||||
in a backward-incompatible manner, or it may even be removed.</p>
|
||||
|
||||
<p>Generate a Garmin GGZ file - an alternative to GPX files, compatible with newer
|
||||
Geocaching-enabled Garmin GPS devices. Internally, GGZ file is a ZIP file - it includes
|
||||
the GPX file, plus an index file which allows the device to access specific geocaches
|
||||
much faster.</p>
|
||||
|
||||
<p>Currently, this method takes the same parameters as the
|
||||
<a href="%OKAPI:methodref:services/caches/formatters/gpx%">services/caches/formatters/gpx</a>
|
||||
method. Only the output format is different.</p>
|
||||
</desc>
|
||||
<import-params method='services/caches/formatters/gpx'/>
|
||||
<returns>
|
||||
<p>A GGZ file. You should copy the file to Garmin's internal memory storage.</p>
|
||||
</returns>
|
||||
</xml>
|
43
htdocs/okapi/services/caches/formatters/ggzindex.tpl.php
Normal file
43
htdocs/okapi/services/caches/formatters/ggzindex.tpl.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?
|
||||
|
||||
namespace okapi\services\caches\formatters\ggz;
|
||||
|
||||
use okapi\Okapi;
|
||||
|
||||
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
||||
|
||||
?>
|
||||
<ggz xmlns="http://www.opencaching.com/xmlschemas/ggz/1/0">
|
||||
<time><?= date('c') ?></time>
|
||||
<? foreach ($vars['files'] as $f) { ?>
|
||||
<file>
|
||||
<name><?= $f['name'] ?></name>
|
||||
<crc><?= $f['crc32'] ?></crc>
|
||||
<time><?= date('c') ?></time>
|
||||
<?
|
||||
foreach ($f['caches'] as $c) {
|
||||
?><gch>
|
||||
<code><?= $c['code'] ?></code>
|
||||
<name><?= Okapi::xmlescape($c['name']) ?></name>
|
||||
<type><?= Okapi::xmlescape($c['type']) ?></type>
|
||||
<lat><?= $c['lat'] ?></lat>
|
||||
<lon><?= $c['lon'] ?></lon>
|
||||
<file_pos><?= $c['file_pos'] ?></file_pos>
|
||||
<file_len><?= $c['file_len'] ?></file_len>
|
||||
<? if (isset($c['ratings'])) {
|
||||
?><ratings>
|
||||
<?
|
||||
foreach ($c['ratings'] as $rating_key => $rating_val){
|
||||
echo "<$rating_key>$rating_val</$rating_key>\n";
|
||||
}
|
||||
?>
|
||||
</ratings><?
|
||||
}
|
||||
if (isset($c['found']) && $c['found']) { ?>
|
||||
<found>true</found>
|
||||
<? } ?>
|
||||
</gch>
|
||||
<? } ?>
|
||||
</file>
|
||||
<? } ?>
|
||||
</ggz>
|
@ -54,7 +54,34 @@ class WebService
|
||||
'other' => 'Other',
|
||||
);
|
||||
|
||||
/**
|
||||
* When used in create_gpx() method, enables GGZ index generation.
|
||||
* The index is then returned along the method's response, in the
|
||||
* 'ggz_entries' key. See formatters/ggz method.
|
||||
*/
|
||||
const FLAG_CREATE_GGZ_IDX = 1;
|
||||
|
||||
public static function call(OkapiRequest $request)
|
||||
{
|
||||
$response = new OkapiHttpResponse();
|
||||
$response->content_type = "application/gpx; charset=utf-8";
|
||||
$response->content_disposition = 'attachment; filename="results.gpx"';
|
||||
|
||||
$result_ref = self::create_gpx($request);
|
||||
$response->body = &$result_ref['gpx'];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a GPX file.
|
||||
*
|
||||
* @param OkapiRequest $request
|
||||
* @param integer $flags
|
||||
* @throws BadRequest
|
||||
* @return An array with GPX file content under 'gpx' key
|
||||
*/
|
||||
public static function create_gpx(OkapiRequest $request, $flags = null)
|
||||
{
|
||||
$vars = array();
|
||||
|
||||
@ -388,14 +415,85 @@ class WebService
|
||||
}
|
||||
}
|
||||
|
||||
$response = new OkapiHttpResponse();
|
||||
$response->content_type = "application/gpx; charset=utf-8";
|
||||
$response->content_disposition = 'attachment; filename="results.gpx"';
|
||||
# Do we need a GGZ index?
|
||||
|
||||
if ($flags & self::FLAG_CREATE_GGZ_IDX) {
|
||||
|
||||
# GGZ index consist of entries - one per each waypoint in the GPX file.
|
||||
# We will keep a list of all such entries here.
|
||||
|
||||
$ggz_entries = array();
|
||||
|
||||
foreach ($vars['caches'] as &$cache_ref)
|
||||
{
|
||||
# Every $cache_ref will also be holding a reference to its entry.
|
||||
# Note, that more attributes are added while processing gpsfile.tpl.php!
|
||||
|
||||
if (!isset($cache_ref['ggz_entry'])) {
|
||||
$cache_ref['ggz_entry'] = array();
|
||||
}
|
||||
$ggz_entry = &$cache_ref['ggz_entry'];
|
||||
$ggz_entries[] = &$ggz_entry;
|
||||
|
||||
$ggz_entry['code'] = $cache_ref['code'];
|
||||
$ggz_entry['name'] = isset($cache_ref['name_2']) ? $cache_ref['name_2'] : $cache_ref['name'];
|
||||
$ggz_entry['type'] = $vars['cache_GPX_types'][$cache_ref['type']];
|
||||
list($lat, $lon) = explode("|", $cache_ref['location']);
|
||||
$ggz_entry['lat'] = $lat;
|
||||
$ggz_entry['lon'] = $lon;
|
||||
|
||||
$ggz_entry['ratings'] = array();
|
||||
$ratings_ref = &$ggz_entry['ratings'];
|
||||
if (isset($cache_ref['rating'])){
|
||||
$ratings_ref['awesomeness'] = $cache_ref['rating'];
|
||||
}
|
||||
$ratings_ref['difficulty'] = $cache_ref['difficulty'];
|
||||
if (!isset($cache_ref['size'])) {
|
||||
$ratings_ref['size'] = 0; // Virtual, Event
|
||||
} else if ($cache_ref['oxsize'] !== null) { // is this ox size one-to-one?
|
||||
$ratings_ref['size'] = $cache_ref['oxsize'];
|
||||
}
|
||||
$ratings_ref['terrain'] = $cache_ref['terrain'];
|
||||
|
||||
if ($vars['mark_found'] && $cache_ref['is_found']) {
|
||||
$ggz_entry['found'] = true;
|
||||
}
|
||||
|
||||
# Additional waypoints. Currently, we're not 100% sure if their entries should
|
||||
# be included in the GGZ file (the format is undocumented).
|
||||
|
||||
if (isset($cache_ref['alt_wpts'])) {
|
||||
$idx = 1;
|
||||
foreach ($cache_ref['alt_wpts'] as &$alt_wpt_ref) {
|
||||
if (!isset($alt_wpt_ref['ggz_entry'])) {
|
||||
$alt_wpt_ref['ggz_entry'] = array();
|
||||
}
|
||||
$ggz_entry = &$alt_wpt_ref['ggz_entry'];
|
||||
$ggz_entries[] = &$ggz_entry;
|
||||
|
||||
$ggz_entry['code'] = $cache_ref['code'] . '-' . $idx;
|
||||
$ggz_entry['name'] = $alt_wpt_ref['type_name'];
|
||||
$ggz_entry['type'] = $alt_wpt_ref['sym'];
|
||||
list($lat, $lon) = explode("|", $alt_wpt_ref['location']);
|
||||
$ggz_entry['lat'] = $lat;
|
||||
$ggz_entry['lon'] = $lon;
|
||||
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
$result = array('gpx' => ob_get_clean());
|
||||
if ($flags & self::FLAG_CREATE_GGZ_IDX) {
|
||||
$result['ggz_entries'] = $ggz_entries;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@
|
||||
<opt name="location_change_prefix" default="#">
|
||||
<p>Prefix to be added to the geocache name, in case its location has been changed due to
|
||||
the <b><a href="%OKAPI:methodargref:#location_source%">location_source</a></b> parameter
|
||||
matching any of the alternate waypoint.</p>
|
||||
matching any of the alternate waypoints.</p>
|
||||
</opt>
|
||||
<returns>
|
||||
<p>GPX file. All invalid cache codes will be skipped without any notice!</p>
|
||||
|
@ -22,8 +22,16 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
|
||||
<url><?= $vars['installation']['site_url'] ?></url>
|
||||
<urlname><?= $vars['installation']['site_name'] ?></urlname>
|
||||
<time><?= date('c') ?></time>
|
||||
<? foreach ($vars['caches'] as $c) { ?>
|
||||
<? list($lat, $lon) = explode("|", $c['location']); ?>
|
||||
<? foreach ($vars['caches'] as &$cache_ref) { ?>
|
||||
<?
|
||||
if (isset($cache_ref['ggz_entry'])) {
|
||||
/* The base parts of the GGZ index are the offset and length of the entry
|
||||
* in the GPX file. This needs to be calculated here. */
|
||||
$cache_ref['ggz_entry']['file_pos'] = ob_get_length();
|
||||
}
|
||||
$c = $cache_ref;
|
||||
list($lat, $lon) = explode("|", $c['location']);
|
||||
?>
|
||||
<wpt lat="<?= $lat ?>" lon="<?= $lon ?>">
|
||||
<time><?= $c['date_created'] ?></time>
|
||||
<name><?= $c['code'] ?></name>
|
||||
@ -154,7 +162,6 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
|
||||
<? } ?>
|
||||
</groundspeak:logs>
|
||||
<? } ?>
|
||||
<? /* groundspeak:travelbugs - does it actually DO anything? WRTODO */ ?>
|
||||
</groundspeak:cache>
|
||||
<? } ?>
|
||||
<? if ($vars['ns_ox']) { /* Does user want us to include Garmin's <opencaching> element? */ ?>
|
||||
@ -183,9 +190,20 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
|
||||
</ox:opencaching>
|
||||
<? } ?>
|
||||
</wpt>
|
||||
<?
|
||||
if (isset($cache_ref['ggz_entry'])) {
|
||||
$cache_ref['ggz_entry']['file_len'] = ob_get_length() - $cache_ref['ggz_entry']['file_pos'];
|
||||
}
|
||||
?>
|
||||
<? if ($vars['alt_wpts']) { ?>
|
||||
<? foreach ($c['alt_wpts'] as $wpt) { ?>
|
||||
<? list($lat, $lon) = explode("|", $wpt['location']); ?>
|
||||
<? foreach ($cache_ref['alt_wpts'] as &$wpt_ref) { ?>
|
||||
<?
|
||||
if (isset($wpt_ref['ggz_entry'])) {
|
||||
$wpt_ref['ggz_entry']['file_pos'] = ob_get_length();
|
||||
}
|
||||
$wpt = $wpt_ref;
|
||||
list($lat, $lon) = explode("|", $wpt['location']);
|
||||
?>
|
||||
<wpt lat="<?= $lat ?>" lon="<?= $lon ?>">
|
||||
<time><?= $c['date_created'] ?></time>
|
||||
<name><?= Okapi::xmlescape($wpt['name']) ?></name>
|
||||
@ -201,6 +219,11 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
|
||||
</gsak:wptExtension>
|
||||
<? } ?>
|
||||
</wpt>
|
||||
<?
|
||||
if (isset($wpt_ref['ggz_entry'])){
|
||||
$wpt_ref['ggz_entry']['file_len'] = ob_get_length() - $wpt_ref['ggz_entry']['file_pos'];
|
||||
}
|
||||
?>
|
||||
<? } ?>
|
||||
<? } ?>
|
||||
<? } ?>
|
||||
|
@ -560,7 +560,7 @@ class WebService
|
||||
$preview_field = "mappreview";
|
||||
else
|
||||
$preview_field = "0";
|
||||
$rs = Db::query("
|
||||
$sql = "
|
||||
select object_id, uuid, url, title, spoiler, ".$preview_field." as preview
|
||||
from pictures
|
||||
where
|
||||
@ -568,8 +568,15 @@ class WebService
|
||||
and display = 1
|
||||
and object_type = 2
|
||||
and unknown_format = 0
|
||||
order by object_id, date_created
|
||||
");
|
||||
";
|
||||
if (Settings::get('OC_BRANCH') == 'oc.pl'){
|
||||
// oc.pl installation allows arbitrary order of the geocache's images
|
||||
$sql .= "order by object_id, seq, date_created";
|
||||
} else {
|
||||
$sql .= "order by object_id, date_created";
|
||||
}
|
||||
$rs = Db::query($sql);
|
||||
unset($sql);
|
||||
$prev_cache_code = null;
|
||||
while ($row = mysql_fetch_assoc($rs))
|
||||
{
|
||||
|
226
htdocs/okapi/services/caches/save_personal_notes.php
Normal file
226
htdocs/okapi/services/caches/save_personal_notes.php
Normal file
@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace okapi\services\caches\save_personal_notes;
|
||||
|
||||
use Exception;
|
||||
use okapi\Okapi;
|
||||
use okapi\Db;
|
||||
use okapi\OkapiRequest;
|
||||
use okapi\ParamMissing;
|
||||
use okapi\InvalidParam;
|
||||
use okapi\BadRequest;
|
||||
use okapi\OkapiInternalRequest;
|
||||
use okapi\OkapiServiceRunner;
|
||||
use okapi\OkapiAccessToken;
|
||||
use okapi\Settings;
|
||||
|
||||
|
||||
class WebService
|
||||
{
|
||||
public static function options()
|
||||
{
|
||||
return array(
|
||||
'min_auth_level' => 3
|
||||
);
|
||||
}
|
||||
|
||||
public static function call(OkapiRequest $request)
|
||||
{
|
||||
# Get current notes, and verify cache_code
|
||||
|
||||
$cache_code = $request->get_parameter('cache_code');
|
||||
if ($cache_code == null)
|
||||
throw new ParamMissing('cache_code');
|
||||
$geocache = OkapiServiceRunner::call(
|
||||
'services/caches/geocache',
|
||||
new OkapiInternalRequest($request->consumer, $request->token, array(
|
||||
'cache_code' => $cache_code,
|
||||
'fields' => 'my_notes|internal_id'
|
||||
))
|
||||
);
|
||||
$current_value = $geocache['my_notes'];
|
||||
if ($current_value == null) {
|
||||
$current_value = "";
|
||||
}
|
||||
$cache_id = $geocache['internal_id'];
|
||||
|
||||
# old_value
|
||||
|
||||
$old_value = $request->get_parameter('old_value');
|
||||
if ($old_value === null)
|
||||
$old_value = '';
|
||||
|
||||
# new_value (force "no HTML" policy).
|
||||
|
||||
$new_value = $request->get_parameter('new_value');
|
||||
if ($new_value === null)
|
||||
throw new ParamMissing('new_value');
|
||||
|
||||
# Force "no HTML" policy.
|
||||
|
||||
$new_value = strip_tags($new_value);
|
||||
|
||||
# Placeholders for returned values.
|
||||
|
||||
$ret_saved_value = null;
|
||||
$ret_replaced = false;
|
||||
|
||||
if (
|
||||
trim($current_value) == "" ||
|
||||
self::str_equals($old_value, $current_value)
|
||||
) {
|
||||
/* REPLACE mode */
|
||||
|
||||
$ret_replaced = true;
|
||||
if (trim($new_value) == "") {
|
||||
/* empty new value means delete */
|
||||
self::remove_notes($cache_id, $request->token->user_id);
|
||||
$ret_saved_value = null;
|
||||
} else {
|
||||
self::update_notes($cache_id, $request->token->user_id, $new_value);
|
||||
$ret_saved_value = $new_value;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* APPEND mode */
|
||||
|
||||
$ret_saved_value = trim($current_value)."\n\n".trim($new_value);
|
||||
self::update_notes($cache_id, $request->token->user_id, $ret_saved_value);
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'saved_value' => $ret_saved_value,
|
||||
'replaced' => $ret_replaced
|
||||
);
|
||||
return Okapi::formatted_response($request, $result);
|
||||
}
|
||||
|
||||
private static function str_equals($str1, $str2)
|
||||
{
|
||||
if ($str1 == null)
|
||||
$str1 = '';
|
||||
if ($str2 == null)
|
||||
$str2 = '';
|
||||
$str1 = mb_ereg_replace("[ \t\n\r\x0B]+", '', $str1);
|
||||
$str2 = mb_ereg_replace("[ \t\n\r\x0B]+", '', $str2);
|
||||
|
||||
return $str1 == $str2;
|
||||
}
|
||||
|
||||
private static function update_notes($cache_id, $user_id, $new_notes)
|
||||
{
|
||||
if (Settings::get('OC_BRANCH') == 'oc.de')
|
||||
{
|
||||
/* See:
|
||||
*
|
||||
* - https://github.com/OpencachingDeutschland/oc-server3/tree/master/htdocs/libse/CacheNote
|
||||
* - http://www.opencaching.de/okapi/devel/dbstruct
|
||||
*/
|
||||
|
||||
$rs = Db::query("
|
||||
select max(id) as id
|
||||
from coordinates
|
||||
where
|
||||
type = 2 -- personal note
|
||||
and cache_id = '".mysql_real_escape_string($cache_id)."'
|
||||
and user_id = '".mysql_real_escape_string($user_id)."'
|
||||
");
|
||||
$id = null;
|
||||
if($row = mysql_fetch_assoc($rs)) {
|
||||
$id = $row['id'];
|
||||
}
|
||||
if ($id == null) {
|
||||
Db::query("
|
||||
insert into coordinates (
|
||||
type, latitude, longitude, cache_id, user_id,
|
||||
description
|
||||
) values (
|
||||
2, 0, 0,
|
||||
'".mysql_real_escape_string($cache_id)."',
|
||||
'".mysql_real_escape_string($user_id)."',
|
||||
'".mysql_real_escape_string($new_notes)."'
|
||||
)
|
||||
");
|
||||
} else {
|
||||
Db::query("
|
||||
update coordinates
|
||||
set description = '".mysql_real_escape_string($new_notes)."'
|
||||
where
|
||||
id = '".mysql_real_escape_string($id)."'
|
||||
and type = 2
|
||||
");
|
||||
}
|
||||
}
|
||||
else # oc.pl branch
|
||||
{
|
||||
$rs = Db::query("
|
||||
select max(note_id) as id
|
||||
from cache_notes
|
||||
where
|
||||
cache_id = '".mysql_real_escape_string($cache_id)."'
|
||||
and user_id = '".mysql_real_escape_string($user_id)."'
|
||||
");
|
||||
$id = null;
|
||||
if($row = mysql_fetch_assoc($rs)) {
|
||||
$id = $row['id'];
|
||||
}
|
||||
if ($id == null) {
|
||||
Db::query("
|
||||
insert into cache_notes (
|
||||
cache_id, user_id, date, desc_html, `desc`
|
||||
) values (
|
||||
'".mysql_real_escape_string($cache_id)."',
|
||||
'".mysql_real_escape_string($user_id)."',
|
||||
NOW(), 0,
|
||||
'".mysql_real_escape_string($new_notes)."'
|
||||
)
|
||||
");
|
||||
} else {
|
||||
Db::query("
|
||||
update cache_notes
|
||||
set
|
||||
`desc` = '".mysql_real_escape_string($new_notes)."',
|
||||
desc_html = 0,
|
||||
date = NOW()
|
||||
where note_id = '".mysql_real_escape_string($id)."'
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function remove_notes($cache_id, $user_id)
|
||||
{
|
||||
if (Settings::get('OC_BRANCH') == 'oc.de') {
|
||||
# we can delete row if and only if there are no coords in it
|
||||
Db::execute("
|
||||
delete from coordinates
|
||||
where
|
||||
type = 2 -- personal note
|
||||
and cache_id = '".mysql_real_escape_string($cache_id)."'
|
||||
and user_id = '".mysql_real_escape_string($user_id)."'
|
||||
and longitude = 0
|
||||
and latitude = 0
|
||||
");
|
||||
if (Db::get_affected_row_count() <= 0){
|
||||
# no rows deleted - record either doesn't exist, or has coords
|
||||
# remove only description
|
||||
Db::execute("
|
||||
update coordinates
|
||||
set description = null
|
||||
where
|
||||
type = 2
|
||||
and cache_id = '".mysql_real_escape_string($cache_id)."'
|
||||
and user_id = '".mysql_real_escape_string($user_id)."'
|
||||
");
|
||||
}
|
||||
} else { # oc.pl branch
|
||||
Db::execute("
|
||||
delete from cache_notes
|
||||
where
|
||||
cache_id = '".mysql_real_escape_string($cache_id)."'
|
||||
and user_id = '".mysql_real_escape_string($user_id)."'
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
51
htdocs/okapi/services/caches/save_personal_notes.xml
Normal file
51
htdocs/okapi/services/caches/save_personal_notes.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<xml>
|
||||
<brief>Update personal user notes of a geocache</brief>
|
||||
<issue-id>302</issue-id>
|
||||
<desc>
|
||||
<p>This method allows your users to to change the content of the user's
|
||||
personal note from one value to the other value.</p>
|
||||
|
||||
<p>We <b>want</b> you to know the pervious value of the personal note,
|
||||
because we feel that your user needs to know what it is exactly that is
|
||||
going to be replaced.</p>
|
||||
|
||||
<p>Please note, that we <b>do not require</b> you to know - the
|
||||
<b>old_value</b> parameter is optional. However, if you don't provide
|
||||
it (or provide an obsolete value), then your <b>new_value</b> will be
|
||||
appended to the current personal note instead of replacing it.</p>
|
||||
|
||||
<p>Current personal user notes for the geocache can be retrieved
|
||||
using the <b>my_notes</b> field in the
|
||||
<a href="%OKAPI:methodargref:services/caches/geocache#fields%">services/caches/geocache</a>
|
||||
method.</p>
|
||||
</desc>
|
||||
<req name='cache_code'>
|
||||
<p>Code of the geocache.</p>
|
||||
</req>
|
||||
<req name='new_value'>
|
||||
<p>The new content of the note, this also can be an empty string (in
|
||||
this case you indicate that you want the note to be removed).</p>
|
||||
</req>
|
||||
<opt name='old_value' default="(empty string)">
|
||||
<p>The previous content of the note. (The content that you <b>think</b>
|
||||
that you are about to change.)</p>
|
||||
</opt>
|
||||
<common-format-params/>
|
||||
<returns>
|
||||
<p>A dictionary of the following structure:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>saved_value</b> - the actual value that was saved. Please
|
||||
note, that this may differ from the <b>new_value</b> you have
|
||||
provided. If the note has been removed, <b>null</b> is
|
||||
returned.
|
||||
</li>
|
||||
<li>
|
||||
<b>replaced</b> - boolean, if <b>true</b> then your
|
||||
<b>new_value</b> had replaced the current user notes. If
|
||||
<b>false</b> the the <b>new_value</b> been appended to the
|
||||
current user notes.
|
||||
</li>
|
||||
</ul>
|
||||
</returns>
|
||||
</xml>
|
Loading…
x
Reference in New Issue
Block a user