okapi r651

This commit is contained in:
following
2013-04-01 00:27:55 +02:00
parent cfb1a9dc9d
commit 21de4b6af3
91 changed files with 2235 additions and 1172 deletions

View File

@ -10,7 +10,7 @@ use okapi\views\menu\OkapiMenu;
#
# All HTTP requests within the /okapi/ path are redirected through this
# controller. From here we'll pass them to the right entry point (or
# display an appropriate error message).
# display an appropriate error message).
#
# To learn more about OKAPI, see core.php.
#
@ -26,7 +26,7 @@ if (ob_list_handlers() == array('default output handler'))
# We will assume that this one comes from "output_buffering" being turned on
# in PHP config. This is very common and probably is good for most other OC
# pages. But we don't need it in OKAPI. We will just turn this off.
ob_end_clean();
}
@ -36,32 +36,32 @@ class OkapiScriptEntryPointController
public static function dispatch_request($uri)
{
# Chop off the ?args=... part.
if (strpos($uri, '?') !== false)
$uri = substr($uri, 0, strpos($uri, '?'));
# Chop off everything before "/okapi/". This should work for okay for most "weird"
# server configurations. It will also address a more subtle issue described here:
# http://stackoverflow.com/questions/8040461/request-uri-unexpectedly-contains-fqdn
if (strpos($uri, "/okapi/") !== false)
$uri = substr($uri, strpos($uri, "/okapi/"));
# Make sure we're in the right directory (.htaccess should make sure of that).
if (strpos($uri, "/okapi/") !== 0)
throw new Exception("'$uri' is outside of the /okapi/ path.");
$uri = substr($uri, 7);
# Initializing internals and running pre-request cronjobs (we don't want
# cronjobs to be run before "okapi/update", for example before database
# was installed).
$allow_cronjobs = ($uri != "update");
Okapi::init_internals($allow_cronjobs);
# Checking for allowed patterns...
try
{
foreach (OkapiUrls::$mapping as $pattern => $namespace)
@ -70,7 +70,7 @@ class OkapiScriptEntryPointController
if (preg_match("#$pattern#", $uri, $matches))
{
# Pattern matched! Moving on to the proper View...
array_shift($matches);
require_once($GLOBALS['rootpath']."okapi/views/$namespace.php");
$response = call_user_func_array(array('\\okapi\\views\\'.
@ -85,9 +85,9 @@ class OkapiScriptEntryPointController
{
/* pass */
}
# None of the patterns matched OR method threw the Http404 exception.
require_once($GLOBALS['rootpath']."okapi/views/http404.php");
$response = \okapi\views\http404\View::call();
$response->display();

View File

@ -291,9 +291,11 @@ class ParamMissing extends BadRequest
/** Common type of BadRequest: Parameter has invalid value. */
class InvalidParam extends BadRequest
{
private $paramName;
public $paramName;
/** What was wrong about the param? */
public $whats_wrong_about_it;
protected function provideExtras(&$extras) {
parent::provideExtras($extras);
$extras['reason_stack'][] = 'invalid_parameter';
@ -419,6 +421,23 @@ class Db
}
return $rs;
}
public static function field_exists($table, $field)
{
if (!preg_match("/[a-z0-9_]+/", $table.$field))
return false;
try {
$spec = self::select_all("desc ".$table.";");
} catch (Exception $e) {
/* Table doesn't exist, probably. */
return false;
}
foreach ($spec as &$row_ref) {
if (strtoupper($row_ref['Field']) == strtoupper($field))
return true;
}
return false;
}
}
#
@ -670,7 +689,7 @@ class OkapiHttpResponse
if ($try_gzip && is_string($this->body))
{
header("Content-Encoding: gzip");
$gzipped = gzencode($this->body, 5, true);
$gzipped = gzencode($this->body, 5);
header("Content-Length: ".strlen($gzipped));
print $gzipped;
}
@ -759,7 +778,7 @@ class Okapi
{
public static $data_store;
public static $server;
public static $revision = 556; # This gets replaced in automatically deployed packages
public static $revision = 651; # 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. */
@ -1957,7 +1976,7 @@ class OkapiHttpRequest extends OkapiRequest
if (!Settings::get('DEBUG'))
{
throw new Exception("Attempted to set DEBUG_AS_USERNAME set in ".
throw new Exception("Attempted to use DEBUG_AS_USERNAME in ".
"non-debug environment. Accidental commit?");
}

View File

@ -27,6 +27,7 @@ use okapi\OkapiServiceRunner;
use okapi\OkapiInternalRequest;
use okapi\OkapiInternalConsumer;
use okapi\services\replicate\ReplicateCommon;
use okapi\services\attrs\AttrHelper;
class CronJobController
{
@ -50,6 +51,7 @@ class CronJobController
new FulldumpGeneratorJob(),
new TileTreeUpdater(),
new SearchSetsCleanerJob(),
new AttrsRefresherJob(),
);
foreach ($cache as $cronjob)
if (!in_array($cronjob->get_type(), array('pre-request', 'cron-5')))
@ -57,7 +59,7 @@ class CronJobController
}
return $cache;
}
/**
* Execute all scheduled cronjobs of given type, reschedule, and return
* UNIX timestamp of the nearest scheduled event.
@ -65,7 +67,7 @@ class CronJobController
public static function run_jobs($type)
{
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
# We don't want other cronjobs of the same time to run simultanously.
$lock = OkapiLock::get('cronjobs-'.$type);
$lock->acquire();
@ -99,9 +101,9 @@ class CronJobController
Cache::set("cron_schedule", $schedule, 30*86400);
}
}
# Remove "stale" schedule keys (those which are no longer declared).
$fixed_schedule = array();
foreach (self::get_enabled_cronjobs() as $cronjob)
{
@ -109,9 +111,9 @@ class CronJobController
$fixed_schedule[$name] = $schedule[$name];
}
unset($schedule);
# Return the nearest scheduled event time.
$nearest = time() + 3600;
foreach ($fixed_schedule as $name => $time)
if ($time < $nearest)
@ -120,7 +122,7 @@ class CronJobController
$lock->release();
return $nearest;
}
/**
* Force a specified cronjob to run. Throw an exception if cronjob not found.
* $job_name mast equal one of the names returned by ->get_name() method.
@ -128,7 +130,7 @@ class CronJobController
public static function force_run($job_name)
{
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
foreach (self::get_enabled_cronjobs() as $cronjob)
{
if (($cronjob->get_name() == $job_name) || ($cronjob->get_name() == "okapi\\cronjobs\\".$job_name))
@ -139,7 +141,7 @@ class CronJobController
}
throw new Exception("CronJob $job_name not found.");
}
/**
* Reset the schedule of a specified cronjob. This will force the job to
* run on nearest occasion (but not NOW).
@ -152,10 +154,10 @@ class CronJobController
$thejob = $tmp;
if ($thejob == null)
throw new Exception("Could not reset schedule for job $job_name. $jon_name not found.");
# We have to acquire lock on the schedule. This might take some time if cron-5 jobs are
# currently being run.
$type = $thejob->get_type();
$lock = OkapiLock::get('cronjobs-'.$type);
$lock->acquire();
@ -167,7 +169,7 @@ class CronJobController
unset($schedule[$thejob->get_name()]);
Cache::set("cron_schedule", $schedule, 30*86400);
}
$lock->release();
}
}
@ -176,10 +178,10 @@ abstract class CronJob
{
/** Run the job. */
public abstract function execute();
/** Get unique name for this cronjob. */
public function get_name() { return get_class($this); }
/**
* Get the type of this cronjob. Currently there are two: 'pre-request'
* and 'cron-5'. The first can be executed before every request, the second
@ -189,7 +191,7 @@ abstract class CronJob
* (before 'cron-5' runs).
*/
public abstract function get_type();
/**
* Get the next scheduled run (unix timestamp). You may assume this function
* will be called ONLY directly after the job was run. You may use this to say,
@ -211,12 +213,12 @@ abstract class PrerequestCronJob extends CronJob
*/
public final function get_type() { return 'pre-request'; }
/**
/**
* Return number of seconds - a *minimum* time period that should pass between
* running the job.
*/
public abstract function get_period();
public function get_next_scheduled_run($previously_scheduled_run)
{
return time() + $this->get_period();
@ -233,13 +235,13 @@ abstract class Cron5Job extends CronJob
* Always returns 'cron-5'.
*/
public final function get_type() { return 'cron-5'; }
/**
/**
* Return number of seconds - period of time after which cronjob execution
* should be repeated. This should be dividable be 300 (5 minutes).
*/
public abstract function get_period();
public function get_next_scheduled_run($previously_scheduled_run)
{
$t = time() + $this->get_period();
@ -287,23 +289,23 @@ class CacheCleanupCronJob extends Cron5Job
public function execute()
{
# Delete all expired elements.
Db::execute("
delete from okapi_cache
where expires < now()
");
# Update the "score" stats.
$multiplier = 0.9; # Every hour, all scores are multiplied by this.
$limit = 0.01; # When a score reaches this limit, the entry is deleted.
# Every time the entry is read, its score is incread by 1. If an entry
# is saved, but never read, it will be deleted after log(L,M) hours
# (log(0.01, 0.9) = 43h). If an entry is read 1000000 times and then
# never read anymore, it will be deleted after log(1000000/L, 1/M)
# hours (log(1000000/0.01, 1/0.9) = 174h = 7 days).
Db::execute("
update okapi_cache
set score = score * $multiplier
@ -323,10 +325,10 @@ class CacheCleanupCronJob extends Cron5Job
and c.score is not null
");
Db::execute("truncate okapi_cache_reads");
# Delete elements with the lowest score. Entries which have been set
# but never read will be removed after 36 hours (0.9^36 < 0.02 < 0.9^35).
Db::execute("
delete from okapi_cache
where
@ -334,10 +336,10 @@ class CacheCleanupCronJob extends Cron5Job
and score < $limit
");
Db::query("optimize table okapi_cache");
# FileCache does not have an expiry date. We will delete all files older
# than 24 hours.
$dir = Okapi::get_var_dir();
if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) {
@ -431,13 +433,13 @@ class CheckCronTab2 extends PrerequestCronJob
{
# There was a ping during the last hour. Everything is okay.
# Reset the counter and return.
Cache::set('crontab_check_counter', 5, 86400);
return;
}
# There was no ping. Decrement the counter. When reached zero, alert.
$counter = Cache::get('crontab_check_counter');
if ($counter === null)
$counter = 5;
@ -458,10 +460,10 @@ class CheckCronTab2 extends PrerequestCronJob
"ignore it. Probably you just paused (or switched off) your VM for some time\n".
"(which would be considered an error in production environment)."
);
# Schedule the next admin-nagging. Each subsequent notification will be sent
# with a greater delay.
$since_last = time() - $last_ping;
Cache::set('crontab_check_counter', (int)($since_last / $this->get_period()), 86400);
}
@ -537,13 +539,13 @@ class TileTreeUpdater extends Cron5Job
if (!$response['more'])
break;
} catch (BadRequest $e) {
# Invalid 'since' parameter? May happen whne crontab was
# Invalid 'since' parameter? May happen when crontab was
# not working for more than 10 days. Or, just after OKAPI
# is installed (and this is the first time this cronjob
# if being run).
$mail_admins = ($tiletree_revision > 0);
\okapi\services\caches\map\ReplicateListener::reset($mail_admins);
\okapi\services\caches\map\ReplicateListener::reset();
Okapi::set_var('clog_followup_revision', $current_clog_revision);
break;
}
@ -702,21 +704,21 @@ class AdminStatsSender extends Cron5Job
print str_pad($row['users'], 8, " ", STR_PAD_LEFT)."\n";
}
print "\n";
print "This report includes requests from external consumers and those made via\n";
print "Facade class (used by OC code). It does not include methods used by OKAPI\n";
print "internally (i.e. while running cronjobs). Runtimes do not include HTTP\n";
print "request handling overhead.\n";
$message = ob_get_clean();
Okapi::mail_admins("Weekly OKAPI usage report", $message);
}
private static function mb_str_pad($input, $pad_length, $pad_string, $pad_style)
{
{
return str_pad($input, strlen($input) - mb_strlen($input) + $pad_length,
$pad_string, $pad_style);
}
$pad_string, $pad_style);
}
}
/**
@ -773,3 +775,17 @@ class LocaleChecker extends Cron5Job
}
}
/**
* Once every hour, update the official cache attributes listing.
*
* WRTODO: Make it 12 hours later.
*/
class AttrsRefresherJob extends Cron5Job
{
public function get_period() { return 3600; }
public function execute()
{
require_once($GLOBALS['rootpath']."okapi/services/attrs/attr_helper.inc.php");
AttrHelper::refresh_if_stale();
}
}

View File

@ -4,22 +4,23 @@ namespace okapi;
# OKAPI Framework -- Wojciech Rygielski <rygielski@mimuw.edu.pl>
# Include this file if you want to use OKAPI's services with any
# external code (your service calls will appear under the name "Facade"
# in the weekly OKAPI usage report).
# Use this class when you want to use OKAPI's services within OC code.
# (Your service calls will appear with the name "Facade" in the weekly
# OKAPI usage report).
# Note, that his is the *ONLY* internal OKAPI file that is guaranteed
# to stay backward-compatible (I'm speaking about INTERNAL files here,
# all OKAPI methods will stay compatible forever). If you want to use
# something that has not been exposed through the Facade class, contact
# IMPORTANT COMPATIBILITY NOTES:
# Note, that this is the *ONLY* internal OKAPI file that is guaranteed
# to stay backward-compatible (note that we mean FILES here, all OKAPI
# methods will stay compatible forever). If you want to use any class or
# method that has not been exposed through the Facade class, contact
# OKAPI developers, we will add it here.
# Including this file will initialize OKAPI Framework with its default
# exception and error handlers. OKAPI is strict about PHP warnings and
# notices. You might need to temporarily disable the error handler in
# order to get it to work with some legacy code. Do this by calling
# OkapiErrorHandler::disable() BEFORE calling the "buggy" code, and
# OkapiErrorHandler::reenable() AFTER returning from it.
# notices, so you might need to temporarily disable the error handler in
# order to get it to work with your code. Just call this after you
# include the Facade file: OkapiErrorHandler::disable().
use Exception;
@ -54,7 +55,7 @@ class Facade
$request->perceive_as_http_request = true;
return OkapiServiceRunner::call($service_name, $request);
}
/**
* This works like service_call with two exceptions: 1. It passes all your
* current HTTP request headers to OKAPI (which can make use of them in
@ -74,7 +75,7 @@ class Facade
$response = OkapiServiceRunner::call($service_name, $request);
$response->display();
}
/**
* Create a search set from a temporary table. This is very similar to
* the "services/caches/search/save" method, but allows OC server to
@ -98,4 +99,42 @@ class Facade
$tables, $where_conds, $min_store, $max_ref_age
);
}
/**
* Mark the specified caches as *possibly* modified. The replicate module
* will scan for changes within these caches on the next changelog update.
* This is useful in some cases, when OKAPI cannot detect the modification
* for itself (grep OCPL code for examples). See issue #179.
*
* $cache_codes may be a single cache code or an array of codes.
*/
public static function schedule_geocache_check($cache_codes)
{
if (!is_array($cache_codes))
$cache_codes = array($cache_codes);
Db::execute("
update caches
set okapi_syncbase = now()
where wp_oc in ('".implode("','", array_map('mysql_real_escape_string', $cache_codes))."')
");
}
/**
* You will probably want to call that with FALSE when using Facade
* in buggy, legacy OC code. This will disable OKAPI's default behavior
* of treating NOTICEs as errors.
*/
public static function disable_error_handling()
{
OkapiErrorHandler::disable();
}
/**
* If you disabled OKAPI's error handling with disable_error_handling,
* you may reenable it with this method.
*/
public static function reenable_error_handling()
{
OkapiErrorHandler::reenable();
}
}

View File

@ -14,16 +14,16 @@ class OCSession
static $cached_result = false;
if ($cached_result !== false)
return $cached_result;
$cookie_name = Settings::get('OC_COOKIE_NAME');
if (!isset($_COOKIE[$cookie_name]))
return null;
$OC_data = unserialize(base64_decode($_COOKIE[$cookie_name]));
$OC_sessionid = $OC_data['sessionid'];
if (!$OC_sessionid)
return null;
return Db::select_value("select user_id from sys_sessions where uuid='".mysql_real_escape_string($OC_sessionid)."'");
}
}

View File

@ -2,75 +2,95 @@ msgid ""
msgstr ""
"Project-Id-Version: OKAPI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-08-22 21:18+0100\n"
"PO-Revision-Date: 2012-08-22 21:18+0100\n"
"Last-Translator: \n"
"Language-Team: following <following-okapi@online.de>\n"
"POT-Creation-Date: 2013-03-30 23:41+0100\n"
"PO-Revision-Date: 2013-03-30 23:42+0100\n"
"Last-Translator: following <following@online.de>\n"
"Language-Team: following <following@online.de>\n"
"Language: German\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: c:\\source\\oc\\server-3.0\\htdocs\\okapi\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Poedit-Basepath: c:\\source\\oc\\okapi\\following2\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: c:\\source\\oc\\server-3.0\\htdocs\\okapi\n"
"X-Generator: Poedit 1.5.5\n"
"X-Poedit-SearchPath-0: c:\\source\\oc\\okapi\\following2\n"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/geocaches.php:640
#: c:\source\oc\okapi\following2/okapi/services/caches/geocaches.php:817
msgid "Stage"
msgstr "Station"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:27
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:44
#: c:\source\oc\okapi\following2/okapi/services/caches/geocaches.php:950
#, php-format
msgid ""
"<em>&copy; <a href='%s'>%s</a>, <a href='%s'>%s</a>, <a href='http://"
"creativecommons.org/licenses/by-nc-nd/3.0/de/deed.en'>CC-BY-NC-ND</a>, as of "
"%s; all log entries &copy; their authors</em>"
msgstr ""
"<em>&copy; <a href='%s'>%s</a>, <a href='%s'>%s</a>, <a href='http://"
"creativecommons.org/licenses/by-nc-nd/3.0/de/'>CC-BY-NC-ND</a>, Stand: %s; "
"alle Logeinträge &copy; jeweiliger Autor</em>"
#: c:\source\oc\okapi\following2/okapi/services/caches/geocaches.php:960
#, php-format
msgid ""
"This <a href='%s'>geocache</a> description comes from the <a href='%s'>%s</"
"a> site."
msgstr ""
"Diese <a href='%s'>Cache</a>-Beschreibung stammt von <a href='%s'>%s</a>."
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:31
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:48
msgid "hidden by"
msgstr "versteckt von"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:46
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:50
#, php-format
msgid "%d recommendation"
msgid_plural "%d recommendations"
msgstr[0] "%d Empfehlung"
msgstr[1] "%d Empfehlungen"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:47
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:51
#, php-format
msgid "found %d time"
msgid_plural "found %d times"
msgstr[0] "%d mal gefunden"
msgstr[1] "%d mal gefunden"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:50
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:54
#, php-format
msgid "%d trackable"
msgid_plural "%d trackables"
msgstr[0] "%d Geokret"
msgstr[1] "%d Geokrets"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:54
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:58
msgid "Personal notes"
msgstr "Persönliche Notizen"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:58
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:62
msgid "Attributes"
msgstr "Attribute"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:62
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:66
msgid "Trackables"
msgstr "Geokrets"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:80
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:84
msgid "Images"
msgstr "Bilder"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:87
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:91
msgid "Spoilers"
msgstr "Spoiler"
#: c:\source\oc\server-3.0\htdocs\okapi/services/caches/formatters/gpxfile.tpl.php:95
#: c:\source\oc\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:99
msgid "Image descriptions"
msgstr "Bildbeschreibungen"
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:62
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:75
msgid ""
"You are trying to publish a log entry with a date in future. Cache log "
"entries are allowed to be published in the past, but NOT in the future."
@ -78,7 +98,7 @@ msgstr ""
"Das Datum deines Logeintrags liegt in der Zukunft. Cache-Logs können nur für "
"die Vergangenheit oder für heute eingetragen werden."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:82
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:97
#, php-format
msgid ""
"However, your cache rating was ignored, because %s does not have a rating "
@ -86,11 +106,11 @@ msgid ""
msgstr ""
"Deine Cachewertung wurde jedoch ignoriert, weil %s kein Bewertungssystem hat."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:92
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:108
msgid "Recommending is allowed only for 'Found it' logtypes."
msgstr "Empfehlungen sind nur bei 'Gefunden'-Logs erlaubt."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:101
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:118
#, php-format
msgid ""
"However, your \"needs maintenance\" flag was ignored, because %s does not "
@ -99,15 +119,7 @@ msgstr ""
"Deine Angabe \"benötigt Wartung\" wurde jedoch ignoriert, weil es diese "
"Funktion bei %s nicht gibt."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:124
msgid ""
"This cache is archived. Only admins and the owner are allowed to add a log "
"entry."
msgstr ""
"Dieser Cache ist archiviert. Nur OC-Admins und der Besitzer können "
"Logeinträge machen."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:128
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:136
msgid ""
"This cache is an Event cache. You cannot \"Find it\"! (But - you may "
"\"Comment\" on it.)"
@ -115,26 +127,26 @@ msgstr ""
"Dies ist ein Event-Cache. Du kannst ihn nicht \"finden\" (aber du kannst "
"einen Hinweis loggen)."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:130
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:138
msgid "Your have to supply some text for your comment."
msgstr "Du musst einen Text für dein Hinweislog eingeben!"
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:143
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:151
msgid "This cache requires a password. You didn't provide one!"
msgstr ""
"Dieser Cache kann nur mit Kennwort geloggt werden, aber du hast keines "
"angegeben."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:145
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:153
msgid "Invalid password!"
msgstr "Ungültiges Kennwort!"
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:194
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:203
msgid "You have already submitted a log entry with exactly the same contents."
msgstr ""
"Du hast bereits einen Logeintrag mit genau dem gleichen Inhalt gemacht."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:213
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:222
msgid ""
"You have already submitted a \"Found it\" log entry once. Now you may submit "
"\"Comments\" only!"
@ -142,49 +154,49 @@ msgstr ""
"Du hast diesen Cache bereits als gefunden geloggt. Ein zweites Fundlog ist "
"nicht möglich, aber du kannst stattdessen einen Hinweis loggen."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:215
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:224
msgid "You are the owner of this cache. You may submit \"Comments\" only!"
msgstr ""
"Als Besitzer des Caches kannst du nur Hinweise loggen, keine Funde oder "
"Nichtfunde."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:233
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:242
msgid "You have already rated this cache once. Your rating cannot be changed."
msgstr ""
"Du hast diesen Cache bereits bewertet. Deine Bewertung ist nicht änderbar."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:250
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:259
msgid "You have already recommended this cache once."
msgstr "Du hast diesen Cache bereits empfohlen."
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:257
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:266
msgid "You don't have any recommendations to give. Find more caches first!"
msgstr ""
"Du musst mehr Caches finden, um eine weitere Bewertung abgeben zu können!"
#: c:\source\oc\server-3.0\htdocs\okapi/services/logs/submit.php:409
#: c:\source\oc\okapi\following2/okapi/services/logs/submit.php:430
msgid "Your cache log entry was posted successfully."
msgstr "Dein Log wurde veröffentlicht."
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:5
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:5
msgid "Authorization Form"
msgstr "Authorisierungs-Formular"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:46
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:46
msgid "Expired request"
msgstr "Anfrage abgelaufen"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:47
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:47
msgid "Unfortunately, the request has expired. Please try again."
msgstr ""
"Die Anfrage ist wegen Zeitüberschreitung abgelaufen. Bitte versuche es noch "
"einmal."
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:49
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:49
msgid "External application is requesting access..."
msgstr "Eine externe Anwendung wünscht Zugriff ..."
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:50
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:50
#, php-format
msgid ""
"<b>%s</b> wants to access your <b>%s</b> account. Do you agree to grant "
@ -193,15 +205,15 @@ msgstr ""
"<b>%s</b> möchte auf dein <b>%s</b>-Benutzerkonto zugreifen. Möchtest du "
"dieser Anwendung Zugriff gewähren?"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:53
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:53
msgid "I agree"
msgstr "Ja"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:54
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:54
msgid "Decline"
msgstr "Nein"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorize.tpl.php:56
#: c:\source\oc\okapi\following2/okapi/views/apps/authorize.tpl.php:56
#, php-format
msgid ""
"\n"
@ -229,15 +241,15 @@ msgstr ""
"\t\t\t\t\tDu kannst diese Erlaubnis jederzeit widerrufen.</p>\n"
"\t\t\t\t"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorized.tpl.php:5
#: c:\source\oc\okapi\following2/okapi/views/apps/authorized.tpl.php:5
msgid "Authorization Succeeded"
msgstr "Authorisierung erfolgreich"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorized.tpl.php:28
#: c:\source\oc\okapi\following2/okapi/views/apps/authorized.tpl.php:28
msgid "Access successfully granted"
msgstr "Zugang wurde gewährt"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/authorized.tpl.php:29
#: c:\source\oc\okapi\following2/okapi/views/apps/authorized.tpl.php:29
#, php-format
msgid ""
"\n"
@ -254,15 +266,15 @@ msgstr ""
"PIN-Code ein:</p>\n"
"\t\t\t"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/index.tpl.php:5
#: c:\source\oc\okapi\following2/okapi/views/apps/index.tpl.php:5
msgid "My Apps"
msgstr "Meine Apps"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/index.tpl.php:29
#: c:\source\oc\okapi\following2/okapi/views/apps/index.tpl.php:29
msgid "Your external applications"
msgstr "Deine externe Anwendung"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/index.tpl.php:31
#: c:\source\oc\okapi\following2/okapi/views/apps/index.tpl.php:31
#, php-format
msgid ""
"\n"
@ -284,11 +296,11 @@ msgstr ""
"Aktionen mehr unter deinem \t\t\t\t\tBenutzername ausführen können.</p>\n"
"\t\t\t\t"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/index.tpl.php:45
#: c:\source\oc\okapi\following2/okapi/views/apps/index.tpl.php:45
msgid "remove"
msgstr "entfernen"
#: c:\source\oc\server-3.0\htdocs\okapi/views/apps/index.tpl.php:50
#: c:\source\oc\okapi\following2/okapi/views/apps/index.tpl.php:50
#, php-format
msgid ""
"\n"
@ -308,3 +320,10 @@ msgstr ""
"\t\t\t\t\tdazu ermächtigt. Sobald du externe Opencaching-Anwendungen "
"aktivierst, werden diese hier erscheinen.</p>\n"
"\t\t\t\t"
#~ msgid ""
#~ "This cache is archived. Only admins and the owner are allowed to add a "
#~ "log entry."
#~ msgstr ""
#~ "Dieser Cache ist archiviert. Nur OC-Admins und der Besitzer können "
#~ "Logeinträge machen."

View File

@ -0,0 +1,314 @@
msgid ""
msgstr ""
"Project-Id-Version: OKAPI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-03-26 16:20+0100\n"
"PO-Revision-Date: 2013-03-30 15:55+0100\n"
"Last-Translator: Stefano Cotterli <stefanocotterli@gmail.com>\n"
"Language-Team: following <following@online.de>\n"
"Language: Italian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: c:\\source\\okapi\\following2\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Generator: Poedit 1.5.5\n"
"X-Poedit-SearchPath-0: c:\\source\\okapi\\following2\n"
#: c:\source\okapi\following2/okapi/services/caches/geocaches.php:777
msgid "Stage"
msgstr "Passo"
#: c:\source\okapi\following2/okapi/services/caches/geocaches.php:897
#, php-format
msgid ""
"This <a href='%s'>geocache</a> description comes from the <a href='%s'>%s</"
"a> site."
msgstr ""
"La deescrizione di questa <a href='%s'>geocache</a>proviene dal sito <a "
"href='%s'>%s</a>."
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:31
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:48
msgid "hidden by"
msgstr "nascosta da"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:50
#, php-format
msgid "%d recommendation"
msgid_plural "%d recommendations"
msgstr[0] "%d raccomandazione"
msgstr[1] "%d raccomandazioni"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:51
#, php-format
msgid "found %d time"
msgid_plural "found %d times"
msgstr[0] "trovata %d volta"
msgstr[1] "trovata %d volte"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:54
#, php-format
msgid "%d trackable"
msgid_plural "%d trackables"
msgstr[0] "%d travel bug"
msgstr[1] "%d travel bugs"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:58
msgid "Personal notes"
msgstr "Note personali"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:62
msgid "Attributes"
msgstr "Attributi"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:66
msgid "Trackables"
msgstr "Travel bugs"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:84
msgid "Images"
msgstr "Immagini"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:91
msgid "Spoilers"
msgstr "Spoiler"
#: c:\source\okapi\following2/okapi/services/caches/formatters/gpxfile.tpl.php:99
msgid "Image descriptions"
msgstr "Descrizione immagine"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:62
msgid ""
"You are trying to publish a log entry with a date in future. Cache log "
"entries are allowed to be published in the past, but NOT in the future."
msgstr ""
"Hai cercato di pubblicare un log con una data nel futuro. E' permsso loggare "
"una cache con una data passata, ma NON futura."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:82
#, php-format
msgid ""
"However, your cache rating was ignored, because %s does not have a rating "
"system."
msgstr ""
"Tuttavia, la tua valutazione della cache sarà ignorata, perché %s non ha un "
"sistema di valutazione."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:92
msgid "Recommending is allowed only for 'Found it' logtypes."
msgstr "Le raccomandazioni sono ammesse solo per i log di tipo 'Trovata'"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:101
#, php-format
msgid ""
"However, your \"needs maintenance\" flag was ignored, because %s does not "
"support this feature."
msgstr ""
"Tuttavia, la tua segnalazione di \"manutenzione necessaria\" sarà ignorata, "
"perché %s non supporta questa opzione."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:119
msgid ""
"This cache is an Event cache. You cannot \"Find it\"! (But - you may "
"\"Comment\" on it.)"
msgstr ""
"Questa cache è una cache Evento. Non puoi \"Trovarla\"! (Ma puoi inserirci "
"un \"Commento\")."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:121
msgid "Your have to supply some text for your comment."
msgstr "Devi inserire del testo per il tuo commento."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:134
msgid "This cache requires a password. You didn't provide one!"
msgstr "Questa cache richiede una password, ma non ne hai fornita nessuuna."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:136
msgid "Invalid password!"
msgstr "Password non valida!"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:186
msgid "You have already submitted a log entry with exactly the same contents."
msgstr "Hai già inserito un log esattamente con lo stesso contenuto."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:205
msgid ""
"You have already submitted a \"Found it\" log entry once. Now you may submit "
"\"Comments\" only!"
msgstr ""
"Hai già inserito un log \"Trovata\". Adesso puoi inserire solamente "
"\"Commenti\"!"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:207
msgid "You are the owner of this cache. You may submit \"Comments\" only!"
msgstr "Sei il proprietario della cache. Puoi inserire solamente \"Commenti\"!"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:225
msgid "You have already rated this cache once. Your rating cannot be changed."
msgstr ""
"Hai giàvalutato questa cache. La tua valutazione non può essere cambiata."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:242
msgid "You have already recommended this cache once."
msgstr "Hai già raccmandato questa cache."
#: c:\source\okapi\following2/okapi/services/logs/submit.php:249
msgid "You don't have any recommendations to give. Find more caches first!"
msgstr ""
"Non hai possibilità di dare raccomandazioni. Prima devio trovare altre cache!"
#: c:\source\okapi\following2/okapi/services/logs/submit.php:413
msgid "Your cache log entry was posted successfully."
msgstr "Il tuo log sulla cache è stato correttamente inserito."
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:5
msgid "Authorization Form"
msgstr "Modulo di autorizzazione"
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:46
msgid "Expired request"
msgstr "Richiesta scaduta"
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:47
msgid "Unfortunately, the request has expired. Please try again."
msgstr "Sfortunatamente, la richiesta è scaduta. Per favore riprova."
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:49
msgid "External application is requesting access..."
msgstr "Una applicazione esterna sta richiedendo l'accesso."
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:50
#, php-format
msgid ""
"<b>%s</b> wants to access your <b>%s</b> account. Do you agree to grant "
"access to this application?"
msgstr ""
"<b>%s</b> vuole accedere al tuo account <b>%s</b>. Sei d'accordo nel "
"concedere l'accesso a questa applicazione?"
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:53
msgid "I agree"
msgstr "Sono d'accordo"
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:54
msgid "Decline"
msgstr "No"
#: c:\source\okapi\following2/okapi/views/apps/authorize.tpl.php:56
#, php-format
msgid ""
"\n"
"\t\t\t\t\t<p>Once permission is granted it is valid until its withdrawal on\n"
"\t\t\t\t\tthe <a href='%s'>applications management</a> page.</p>\n"
"\t\t\t\t\t<p>The application will access your acount via <a href='%s'>the "
"OKAPI Framework</a>.\n"
"\t\t\t\t\tIf you allow this request application will be able to access all "
"methods delivered\n"
"\t\t\t\t\tby the OKAPI Framework, i.e. post log entries on geocaches in your "
"name.\n"
"\t\t\t\t\tYou can revoke this permission at any moment.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"\t\t\t\t\t<p>Una volta concessa, l'autorizzazoine è valida finché non venga\n"
"\t\t\t\t\trevocata nella pagina di <a href='%s'>gestione applicazioni</a>.</"
"p>\n"
"\t\t\t\t\t<p>L'applicazione accederà al tuo account tramite il <a "
"href='%s'>frameword OKAPI</a>.\n"
"\t\t\t\t\tSe permetti questa richiesta, l'applicazione potrà accedere a "
"tutti i metodi forniti\n"
"\t\t\t\t\tdal framework OKAPI, per es. postare i log delle geocache a tuo "
"nome..\n"
"\t\t\t\t\tPuoi revocare questo permesso in qualsiasi momento.</p>\n"
"\t\t\t\t"
#: c:\source\okapi\following2/okapi/views/apps/authorized.tpl.php:5
msgid "Authorization Succeeded"
msgstr "Autorizzazione concessa"
#: c:\source\okapi\following2/okapi/views/apps/authorized.tpl.php:28
msgid "Access successfully granted"
msgstr "Accesso correttamente consentito"
#: c:\source\okapi\following2/okapi/views/apps/authorized.tpl.php:29
#, php-format
msgid ""
"\n"
"\t\t\t\t<p><b>You've just granted %s application access to your %s account.</"
"b>\n"
"\t\t\t\tTo complete the operation, go back to %s and enter the following PIN "
"code:</p>\n"
"\t\t\t"
msgstr ""
"\n"
"\t\t\t\t<p><b>Hai appena concesso all'applicazione \"%s\" l'accesso al tuo "
"account %s.</b>\n"
"\t\t\t\tPer completare l'operazione, riorna a %s e inserisci il seguente "
"codice PIN:</p>\n"
"\t\t\t"
#: c:\source\okapi\following2/okapi/views/apps/index.tpl.php:5
msgid "My Apps"
msgstr "Mie App"
#: c:\source\okapi\following2/okapi/views/apps/index.tpl.php:29
msgid "Your external applications"
msgstr "Le tue applicazioni esterne"
#: c:\source\okapi\following2/okapi/views/apps/index.tpl.php:31
#, php-format
msgid ""
"\n"
"\t\t\t\t\t<p>This is the list of applications which you granted access to "
"your <b>%s</b> account.\n"
"\t\t\t\t\tThis page gives you the abbility to revoke all previously granted "
"privileges.\n"
"\t\t\t\t\tOnce you click \"remove\" the application will no longer be able "
"to perform any\n"
"\t\t\t\t\tactions on your behalf.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"\t\t\t\t\t<p>Questa è la lista delle applicazioni a cui hai concesso "
"l'accesso al tuo account <b>%s</b.\n"
"\t\t\t\t\tQuesta pagina ti da la possibilità di revocare tutti privilegi "
"precedentemente concessi.\n"
"\t\t\t\t\tQundo clicchi su \"rimuovi\" all'applicazione non sarà più "
"concesso eseguire nessuna\n"
" \t\t\t\t\tazione per tuo conto.</p>\n"
"\t\t\t\t"
#: c:\source\okapi\following2/okapi/views/apps/index.tpl.php:45
msgid "remove"
msgstr "entfernen"
#: c:\source\okapi\following2/okapi/views/apps/index.tpl.php:50
#, php-format
msgid ""
"\n"
"\t\t\t\t\t<p>Thanks to the <a href='%s'>OKAPI Framework</a> you can grant "
"external applications\n"
"\t\t\t\t\taccess to your <b>%s</b> account. Currently no applications are "
"authorized to act\n"
"\t\t\t\t\ton your behalf. Once you start using external OpenCaching "
"applications, they will appear here.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"\t\t\t\t\t<p>Grazie al <a href='%s'>framework OKAPI</a>puoi concedere ad "
"applicazioni esterne\n"
"\t\t\t\t\t l'accesso al tuo account <b>%s</b>. Attualmente non ci son "
"applicazioni autorizzate\n"
"\t\t\t\t\tad agire per tuo conto. Quando userai applicazioni esterne a "
"OpenCaching, queste appariranno qui</p>\n"
"\t\t\t\t"
#~ msgid ""
#~ "This cache is archived. Only admins and the owner are allowed to add a "
#~ "log entry."
#~ msgstr ""
#~ "Dieser Cache ist archiviert. Nur OC-Admins und der Besitzer können "
#~ "Logeinträge machen."

View File

@ -9,8 +9,9 @@ class Locales
'en' => array('lang' => 'en', 'locale' => 'en_US.utf8', 'name' => 'English'),
'nl' => array('lang' => 'nl', 'locale' => 'nl_NL.utf8', 'name' => 'Dutch'),
'de' => array('lang' => 'de', 'locale' => 'de_DE.utf8', 'name' => 'German'),
'it' => array('lang' => 'it', 'locale' => 'it_IT.utf8', 'name' => 'Italian'),
);
/**
* Get the list of locales that should be installed on the system in order
* for all translations to work properly.
@ -22,7 +23,7 @@ class Locales
$arr[] = $value['locale'];
return $arr;
}
/**
* Get the list of locales installed on the current system.
*/
@ -34,14 +35,14 @@ class Locales
$arr[] = $item;
return $arr;
}
private static function get_locale_for_language($lang)
{
if (isset(self::$languages[$lang]))
return self::$languages[$lang]['locale'];
return null;
}
public static function get_best_locale($langprefs)
{
foreach ($langprefs as $lang)

View File

@ -2,31 +2,40 @@ msgid ""
msgstr ""
"Project-Id-Version: OKAPI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-08-23 10:20+0100\n"
"PO-Revision-Date: 2012-08-23 10:27+0100\n"
"POT-Creation-Date: 2013-02-19 10:36+0100\n"
"PO-Revision-Date: 2013-02-19 10:37+0100\n"
"Last-Translator: Wojciech Rygielski <rygielski@mimuw.edu.pl>\n"
"Language-Team: \n"
"Language: pl_PL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: D:\\PRIV\\Projekty\\EclipseWorkspace\\opencaching-api\\okapi\n"
"Plural-Forms: nplurals=3; plural= n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Poedit-Language: Polish\n"
"X-Poedit-Country: POLAND\n"
"X-Poedit-Basepath: D:\\PRIV\\Projekty\\EclipseWorkspace\\opencaching-api"
"\\okapi\n"
"Plural-Forms: nplurals=3; plural= n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Poedit 1.5.5\n"
"X-Poedit-SearchPath-0: .\n"
#: services/caches/geocaches.php:642
#: services/caches/geocaches.php:746
msgid "Stage"
msgstr "Etap"
#: services/caches/formatters/gpxfile.tpl.php:27
#: services/caches/formatters/gpxfile.tpl.php:44
#: services/caches/geocaches.php:866
#, php-format
msgid ""
"This <a href='%s'>geocache</a> description comes from the <a href='%s'>%s</"
"a> site."
msgstr "Opis <a href='%s'>skrzynki</a> pochodzi z serwisu <a href='%s'>%s</a>."
#: services/caches/formatters/gpxfile.tpl.php:31
#: services/caches/formatters/gpxfile.tpl.php:48
msgid "hidden by"
msgstr "ukryta przez"
#: services/caches/formatters/gpxfile.tpl.php:46
#: services/caches/formatters/gpxfile.tpl.php:50
#, php-format
msgid "%d recommendation"
msgid_plural "%d recommendations"
@ -34,7 +43,7 @@ msgstr[0] "%d rekomendacja"
msgstr[1] "%d rekomendacje"
msgstr[2] "%d rekomendacji"
#: services/caches/formatters/gpxfile.tpl.php:47
#: services/caches/formatters/gpxfile.tpl.php:51
#, php-format
msgid "found %d time"
msgid_plural "found %d times"
@ -42,7 +51,7 @@ msgstr[0] "znaleziona %d raz"
msgstr[1] "znaleziona %d razy"
msgstr[2] "znaleziona %d razy"
#: services/caches/formatters/gpxfile.tpl.php:50
#: services/caches/formatters/gpxfile.tpl.php:54
#, php-format
msgid "%d trackable"
msgid_plural "%d trackables"
@ -50,38 +59,46 @@ msgstr[0] "%d GeoKret (lub TravelBug)"
msgstr[1] "%d GeoKrety (lub TravelBugi)"
msgstr[2] "%d GeoKretów (lub TravelBugów)"
#: services/caches/formatters/gpxfile.tpl.php:54
#: services/caches/formatters/gpxfile.tpl.php:58
msgid "Personal notes"
msgstr "Osobiste notatki"
#: services/caches/formatters/gpxfile.tpl.php:58
#: services/caches/formatters/gpxfile.tpl.php:62
msgid "Attributes"
msgstr "Atrybuty"
#: services/caches/formatters/gpxfile.tpl.php:62
#: services/caches/formatters/gpxfile.tpl.php:66
msgid "Trackables"
msgstr "Geokrety, Travelbugi itp."
#: services/caches/formatters/gpxfile.tpl.php:80
#: services/caches/formatters/gpxfile.tpl.php:84
msgid "Images"
msgstr "Obrazki"
#: services/caches/formatters/gpxfile.tpl.php:87
#: services/caches/formatters/gpxfile.tpl.php:91
msgid "Spoilers"
msgstr "Spoilery"
#: services/caches/formatters/gpxfile.tpl.php:95
#: services/caches/formatters/gpxfile.tpl.php:99
msgid "Image descriptions"
msgstr "Opisy obrazków"
#: services/logs/submit.php:62
msgid "You are trying to publish a log entry with a date in future. Cache log entries are allowed to be published in the past, but NOT in the future."
msgstr "Próbujesz opublikować wpis do logbooka używając daty w przyszłości. Wpisy mogą być publikowane z datą w przeszłości, ale NIE w przyszłości."
msgid ""
"You are trying to publish a log entry with a date in future. Cache log "
"entries are allowed to be published in the past, but NOT in the future."
msgstr ""
"Próbujesz opublikować wpis do logbooka używając daty w przyszłości. Wpisy "
"mogą być publikowane z datą w przeszłości, ale NIE w przyszłości."
#: services/logs/submit.php:82
#, php-format
msgid "However, your cache rating was ignored, because %s does not have a rating system."
msgstr "Niestety Twoja ocena skrzynki nie została zapisana, ponieważ %s nie prowadzi oceny skrzynek."
msgid ""
"However, your cache rating was ignored, because %s does not have a rating "
"system."
msgstr ""
"Niestety Twoja ocena skrzynki nie została zapisana, ponieważ %s nie prowadzi "
"oceny skrzynek."
#: services/logs/submit.php:92
msgid "Recommending is allowed only for 'Found it' logtypes."
@ -89,54 +106,65 @@ msgstr "Rekomendacje są dozwolone jedynie z wpisem \"Znaleziona\"."
#: services/logs/submit.php:101
#, php-format
msgid "However, your \"needs maintenance\" flag was ignored, because %s does not support this feature."
msgstr "Niestety, wpis \"potrzebny serwis\" został zignorowany, ponieważ %s nie wspiera tej funkcjonalności."
msgid ""
"However, your \"needs maintenance\" flag was ignored, because %s does not "
"support this feature."
msgstr ""
"Niestety, wpis \"potrzebny serwis\" został zignorowany, ponieważ %s nie "
"wspiera tej funkcjonalności."
#: services/logs/submit.php:124
msgid "This cache is archived. Only admins and the owner are allowed to add a log entry."
msgstr "Ta skrzynka jest zarchiwizowana. Jedynie administratorzy oraz właściciel mogą dodawać komentarze."
#: services/logs/submit.php:119
msgid ""
"This cache is an Event cache. You cannot \"Find it\"! (But - you may "
"\"Comment\" on it.)"
msgstr ""
"Ta skrzynka jest typu Wydarzenie. Nie możesz jej \"znaleźć\"! (Ale - możesz "
"dodać wpis typu \"Komentarz\".)"
#: services/logs/submit.php:128
msgid "This cache is an Event cache. You cannot \"Find it\"! (But - you may \"Comment\" on it.)"
msgstr "Ta skrzynka jest typu Wydarzenie. Nie możesz jej \"znaleźć\"! (Ale - możesz dodać wpis typu \"Komentarz\".)"
#: services/logs/submit.php:130
#: services/logs/submit.php:121
msgid "Your have to supply some text for your comment."
msgstr "Wpis typu \"Komentarz\" wymaga wpisania komentarza."
#: services/logs/submit.php:143
#: services/logs/submit.php:134
msgid "This cache requires a password. You didn't provide one!"
msgstr "Ta skrzynka wymaga podania hasła. Nie wpisałeś go."
#: services/logs/submit.php:145
#: services/logs/submit.php:136
msgid "Invalid password!"
msgstr "Niepoprawne hasło!"
#: services/logs/submit.php:194
#: services/logs/submit.php:186
msgid "You have already submitted a log entry with exactly the same contents."
msgstr "Już opublikowałeś wcześniej wpis z dokładnie taką samą treścią."
#: services/logs/submit.php:213
msgid "You have already submitted a \"Found it\" log entry once. Now you may submit \"Comments\" only!"
msgstr "Już opublikowałeś jeden wpis typu \"Znaleziona\" dla tej skrzynki. Teraz możesz dodawać jedynie \"Komentarze\"!"
#: services/logs/submit.php:205
msgid ""
"You have already submitted a \"Found it\" log entry once. Now you may submit "
"\"Comments\" only!"
msgstr ""
"Już opublikowałeś jeden wpis typu \"Znaleziona\" dla tej skrzynki. Teraz "
"możesz dodawać jedynie \"Komentarze\"!"
#: services/logs/submit.php:215
#: services/logs/submit.php:207
msgid "You are the owner of this cache. You may submit \"Comments\" only!"
msgstr "Jesteś właścicielem tej skrzynki. Możesz przesyłać jedynie \"Komentarze\"."
msgstr ""
"Jesteś właścicielem tej skrzynki. Możesz przesyłać jedynie \"Komentarze\"."
#: services/logs/submit.php:233
#: services/logs/submit.php:225
msgid "You have already rated this cache once. Your rating cannot be changed."
msgstr "Już oceniłeś tę skrzynkę. Ocena nie może być zmieniona."
#: services/logs/submit.php:250
#: services/logs/submit.php:242
msgid "You have already recommended this cache once."
msgstr "Już raz zarekomendowałeś tę skrzynkę."
#: services/logs/submit.php:257
#: services/logs/submit.php:249
msgid "You don't have any recommendations to give. Find more caches first!"
msgstr "Aktualnie nie możesz wystawić kolejnej rekomendacji. Znajdź najpierw więcej skrzynek!"
msgstr ""
"Aktualnie nie możesz wystawić kolejnej rekomendacji. Znajdź najpierw więcej "
"skrzynek!"
#: services/logs/submit.php:409
#: services/logs/submit.php:413
msgid "Your cache log entry was posted successfully."
msgstr "Twój wpis do logbooka został opublikowany pomyślnie."
@ -158,8 +186,12 @@ msgstr "Aplikacja zewnętrzna prosi o dostęp..."
#: views/apps/authorize.tpl.php:50
#, php-format
msgid "<b>%s</b> wants to access your <b>%s</b> account. Do you agree to grant access to this application?"
msgstr "<b>%s</b> chce uzyskać dostęp do Twojego konta <b>%s</b>. Czy zgadzasz się na udzielenie dostępu tej aplikacji?"
msgid ""
"<b>%s</b> wants to access your <b>%s</b> account. Do you agree to grant "
"access to this application?"
msgstr ""
"<b>%s</b> chce uzyskać dostęp do Twojego konta <b>%s</b>. Czy zgadzasz się "
"na udzielenie dostępu tej aplikacji?"
#: views/apps/authorize.tpl.php:53
msgid "I agree"
@ -175,14 +207,23 @@ msgid ""
"\n"
"\t\t\t\t\t<p>Once permission is granted it is valid until its withdrawal on\n"
"\t\t\t\t\tthe <a href='%s'>applications management</a> page.</p>\n"
"\t\t\t\t\t<p>The application will access your acount via <a href='%s'>the OKAPI Framework</a>.\n"
"\t\t\t\t\tIf you allow this request application will be able to access all methods delivered\n"
"\t\t\t\t\tby the OKAPI Framework, i.e. post log entries on geocaches in your name.\n"
"\t\t\t\t\t<p>The application will access your acount via <a href='%s'>the "
"OKAPI Framework</a>.\n"
"\t\t\t\t\tIf you allow this request application will be able to access all "
"methods delivered\n"
"\t\t\t\t\tby the OKAPI Framework, i.e. post log entries on geocaches in your "
"name.\n"
"\t\t\t\t\tYou can revoke this permission at any moment.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"<p>Raz udzielona zgoda jest ważna aż do momentu jej wycofania na stronie <a href='%s'>zarządzania aplikacjami</a>.</p><p>Aplikacja będzie łączyć się z Twoim kontem poprzez <a href='%s'>platformę OKAPI</a> (strona w języku angielskim). Uzyskanie zgody na dostęp pozwoli aplikacji na korzystanie ze wszystkich metod udostępnianych przez platformę OKAPI (m.in. aplikacja będzie mogła umieszczać komentarze pod znajdowanymi przez Ciebie skrzynkami). Zgodę możesz wycofać w każdym momencie.</p>"
"<p>Raz udzielona zgoda jest ważna aż do momentu jej wycofania na stronie <a "
"href='%s'>zarządzania aplikacjami</a>.</p><p>Aplikacja będzie łączyć się z "
"Twoim kontem poprzez <a href='%s'>platformę OKAPI</a> (strona w języku "
"angielskim). Uzyskanie zgody na dostęp pozwoli aplikacji na korzystanie ze "
"wszystkich metod udostępnianych przez platformę OKAPI (m.in. aplikacja "
"będzie mogła umieszczać komentarze pod znajdowanymi przez Ciebie "
"skrzynkami). Zgodę możesz wycofać w każdym momencie.</p>"
#: views/apps/authorized.tpl.php:5
msgid "Authorization Succeeded"
@ -196,13 +237,16 @@ msgstr "Pomyślnie dałeś dostęp"
#, php-format
msgid ""
"\n"
"\t\t\t\t<p><b>You've just granted %s application access to your %s account.</b>\n"
"\t\t\t\tTo complete the operation, go back to %s and enter the following PIN code:</p>\n"
"\t\t\t\t<p><b>You've just granted %s application access to your %s account.</"
"b>\n"
"\t\t\t\tTo complete the operation, go back to %s and enter the following PIN "
"code:</p>\n"
"\t\t\t"
msgstr ""
"\n"
"<p><b>Właśnie dałeś dostęp aplikacji %s do Twojego konta %s.</b>\n"
"Aby zakończyć operację, wróć teraz do aplikacji %s i wpisz następujący kod PIN:</p>"
"Aby zakończyć operację, wróć teraz do aplikacji %s i wpisz następujący kod "
"PIN:</p>"
#: views/apps/index.tpl.php:5
msgid "My Apps"
@ -216,16 +260,21 @@ msgstr "Twoje zewnętrzne aplikacje"
#, php-format
msgid ""
"\n"
"\t\t\t\t\t<p>This is the list of applications which you granted access to your <b>%s</b> account.\n"
"\t\t\t\t\tThis page gives you the abbility to revoke all previously granted privileges.\n"
"\t\t\t\t\tOnce you click \"remove\" the application will no longer be able to perform any\n"
"\t\t\t\t\t<p>This is the list of applications which you granted access to "
"your <b>%s</b> account.\n"
"\t\t\t\t\tThis page gives you the abbility to revoke all previously granted "
"privileges.\n"
"\t\t\t\t\tOnce you click \"remove\" the application will no longer be able "
"to perform any\n"
"\t\t\t\t\tactions on your behalf.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"<p>Następującym aplikacjom zezwoliłeś na dostęp do swojego konta <b>%s</b>.\n"
"Na tej stronie możesz wycofać udzielone poprzednio zezwolenia. Po kliknięciu\n"
"\"usuń\" aplikacja nie będzie już mogła wykonywać żadnych operacji w Twoim imieniu.</p>"
"Na tej stronie możesz wycofać udzielone poprzednio zezwolenia. Po "
"kliknięciu\n"
"\"usuń\" aplikacja nie będzie już mogła wykonywać żadnych operacji w Twoim "
"imieniu.</p>"
#: views/apps/index.tpl.php:45
msgid "remove"
@ -235,18 +284,30 @@ msgstr "usuń"
#, php-format
msgid ""
"\n"
"\t\t\t\t\t<p>Thanks to the <a href='%s'>OKAPI Framework</a> you can grant external applications\n"
"\t\t\t\t\taccess to your <b>%s</b> account. Currently no applications are authorized to act\n"
"\t\t\t\t\ton your behalf. Once you start using external OpenCaching applications, they will appear here.</p>\n"
"\t\t\t\t\t<p>Thanks to the <a href='%s'>OKAPI Framework</a> you can grant "
"external applications\n"
"\t\t\t\t\taccess to your <b>%s</b> account. Currently no applications are "
"authorized to act\n"
"\t\t\t\t\ton your behalf. Once you start using external OpenCaching "
"applications, they will appear here.</p>\n"
"\t\t\t\t"
msgstr ""
"\n"
"<p>Dzięki platformie <a href='%s'>OKAPI</a> możesz dawać zewnętrznym aplikacjom\n"
"dostęp do Twojego konta <b>%s</b>. Aktualnie nie pozwalasz żadnej aplikacji na działania\n"
"w Twoim imieniu. Jeśli kiedyś dodasz jakieś aplikacje, to ich lista pojawi się tutaj.</p>"
"<p>Dzięki platformie <a href='%s'>OKAPI</a> możesz dawać zewnętrznym "
"aplikacjom\n"
"dostęp do Twojego konta <b>%s</b>. Aktualnie nie pozwalasz żadnej aplikacji "
"na działania\n"
"w Twoim imieniu. Jeśli kiedyś dodasz jakieś aplikacje, to ich lista pojawi "
"się tutaj.</p>"
#~ msgid ""
#~ "This cache is archived. Only admins and the owner are allowed to add a "
#~ "log entry."
#~ msgstr ""
#~ "Ta skrzynka jest zarchiwizowana. Jedynie administratorzy oraz właściciel "
#~ "mogą dodawać komentarze."
#~ msgid "from among %d vote"
#~ msgid_plural "from among %d votes"
#~ msgstr[0] "spośród %d oceny"
#~ msgstr[1] "spośród %d ocen"

View File

@ -262,9 +262,9 @@ abstract class OAuthSignatureMethod {
}
/**
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1")
*/
@ -290,7 +290,7 @@ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
}
/**
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* over a secure channel such as HTTPS. It does not use the Signature Base String.
* - Chapter 9.4 ("PLAINTEXT")
*/
@ -300,8 +300,8 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
}
/**
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* empty. The result MUST be encoded again.
* - Chapter 9.4.1 ("Generating Signatures")
*
@ -323,10 +323,10 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
}
/**
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* verified way to the Service Provider, in a manner which is beyond the scope of this
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* verified way to the Service Provider, in a manner which is beyond the scope of this
* specification.
* - Chapter 9.3 ("RSA-SHA1")
*/
@ -727,7 +727,7 @@ class OAuthServer {
protected function get_version(&$request) {
$version = $request->get_parameter("oauth_version");
if (!$version) {
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Chapter 7.0 ("Accessing Protected Ressources")
$version = '1.0';
}
@ -741,7 +741,7 @@ class OAuthServer {
* figure out the signature with some defaults
*/
private function get_signature_method($request) {
$signature_method = $request instanceof OAuthRequest
$signature_method = $request instanceof OAuthRequest
? $request->get_parameter("oauth_signature_method")
: NULL;
@ -766,7 +766,7 @@ class OAuthServer {
* try to find the consumer for the provided request's consumer key
*/
protected function get_consumer($request) {
$consumer_key = $request instanceof OAuthRequest
$consumer_key = $request instanceof OAuthRequest
? $request->get_parameter("oauth_consumer_key")
: NULL;
@ -838,7 +838,7 @@ class OAuthServer {
private function check_timestamp($timestamp) {
if( ! $timestamp )
throw new OAuthMissingParameterException('oauth_timestamp');
// verify that timestamp is recentish
$now = time();
if (abs($now - $timestamp) > $this->timestamp_threshold) {

View File

@ -23,6 +23,7 @@ class OkapiServiceRunner
'services/apiref/method',
'services/apiref/method_index',
'services/apiref/issue',
'services/attrs/info',
'services/oauth/request_token',
'services/oauth/authorize',
'services/oauth/access_token',
@ -53,13 +54,13 @@ class OkapiServiceRunner
'services/replicate/fulldump',
'services/replicate/info',
);
/** Check if method exists. */
public static function exists($service_name)
{
return in_array($service_name, self::$all_names);
}
/** Get method options (is consumer required etc.). */
public static function options($service_name)
{
@ -77,8 +78,8 @@ class OkapiServiceRunner
$e->getMessage());
}
}
/**
/**
* Get method documentation file contents (stuff within the XML file).
* If you're looking for a parsed representation, use services/apiref/method.
*/
@ -92,13 +93,13 @@ class OkapiServiceRunner
throw new Exception("Missing documentation file: $service_name.xml");
}
}
/**
/**
* Execute the method and return the result.
*
*
* OKAPI methods return OkapiHttpResponses, but some MAY also return
* PHP objects (see OkapiRequest::construct_inside_request for details).
*
*
* If $request must be consistent with given method's options (must
* include Consumer and Token, if they are required).
*/
@ -108,7 +109,7 @@ class OkapiServiceRunner
if (!self::exists($service_name))
throw new Exception("Method does not exist: '$service_name'");
$options = self::options($service_name);
if ($options['min_auth_level'] >= 2 && $request->consumer == null)
{
@ -121,7 +122,7 @@ class OkapiServiceRunner
throw new Exception("Method '$service_name' called with mismatched OkapiRequest: ".
"\$request->token MAY NOT be empty for Level 3 methods.");
}
$time_started = microtime(true);
Okapi::gettext_domain_init();
try
@ -135,14 +136,14 @@ class OkapiServiceRunner
throw $e;
}
$runtime = microtime(true) - $time_started;
# Log the request to the stats table. Only valid requests (these which didn't end up
# with an exception) are logged.
self::save_stats($service_name, $request, $runtime);
return $response;
}
/**
* For internal use only. The stats table can be used to store any kind of
* runtime-stats data, i.e. not only regarding services. This is a special
@ -153,13 +154,13 @@ class OkapiServiceRunner
{
self::save_stats("extra/".$extra_name, $request, $runtime);
}
private static function save_stats($service_name, $request, $runtime)
{
# Getting rid of nulls. MySQL PRIMARY keys cannot contain nullable columns.
# Temp table doesn't have primary key, but other stats tables (which are
# dependant on stats table) - do.
if ($request !== null) {
$consumer_key = ($request->consumer != null) ? $request->consumer->key : 'anonymous';
$user_id = (($request->token != null) && ($request->token instanceof OkapiAccessToken)) ? $request->token->user_id : -1;
@ -172,7 +173,7 @@ class OkapiServiceRunner
$user_id = -1;
$calltype = 'internal';
}
Db::execute("
insert into okapi_stats_temp (`datetime`, consumer_key, user_id, service_name, calltype, runtime)
values (

View File

@ -21,7 +21,7 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
$issue_id = $request->get_parameter('issue_id');
@ -29,13 +29,13 @@ class WebService
throw new ParamMissing('issue_id');
if ((!preg_match("/^[0-9]+$/", $issue_id)) || (strlen($issue_id) > 6))
throw new InvalidParam('issue_id');
$cache_key = "apiref/issue#".$issue_id;
$result = Cache::get($cache_key);
if ($result == null)
{
# Download list of comments from Google Code Issue Tracker.
try
{
$opts = array(
@ -54,7 +54,7 @@ class WebService
"This is probably due to a temporary connection problem. Try again later or contact ".
"us if this seems permanent.");
}
$doc = simplexml_load_string($xml);
$result = array(
'id' => $issue_id + 0,
@ -63,11 +63,11 @@ class WebService
'url' => (string)$doc->link[0]['href'],
'comment_count' => $doc->entry->count()
);
# On one hand, we want newly added comments to show up quickly.
# On the other, we don't want OKAPI to contantly query Google Code.
# It's difficult to choose a correct timeout for this...
Cache::set($cache_key, $result, 3600);
}
return Okapi::formatted_response($request, $result);

View File

@ -20,7 +20,7 @@ class WebService
'min_auth_level' => 0
);
}
private static function arg_desc($arg_node)
{
$attrs = $arg_node->attributes();
@ -32,10 +32,10 @@ class WebService
'description' =>
(isset($attrs['default']) ? ("<p>Default value: <b>".$attrs['default']."</b></p>") : "").
self::get_inner_xml($arg_node),
);
}
private static function get_inner_xml($node)
{
$s = $node->asXML();
@ -43,7 +43,7 @@ class WebService
$length = strlen($s) - $start - (3 + strlen($node->getName()));
return substr($s, $start, $length);
}
public static function call(OkapiRequest $request)
{
$methodname = $request->get_parameter('name');

View File

@ -21,7 +21,7 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
$methodnames = OkapiServiceRunner::$all_names;

View File

@ -19,7 +19,7 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
$result = array();

View File

@ -21,19 +21,19 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
# The list of installations is periodically refreshed by contacting OKAPI
# repository. This method usually displays the cached version of it.
$cachekey = 'apisrv/installations';
$backupkey = 'apisrv/installations-backup';
$results = Cache::get($cachekey);
if (!$results)
{
# Download the current list of OKAPI servers.
try
{
$opts = array(
@ -49,17 +49,17 @@ class WebService
catch (ErrorException $e)
{
# Google failed on us. Try to respond with a backup list.
$results = Cache::get($backupkey);
if ($results)
{
Cache::set($cachekey, $results, 12 * 3600); # so to retry no earlier than after 12 hours
return Okapi::formatted_response($request, $results);
}
# Backup has expired (or have never been cached). If we're on a development
# server then probably it's okay. In production this SHOULD NOT happen.
$results = array(
array(
'site_url' => Settings::get('SITE_URL'),
@ -70,7 +70,7 @@ class WebService
Cache::set($cachekey, $results, 12 * 3600); # so to retry no earlier than after 12 hours
return Okapi::formatted_response($request, $results);
}
$doc = simplexml_load_string($xml);
$results = array();
$i_was_included = false;
@ -93,10 +93,10 @@ class WebService
if ($site_url == Settings::get('SITE_URL'))
$i_was_included = true;
}
# If running on a local development installation, then include the local
# installation URL.
if (!$i_was_included)
{
$results[] = array(
@ -106,9 +106,9 @@ class WebService
);
# Contact OKAPI developers in order to get added to the official sites list!
}
# Cache it for one day. Also, save a backup (valid for 30 days).
Cache::set($cachekey, $results, 86400);
Cache::set($backupkey, $results, 86400*30);
}

View File

@ -21,7 +21,7 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
$cachekey = "apisrv/stats";

View File

@ -0,0 +1,157 @@
<?php
namespace okapi\services\attrs;
use Exception;
use ErrorException;
use okapi\Okapi;
use okapi\Settings;
use okapi\Cache;
use okapi\OkapiRequest;
use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\OkapiServiceRunner;
use okapi\OkapiInternalRequest;
use SimpleXMLElement;
class AttrHelper
{
private static $CACHE_KEY = 'attrs/attrlist/1';
private static $attr_dict = null;
private static $last_refreshed = null;
/**
* Forces the download of the new attributes from Google Code.
*/
private static function refresh_now()
{
try
{
$opts = array(
'http' => array(
'method' => "GET",
'timeout' => 5.0
)
);
$context = stream_context_create($opts);
$xml = file_get_contents("http://opencaching-api.googlecode.com/svn/trunk/etc/attributes.xml",
false, $context);
}
catch (ErrorException $e)
{
# Google failed on us. We won't update the cached attributes.
return;
}
$my_site_url = "http://opencaching.pl/"; // WRTODO
$doc = simplexml_load_string($xml);
$cachedvalue = array(
'attr_dict' => array(),
'last_refreshed' => time(),
);
foreach ($doc->attr as $attrnode)
{
$attr = array(
'code' => (string)$attrnode['okapi_attr_id'],
'gs_equiv' => null,
'internal_id' => null,
'names' => array(),
'descriptions' => array()
);
foreach ($attrnode->groundspeak as $gsnode)
{
$attr['gs_equiv'] = array(
'id' => (int)$gsnode['id'],
'inc' => in_array((string)$gsnode['inc'], array("true", "1")) ? 1 : 0,
'name' => (string)$gsnode['name']
);
}
foreach ($attrnode->opencaching as $ocnode)
{
if ((string)$ocnode['site_url'] == $my_site_url) {
$attr['internal_id'] = (int)$ocnode['id'];
}
}
foreach ($attrnode->name as $namenode)
{
$attr['names'][(string)$namenode['lang']] = (string)$namenode;
}
foreach ($attrnode->desc as $descnode)
{
$xml = $descnode->asxml(); /* contains "<desc lang="...">" and "</desc>" */
$innerxml = preg_replace("/(^[^>]+>)|(<[^<]+$)/us", "", $xml);
$attr['descriptions'][(string)$descnode['lang']] = self::cleanup_string($innerxml);
}
$cachedvalue['attr_dict'][$attr['code']] = $attr;
}
# Cache it for a month (just in case, usually it will be refreshed every day).
Cache::set(self::$CACHE_KEY, $cachedvalue, 30*86400);
self::$attr_dict = $cachedvalue['attr_dict'];
self::$last_refreshed = $cachedvalue['last_refreshed'];
}
/**
* Initialize all the internal attributes (if not yet initialized). This
* loads attribute values from the cache. If they are not present in the cache,
* it won't download them from Google Code, it will initialize them as empty!
*/
private static function init_from_cache()
{
if (self::$attr_dict !== null)
{
/* Already initialized. */
return;
}
$cachedvalue = Cache::get(self::$CACHE_KEY);
if ($cachedvalue === null)
{
$cachedvalue = array(
'attr_dict' => array(),
'last_refreshed' => 0,
);
}
self::$attr_dict = $cachedvalue['attr_dict'];
self::$last_refreshed = $cachedvalue['last_refreshed'];
}
/**
* Check if the cached attribute values might be stale. If they were not
* refreshed in a while, perform the refresh from Google Code. (This might
* take a couple of seconds, it should be done via a cronjob.)
*/
public static function refresh_if_stale()
{
self::init_from_cache();
if (self::$last_refreshed < time() - 86400)
self::refresh_now();
if (self::$last_refreshed < time() - 3 * 86400)
{
Okapi::mail_admins(
"OKAPI was unable to refresh attributes",
"OKAPI periodically refreshes all cache attributes from the list\n".
"kept in global repository. OKAPI tried to contact the repository,\n".
"but it failed. Your list of attributes might be stale.\n\n".
"You should probably update OKAPI or contact OKAPI developers."
);
}
}
/**
* Return a dictionary of all attributes. The format is the same as in the "attributes"
* key returned by the "services/attrs/attrlist" method.
*/
public static function get_attrdict()
{
self::init_from_cache();
return self::$attr_dict;
}
/** "\n\t\tBla blabla\n\t\t<b>bla</b>bla.\n\t" => "Bla blabla <b>bla</b>bla." */
private static function cleanup_string($s)
{
return preg_replace('/(^\s+)|(\s+$)/us', "", preg_replace('/\s+/us', " ", $s));
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace okapi\services\attrs\info;
use Exception;
use ErrorException;
use okapi\Okapi;
use okapi\Settings;
use okapi\Cache;
use okapi\OkapiRequest;
use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\OkapiServiceRunner;
use okapi\OkapiInternalRequest;
use okapi\services\attrs\AttrHelper;
class WebService
{
public static function options()
{
return array(
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
# The list of attributes is periodically refreshed by contacting OKAPI
# repository (the refreshing is done via a cronjob). This method
# displays the cached version of the list.
require_once 'attr_helper.inc.php';
AttrHelper::refresh_if_stale();
$results = array(
'attributes' => AttrHelper::get_attrdict()
);
return Okapi::formatted_response($request, $results);
}
}

View File

@ -0,0 +1,12 @@
<xml>
<brief>ALPHA: Get the list of all cache attributes</brief>
<issue-id>194</issue-id>
<desc>
This method is in its ALPHA stage. It's signature will most probably
change, or it might be removed altogether. You should not use it!
</desc>
<common-format-params/>
<returns>
Not yet documented. You should not use this method.
</returns>
</xml>

View File

@ -22,47 +22,47 @@ class WebService
{
private static $shutdown_function_registered = false;
private static $files_to_unlink = array();
public static function options()
{
return array(
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
$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";
$images = $request->get_parameter('images');
if (!$images) $images = "all";
if (!in_array($images, array("none", "all", "spoilers", "nonspoilers")))
throw new InvalidParam('images');
# Start creating ZIP archive.
$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
# 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(
$request->consumer, $request->token, array(
@ -81,7 +81,7 @@ class WebService
)))->get_body());
# Then, include all the images.
$caches = OkapiServiceRunner::call('services/caches/geocaches', new OkapiInternalRequest(
$request->consumer, $request->token, array('cache_codes' => $cache_codes,
'langpref' => $langpref, 'fields' => "images")));
@ -118,21 +118,21 @@ class WebService
}
if (!$tmp)
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";
}
# The safest way would be to use the URL, but that would be painfully slow!
# That's why we're trying to access files directly (and fail silently on error).
# This was tested on OCPL server only.
# Note: Oliver Dietz (oc.de) replied that images with 'local' set to 0 could not
# be accessed locally. But all the files have 'local' set to 1 anyway.
$syspath = Settings::get('IMAGES_DIR')."/".$img['uuid'].".jpg";
if (file_exists($syspath))
{
@ -142,9 +142,9 @@ class WebService
}
else
{
# If file exists, but does not end with ".jpg", we will create
# If file exists, but does not end with ".jpg", we will create
# JPEG version of it and store it in the cache.
$cache_key = "jpg#".$img['uuid'];
$jpeg_contents = Cache::get($cache_key);
if ($jpeg_contents === null)
@ -167,7 +167,7 @@ class WebService
# GD couldn't parse the file. We will skip it, and cache
# the "false" value as the contents. This way, we won't
# attempt to parse it during the next 24 hours.
$jpeg_contents = false;
}
Cache::set($cache_key, $jpeg_contents, 86400);
@ -181,15 +181,15 @@ class WebService
}
}
}
$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
# what is the PHP's default way of handling such scenario).
set_time_limit(600);
$response = new OkapiHttpResponse();
$response->content_type = "application/zip";
@ -200,14 +200,14 @@ class WebService
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)

View File

@ -13,6 +13,7 @@ use okapi\OkapiAccessToken;
use okapi\InvalidParam;
use okapi\services\caches\search\SearchAssistant;
use okapi\OkapiInternalConsumer;
use okapi\Db;
class WebService
{
@ -35,7 +36,7 @@ class WebService
'Own' => 'Unknown Cache',
'Other' => 'Unknown Cache'
);
/** Maps OKAPI's 'size2' values to geocaching.com size codes. */
public static $cache_GPX_sizes = array(
'none' => 'Virtual',
@ -47,20 +48,20 @@ class WebService
'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)
@ -73,7 +74,7 @@ class WebService
}
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');
if (!$tmp) $tmp = "none";
if (!in_array($tmp, array("none", "desc:text")))
@ -81,39 +82,39 @@ class WebService
if (($tmp != 'none') && ($request->token == null))
throw new BadRequest("Level 3 Authentication is required to access my_notes data.");
$vars['my_notes'] = $tmp;
$images = $request->get_parameter('images');
if (!$images) $images = 'descrefs:nonspoilers';
if (!in_array($images, array('none', 'descrefs:nonspoilers', 'descrefs:all', 'ox:all')))
throw new InvalidParam('images', "'$images'");
$vars['images'] = $images;
$tmp = $request->get_parameter('attrs');
if (!$tmp) $tmp = 'desc:text';
if (!in_array($tmp, array('none', 'desc:text', 'ox:tags')))
throw new InvalidParam('attrs', "'$tmp'");
$vars['attrs'] = $tmp;
$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');
# We can get all the data we need from the services/caches/geocaches method.
# We don't need to do any additional queries here.
$fields = 'code|name|location|date_created|url|type|status|size|size2|oxsize'.
'|difficulty|terrain|description|hint|rating|owner|url|internal_id';
if ($vars['images'] != 'none')
@ -134,15 +135,44 @@ class WebService
$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)));
'langpref' => $langpref, 'fields' => $fields, 'lpc' => $lpc, 'user_uuid' => $user_uuid,
'log_fields' => 'uuid|date|user|type|comment|internal_id|was_recommended')));
$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;
/* 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 = "text/xml; charset=utf-8";
$response->content_disposition = 'attachment; filename="results.gpx"';

View File

@ -1,7 +1,7 @@
<?
namespace okapi\services\caches\formatters\gpx;
namespace okapi\services\caches\formatters\gpx;
use okapi\Okapi;
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
@ -12,12 +12,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd
http://www.opencaching.com/xmlschemas/opencaching/1/0 http://www.opencaching.com/xmlschemas/opencaching/1/0/opencaching.xsd
http://www.groundspeak.com/cache/1/0 http://www.groundspeak.com/cache/1/0/cache.xsd
http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd
http://geocaching.com.au/geocache/1 http://geocaching.com.au/geocache/1/geocache.xsd
http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
">
<name><?= $vars['installation']['site_name'] ?> Geocache Search Results</name>
<desc><?= $vars['installation']['site_name'] ?> Geocache Search Results, downloaded via OKAPI - <?= $vars['installation']['okapi_base_url'] ?></desc>
<desc><?= $vars['installation']['site_name'] ?> Geocache Search Results, downloaded via OKAPI - <?= $vars['installation']['okapi_base_url'] . ($vars['alt_wpts'] && $vars['ns_gsak'] ? ' (HasChildren)' : '') ?></desc>
<author><?= $vars['installation']['site_name'] ?></author>
<url><?= $vars['installation']['site_url'] ?></url>
<urlname><?= $vars['installation']['site_name'] ?></urlname>
@ -37,7 +37,7 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
<groundspeak:cache archived="<?= ($c['status'] == 'Archived') ? "True" : "False" ?>" available="<?= ($c['status'] == 'Available') ? "True" : "False" ?>" id="<?= $c['internal_id'] ?>" xmlns:groundspeak="http://www.groundspeak.com/cache/1/0/1">
<groundspeak:name><?= Okapi::xmlescape($c['name']) ?></groundspeak:name>
<groundspeak:placed_by><?= Okapi::xmlescape($c['owner']['username']) ?></groundspeak:placed_by>
<groundspeak:owner id="<?= $c['owner']['uuid'] ?>"><?= Okapi::xmlescape($c['owner']['username']) ?></groundspeak:owner>
<groundspeak:owner id="<?= $vars['user_uuid_to_internal_id'][$c['owner']['uuid']] ?>"><?= Okapi::xmlescape($c['owner']['username']) ?></groundspeak:owner>
<groundspeak:type><?= $vars['cache_GPX_types'][$c['type']] ?></groundspeak:type>
<groundspeak:container><?= $vars['cache_GPX_sizes'][$c['size2']] ?></groundspeak:container>
<groundspeak:difficulty><?= $c['difficulty'] ?></groundspeak:difficulty>
@ -57,7 +57,7 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
<? if (($vars['my_notes'] == 'desc:text') && ($c['my_notes'] != null)) { /* Does user want us to include personal notes? */ ?>
&lt;p&gt;&lt;b&gt;<?= _("Personal notes") ?>:&lt;/b&gt; <?= Okapi::xmlescape($c['my_notes']) ?>&lt;/p&gt;
<? } ?>
<? if ($vars['attrs'] == 'desc:text' && count($c['attrnames']) > 0) { /* Does user want us to include attributes? */ ?>
&lt;p&gt;<?= _("Attributes") ?>:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;<?= implode("&lt;/li&gt;&lt;li&gt;", $c['attrnames']) ?>&lt;/li&gt;&lt;/ul&gt;
@ -108,11 +108,11 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
<? if ($vars['latest_logs']) { /* Does user want us to include latest log entries? */ ?>
<groundspeak:logs>
<? foreach ($c['latest_logs'] as $log) { ?>
<groundspeak:log id="<?= $log['uuid'] ?>">
<groundspeak:log id="<?= $log['internal_id'] ?>">
<groundspeak:date><?= $log['date'] ?></groundspeak:date>
<groundspeak:type><?= $log['type'] ?></groundspeak:type>
<groundspeak:finder id="<?= $log['user']['uuid'] ?>"><?= Okapi::xmlescape($log['user']['username']) ?></groundspeak:finder>
<groundspeak:text encoded="False"><?= Okapi::xmlescape($log['comment']) ?></groundspeak:text>
<groundspeak:finder id="<?= $vars['user_uuid_to_internal_id'][$log['user']['uuid']] ?>"><?= Okapi::xmlescape($log['user']['username']) ?></groundspeak:finder>
<groundspeak:text encoded="False"><?= $log['was_recommended'] ? "(*) ": "" ?><?= Okapi::xmlescape($log['comment']) ?></groundspeak:text>
</groundspeak:log>
<? } ?>
</groundspeak:logs>
@ -162,9 +162,9 @@ http://www.gsak.net/xmlv1/5 http://www.gsak.net/xmlv1/5/gsak.xsd
<sym><?= $wpt['sym'] ?></sym>
<type>Waypoint|<?= $wpt['sym'] ?></type>
<? if ($vars['ns_gsak']) { ?>
<wptExtension xmlns="http://www.gsak.net/xmlv1/5">
<Parent><?= $c['code'] ?></Parent>
</wptExtension>
<gsak:wptExtension xmlns:gsak="http://www.gsak.net/xmlv1/5">
<gsak:Parent><?= $c['code'] ?></gsak:Parent>
</gsak:wptExtension>
<? } ?>
</wpt>
<? } ?>

View File

@ -28,13 +28,16 @@ class WebService
if (!$langpref) $langpref = "en";
$fields = $request->get_parameter('fields');
if (!$fields) $fields = "code|name|location|type|status";
$log_fields = $request->get_parameter('log_fields');
if (!$log_fields) $log_fields = "uuid|date|user|type|comment";
$lpc = $request->get_parameter('lpc');
if (!$lpc) $lpc = 10;
$params = array(
'cache_codes' => $cache_code,
'langpref' => $langpref,
'fields' => $fields,
'lpc' => $lpc
'lpc' => $lpc,
'log_fields' => $log_fields
);
$my_location = $request->get_parameter('my_location');
if ($my_location)
@ -42,10 +45,10 @@ class WebService
$user_uuid = $request->get_parameter('user_uuid');
if ($user_uuid)
$params['user_uuid'] = $user_uuid;
# There's no need to validate the fields/lpc parameters as the 'geocaches'
# method does this (it will raise a proper exception on invalid values).
$results = OkapiServiceRunner::call('services/caches/geocaches', new OkapiInternalRequest(
$request->consumer, $request->token, $params));
$result = $results[$cache_code];

View File

@ -127,7 +127,7 @@
<b>null</b> if geocache does not have a container,
</li>
<li>
<p><b>size2</b> - string indicating the size od the container, so called
<p><b>size2</b> - string indicating the size of the container, so called
"size2 code". One of the following values:
'none', 'nano', 'micro', 'small', 'regular', 'large', 'xlarge', 'other'.</p>
</li>
@ -170,18 +170,24 @@
<ul>
<li><b>uuid</b> - UUID of the image,</li>
<li><b>url</b> - URL of the image,</li>
<li><b>thumb_url</b> - URL of a small (thumb) version of the image
<b>or null</b> when no thumb is available (e.g. there are no thumbs
for spoiler images),</li>
<li><b>thumb_url</b> - URL of a small (thumb) version of the image,</li>
<li><b>caption</b> - plain-text string, caption of the image,</li>
<li><b>unique_caption</b> - plain-text string, to be used as a filename
for Garmin's crappy images implementation (currently, they get image
caption from the image's filename itself),</li>
<li><b>is_spoiler</b> - boolean, if <b>true</b> then the image is
a spoiler image and should not be displayed to the user unless
the user explicitly asks for it.</li>
the user explicitly asks for it,</li>
</ul>
</li>
<li>
<p><b>preview_image</b> - This is either <b>null</b> or a dictionary describing
an image, which has been marked by the owner as <em>preview image</em>. You are
encouraged to display it as a 'teaser' for this cache.
The structure of the dictionary is the same as in the <b>images</b> field above.</p>
<p>The preview image is no additional image but one of those which
are included in the <b>images</b> list.</p>
</li>
<li>
<p><b>attrnames</b> - list of names of attributes of the cache; the language will
be selected based on the <b>langpref</b> argument.</p>
@ -216,7 +222,7 @@
<p><b>alt_wpts</b> - list of alternate/additional waypoints associated
with this geocache. Each item is a dictionary of the following structure:</p>
<ul>
<li><b>name</b> - plain-text "codename" for the waypoint (not unique),</li>
<li><b>name</b> - plain-text, short, unique "codename" for the waypoint,</li>
<li><b>location</b> - location of the waypoint in the "lat|lon" format
(<i>lat</i> and <i>lon</i> are in full degrees with a dot as a decimal point),</li>
<li>
@ -267,6 +273,10 @@
This should be an integer or a special "all" value. Please note, that you must include
the <b>latest_logs</b> field in <b>fields</b> in order to see the log entries.
</opt>
<opt name='log_fields' default='uuid|date|user|type|comment'>
Pipe-separated list of log fields to include in the <b>latest_logs</b> field.
For valid field names, see <b>logs/entry</b> method.
</opt>
<opt name='my_location'>
<p>The reference point for cache distance and bearing calculation (typically - the user's location),
in the "lat|lon" format. This parameter is required when accessing <b>distance</b> and/or <b>bearing</b>

View File

@ -31,7 +31,7 @@ class WebService
'descriptions', 'hint', 'hints', 'images', 'attrnames', 'latest_logs',
'my_notes', 'trackables_count', 'trackables', 'alt_wpts', 'last_found',
'last_modified', 'date_created', 'date_hidden', 'internal_id', 'is_watched',
'is_ignored', 'willattends', 'country', 'state');
'is_ignored', 'willattends', 'country', 'state', 'preview_image');
public static function call(OkapiRequest $request)
{
@ -63,6 +63,20 @@ class WebService
if (!in_array($field, self::$valid_field_names))
throw new InvalidParam('fields', "'$field' is not a valid field code.");
# Currently, the "owner" field needs to be included whenever the "description" field is.
# That's a little ugly. Grep for "issue 178" below for more insight on this.
if (
(
in_array('description', $fields) || in_array('descriptions', $fields)
|| in_array('hint', $fields) || in_array('hints', $fields)
)
&& !in_array('owner', $fields)
)
$fields[] = "owner";
$log_fields = $request->get_parameter('log_fields');
if (!$log_fields) $log_fields = "uuid|date|user|type|comment"; // validation is done on call
$user_uuid = $request->get_parameter('user_uuid');
if ($user_uuid != null)
{
@ -120,7 +134,7 @@ class WebService
$rs = Db::query("
select
c.cache_id, c.name, c.longitude, c.latitude, c.last_modified,
c.cache_id, c.name, c.longitude, c.latitude, c.listing_last_modified as last_modified,
c.date_created, c.type, c.status, c.date_hidden, c.size, c.difficulty,
c.terrain, c.wp_oc, c.logpw, c.user_id,
@ -258,6 +272,7 @@ class WebService
case 'hint': /* handled separately */ break;
case 'hints': /* handled separately */ break;
case 'images': /* handled separately */ break;
case 'preview_image': /* handled separately */ break;
case 'attrnames': /* handled separately */ break;
case 'latest_logs': /* handled separately */ break;
case 'my_notes': /* handles separately */ break;
@ -421,8 +436,19 @@ class WebService
$cache_code = $cacheid2wptcode[$row['cache_id']];
// strtolower - ISO 639-1 codes are lowercase
if ($row['desc'])
$results[$cache_code]['descriptions'][strtolower($row['language'])] = $row['desc'].
"\n".self::get_cache_attribution_note($row['cache_id'], strtolower($row['language']));
{
/* Regarding the attribution note - please note, that the "owner" field
* is automatically included, whenever the cache description is included.
* This is because we may need it for the attribution note - see issue 178. */
$results[$cache_code]['descriptions'][strtolower($row['language'])] = (
$row['desc']."\n".
self::get_cache_attribution_note(
$row['cache_id'], strtolower($row['language']), $langpref,
$results[$cache_code]['owner']
)
);
}
if ($row['hint'])
$results[$cache_code]['hints'][strtolower($row['language'])] = $row['hint'];
}
@ -442,19 +468,28 @@ class WebService
# Images.
if (in_array('images', $fields))
if (in_array('images', $fields) || in_array('preview_image', $fields))
{
foreach ($results as &$result_ref)
$result_ref['images'] = array();
if (in_array('images', $fields))
foreach ($results as &$result_ref)
$result_ref['images'] = array();
if (in_array('preview_image', $fields))
foreach ($results as &$result_ref)
$result_ref['preview_image'] = null;
if (Db::field_exists('pictures', 'mappreview'))
$preview_field = "mappreview";
else
$preview_field = "0";
$rs = Db::query("
select object_id, uuid, url, thumb_url, title, spoiler
select object_id, uuid, url, title, spoiler, ".$preview_field." as preview
from pictures
where
object_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($cacheid2wptcode)))."')
and display = 1
and object_type = 2
and unknown_format = 0
order by object_id, last_modified
order by object_id, date_created
");
$prev_cache_code = null;
while ($row = mysql_fetch_assoc($rs))
@ -466,14 +501,18 @@ class WebService
self::reset_unique_captions();
$prev_cache_code = $cache_code;
}
$results[$cache_code]['images'][] = array(
$image = array(
'uuid' => $row['uuid'],
'url' => $row['url'],
'thumb_url' => $row['thumb_url'] ? $row['thumb_url'] : null,
'thumb_url' => Settings::get('SITE_URL') . 'thumbs.php?uuid=' . $row['uuid'],
'caption' => $row['title'],
'unique_caption' => self::get_unique_caption($row['title']),
'is_spoiler' => ($row['spoiler'] ? true : false),
);
if (in_array('images', $fields))
$results[$cache_code]['images'][] = $image;
if ($row['preview'] != 0 && in_array('preview_image', $fields))
$results[$cache_code]['preview_image'] = $image;
}
}
@ -517,13 +556,15 @@ class WebService
# technique I could think of...
$rs = Db::query("
select cache_id, id, date
select cache_id, uuid, date
from cache_logs
where
cache_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($cacheid2wptcode)))."')
and ".((Settings::get('OC_BRANCH') == 'oc.pl') ? "deleted = 0" : "true")."
order by cache_id, date desc
");
$logids = array();
$loguuids = array();
$log2cache_map = array();
if ($lpc !== null)
{
# User wants some of the latest logs.
@ -538,7 +579,8 @@ class WebService
});
for ($i = 0; $i < min(count($rowslist_ref), $lpc); $i++)
{
$logids[] = $rowslist_ref[$i]['id'];
$loguuids[] = $rowslist_ref[$i]['uuid'];
$log2cache_map[$rowslist_ref[$i]['uuid']] = $cacheid2wptcode[$rowslist_ref[$i]['cache_id']];
}
}
}
@ -546,35 +588,46 @@ class WebService
{
# User wants ALL logs.
while ($row = mysql_fetch_assoc($rs))
$logids[] = $row['id'];
{
$loguuids[] = $row['uuid'];
$log2cache_map[$row['uuid']] = $cacheid2wptcode[$row['cache_id']];
}
}
# Now retrieve text and join.
# We need to retrieve logs/entry for each of the $logids. We do this in groups
# (there is a limit for log uuids passed to logs/entries method).
$rs = Db::query("
select cl.cache_id, cl.id, cl.uuid, cl.type, unix_timestamp(cl.date) as date, cl.text,
u.uuid as user_uuid, u.username, u.user_id
from cache_logs cl, user u
where
cl.id in ('".implode("','", array_map('mysql_real_escape_string', $logids))."')
and ".((Settings::get('OC_BRANCH') == 'oc.pl') ? "cl.deleted = 0" : "true")."
and cl.user_id = u.user_id
order by cl.cache_id, cl.date desc
");
$cachelogs = array();
while ($row = mysql_fetch_assoc($rs))
try
{
$results[$cacheid2wptcode[$row['cache_id']]]['latest_logs'][] = array(
'uuid' => $row['uuid'],
'date' => date('c', $row['date']),
'user' => array(
'uuid' => $row['user_uuid'],
'username' => $row['username'],
'profile_url' => Settings::get('SITE_URL')."viewprofile.php?userid=".$row['user_id'],
),
'type' => Okapi::logtypeid2name($row['type']),
'comment' => $row['text']
);
foreach (Okapi::make_groups($loguuids, 500) as $subset)
{
$entries = OkapiServiceRunner::call(
"services/logs/entries",
new OkapiInternalRequest(
$request->consumer, $request->token, array(
'log_uuids' => implode("|", $subset),
'fields' => $log_fields
)
)
);
foreach ($subset as $log_uuid)
{
if ($entries[$log_uuid])
$results[$log2cache_map[$log_uuid]]['latest_logs'][] = $entries[$log_uuid];
}
}
}
catch (Exception $e)
{
if (($e instanceof InvalidParam) && ($e->paramName == 'fields'))
{
throw new InvalidParam('log_fields', $e->whats_wrong_about_it);
}
else
{
/* Something is wrong with OUR code. */
throw new Exception($e);
}
}
}
@ -692,6 +745,8 @@ class WebService
{
foreach ($results as &$result_ref)
$result_ref['alt_wpts'] = array();
$cache_codes_escaped_and_imploded = "'".implode("','", array_map('mysql_real_escape_string', array_keys($cacheid2wptcode)))."'";
if (Settings::get('OC_BRANCH') == 'oc.pl')
{
# OCPL uses 'waypoints' table to store additional waypoints. OCPL also have
@ -699,7 +754,7 @@ class WebService
# of a multicache). Such hidden waypoints are not exposed by OKAPI. A stage
# fields is used for ordering and naming.
$rs = Db::query("
$waypoints = Db::select_all("
select
cache_id, stage, latitude, longitude, `desc`,
case type
@ -710,7 +765,7 @@ class WebService
end as sym
from waypoints
where
cache_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($cacheid2wptcode)))."')
cache_id in (".$cache_codes_escaped_and_imploded.")
and status = 1
order by cache_id, stage, `desc`
");
@ -720,27 +775,32 @@ class WebService
# OCDE uses 'coordinates' table (with type=1) to store additional waypoints.
# All waypoints are are public.
$rs = Db::query("
$waypoints = Db::select_all("
select
cache_id,
null as stage,
@stage := @stage + 1 as stage,
latitude, longitude,
description as `desc`,
case subtype
when 1 then 'Parking Area'
when 3 then 'Flag, Blue'
when 4 then 'Circle with X'
when 5 then 'Diamond, Green'
else 'Flag, Green'
end as sym
from coordinates
join (select @stage := 0) s
where
type = 1
and cache_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($cacheid2wptcode)))."')
order by cache_id, `desc`
and cache_id in (".$cache_codes_escaped_and_imploded.")
order by cache_id, id, `desc`
");
}
while ($row = mysql_fetch_assoc($rs))
$wpt_format = "%s-%0".strlen(count($waypoints))."d";
foreach ($waypoints as $index => $row)
{
$results[$cacheid2wptcode[$row['cache_id']]]['alt_wpts'][] = array(
'name' => $cacheid2wptcode[$row['cache_id']]."-".($row['stage'] ? $row['stage'] : "wpt"),
'name' => sprintf($wpt_format, $cacheid2wptcode[$row['cache_id']], $index + 1),
'location' => round($row['latitude'], 6)."|".round($row['longitude'], 6),
'sym' => $row['sym'],
'description' => ($row['stage'] ? _("Stage")." ".$row['stage'].": " : "").$row['desc'],
@ -748,15 +808,19 @@ class WebService
}
}
# Country and/or state.
# Country and/or state.
if (Settings::get('OC_BRANCH') == 'oc.de')
$cache_location_state = 'adm2';
else
$cache_location_state = 'adm3';
if (in_array('country', $fields) || in_array('state', $fields))
{
$rs = Db::query("
select
c.wp_oc as cache_code,
cl.adm1 as country,
cl.adm3 as state
cl.".$cache_location_state." as state
from
caches c,
cache_location cl
@ -845,22 +909,54 @@ class WebService
self::$caption_no = 1;
}
public static function get_cache_attribution_note($cache_id, $lang)
/**
* Return attribution note to be included in the cache description.
*
* The $lang parameter identifies the language of the cache description
* to which the attribution note will be appended to (one cache may
* have descriptions in multiple languages!).
*
* The $langpref parameter is *an array* of language preferences
* extracted from the langpref parameter passed to the method by the
* OKAPI Consumer.
*
* Both values ($lang and $langpref) will be taken into account when
* generating the attribution note, but $lang will have a higher
* priority than $langpref (we don't want to mix the languages in the
* descriptions if we don't have to).
*
* $owner is in object describing the user, it has the same format as
* defined in "geocache" method specs (see the "owner" field).
*/
public static function get_cache_attribution_note($cache_id, $lang, array $langpref, $owner)
{
$site_url = Settings::get('SITE_URL');
$site_name = Okapi::get_normalized_site_name();
$cache_url = $site_url."viewcache.php?cacheid=$cache_id";
# This list if to be extended (opencaching.de, etc.). (_)
switch ($lang)
Okapi::gettext_domain_init(array_merge(array($lang), $langpref));
$note = "<p>";
if (Settings::get('OC_BRANCH') == 'oc.de')
{
case 'pl':
return "<p>Opis <a href='$cache_url'>skrzynki</a> pochodzi z serwisu <a href='$site_url'>$site_name</a>.</p>";
break;
default:
return "<p>This <a href='$cache_url'>geocache</a> description comes from the <a href='$site_url'>$site_name</a> site.</p>";
break;
$note .= sprintf(
_(
"<em>&copy; <a href='%s'>%s</a>, <a href='%s'>%s</a>, ".
"<a href='http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.en'>CC-BY-NC-ND</a>, ".
"as of %s; all log entries &copy; their authors</em>"
),
$owner['profile_url'], $owner['username'], $cache_url, $site_name, strftime('%x')
);
}
else
{
$note .= sprintf(
_("This <a href='%s'>geocache</a> description comes from the <a href='%s'>%s</a> site."),
$cache_url, $site_url, $site_name
);
}
$note .= "</p>";
Okapi::gettext_domain_restore();
return $note;
}
}

View File

@ -28,6 +28,9 @@
<opt name='lpc' default='10'>
Same as in the services/caches/geocache method.
</opt>
<opt name='log_fields' default='uuid|date|user|type|comment'>
Same as in the services/caches/geocache method.
</opt>
<opt name='user_uuid'>
Same as in the services/caches/geocache method.
</opt>

View File

@ -28,7 +28,7 @@ class ReplicateListener
# This will be called every time new items arrive from replicate module's
# changelog. The format of $changelog is described in the replicate module
# (NOT the entire response, just the "changelog" key).
foreach ($changelog as $c)
{
if ($c['object_type'] == 'geocache')
@ -40,38 +40,22 @@ class ReplicateListener
}
}
}
public static function reset($mail_admins = true)
{
# This will be called when there are "too many" entries in the changelog
# and the replicate module thinks it better to just reset the entire TileTree.
# For the first hours after such reset maps may work very slow!
if ($mail_admins)
{
Okapi::mail_admins("OKAPI TileMap database reset",
"Hello,\n\n".
"OKAPI's 'replicate' module detected a big database update. As result\n".
"of this, OKAPI decided to reset the TileMap cache. This may\n".
"temporarily influence TileMap performance. The map may work much\n".
"slower during the next few hours or days, while the cache is being\n".
"rebuilt.\n\n".
"If this happens frequently, please contact OKAPI developers. It may\n".
"indicate a bug in OKAPI's 'replicate' module or cronjob settings.\n\n".
"Thanks!\n\n".
"P.S. This may also happen if you didn't run OKAPI on this server\n".
"for a while (your server was down or OKAPI didn't work properly)."
);
}
# For the first hours after such reset maps may work a little slower.
Db::execute("delete from okapi_tile_status");
Db::execute("delete from okapi_tile_caches");
}
private static function handle_geocache_replace($c)
{
# Check if any relevant geocache attributes have changed.
# We will pick up "our" copy of the cache from zero-zoom level.
try {
$cache = OkapiServiceRunner::call("services/caches/geocache", new OkapiInternalRequest(new OkapiInternalConsumer(), null, array(
'cache_code' => $c['object_key']['code'],
@ -81,7 +65,7 @@ class ReplicateListener
# Unprobable, but possible. Ignore changelog entry.
return;
}
$theirs = TileTree::generate_short_row($cache);
$ours = mysql_fetch_row(Db::query("
select cache_id, z21x, z21y, status, type, rating, flags
@ -93,13 +77,13 @@ class ReplicateListener
if (!$ours)
{
# Aaah, a new geocache! How nice... ;)
self::add_geocache_to_cached_tiles($theirs);
}
elseif (($ours[1] != $theirs[1]) || ($ours[2] != $theirs[2])) # z21x & z21y fields
{
# Location changed.
self::remove_geocache_from_cached_tiles($ours[0]);
self::add_geocache_to_cached_tiles($theirs);
}
@ -114,23 +98,23 @@ class ReplicateListener
# many updates which do not influence our cache.
}
}
private static function remove_geocache_from_cached_tiles($cache_id)
{
# Simply remove all traces of this geocache from all tiles.
# This includes all references along tiles' borders, etc.
Db::execute("
delete from okapi_tile_caches
where cache_id = '".mysql_real_escape_string($cache_id)."'
");
# Note, that after this operation, okapi_tile_status may be out-of-date.
# There might exist some rows with status==2, but they should be in status==1.
# Currently, we can ignore this, because status==1 is just a shortcut to
# avoid making unnecessary queries.
}
private static function add_geocache_to_cached_tiles(&$row)
{
# This one is the most complicated. We need to identify all tiles
@ -138,11 +122,11 @@ class ReplicateListener
# tiles (one per each zoom level), *and* all "just outside the border"
# tiles (one geocache can be present in up to 4 tiles per zoom level).
# This gives us max. 88 tiles to add the geocache to.
$tiles_to_update = array();
# We will begin at zoom 21 and then go down to zoom 0.
$z21x = $row[1];
$z21y = $row[2];
$ex = $z21x >> 8; # initially, z21x / <tile width>
@ -152,25 +136,25 @@ class ReplicateListener
# ($ex, $ey) points to the "exact match" tile. We need to determine
# tile-range to check for "just outside the border" tiles. We will
# go with the simple approach and check all 1+8 bordering tiles.
$tiles_in_this_region = array();
for ($x=$ex-1; $x<=$ex+1; $x++)
for ($y=$ey-1; $y<=$ey+1; $y++)
if (($x >= 0) && ($x < 1<<$zoom) && ($y >= 0) && ($y < 1<<$zoom))
$tiles_in_this_region[] = array($x, $y);
foreach ($tiles_in_this_region as $coords)
{
list($x, $y) = $coords;
$scale = 8 + 21 - $zoom;
$margin = 1 << ($scale - 3); # 32px of current $zoom level, measured in z21 pixels.
$left_z21x = ($x << $scale) - $margin;
$right_z21x = (($x + 1) << $scale) + $margin;
$top_z21y = ($y << $scale) - $margin;
$bottom_z21y = (($y + 1) << $scale) + $margin;
if ($z21x < $left_z21x)
continue;
if ($z21x > $right_z21x)
@ -179,17 +163,17 @@ class ReplicateListener
continue;
if ($z21y > $bottom_z21y)
continue;
# We found a match. Store it for later.
$tiles_to_update[] = array($zoom, $x, $y);
}
}
# We have a list of all possible tiles that need updating.
# Most of these tiles aren't cached at all. We need to update
# only the cached ones.
$alternatives = array();
foreach ($tiles_to_update as $coords)
{
@ -200,45 +184,48 @@ class ReplicateListener
and y = '".mysql_real_escape_string($y)."'
)";
}
Db::execute("
replace into okapi_tile_caches (
z, x, y, cache_id, z21x, z21y, status, type, rating, flags
)
select
z, x, y,
'".mysql_real_escape_string($row[0])."',
'".mysql_real_escape_string($row[1])."',
'".mysql_real_escape_string($row[2])."',
'".mysql_real_escape_string($row[3])."',
'".mysql_real_escape_string($row[4])."',
".(($row[5] === null) ? "null" : $row[5]).",
'".mysql_real_escape_string($row[6])."'
from okapi_tile_status
where
(".implode(" or ", $alternatives).")
and status in (1,2)
");
# We might have just filled some empty tiles (status 1) with data.
# We need to update their status to 2.
Db::execute("
update okapi_tile_status
set status=2
where
(".implode(" or ", $alternatives).")
and status=1
");
if (count($alternatives) > 0)
{
Db::execute("
replace into okapi_tile_caches (
z, x, y, cache_id, z21x, z21y, status, type, rating, flags
)
select
z, x, y,
'".mysql_real_escape_string($row[0])."',
'".mysql_real_escape_string($row[1])."',
'".mysql_real_escape_string($row[2])."',
'".mysql_real_escape_string($row[3])."',
'".mysql_real_escape_string($row[4])."',
".(($row[5] === null) ? "null" : $row[5]).",
'".mysql_real_escape_string($row[6])."'
from okapi_tile_status
where
(".implode(" or ", $alternatives).")
and status in (1,2)
");
# We might have just filled some empty tiles (status 1) with data.
# We need to update their status to 2.
Db::execute("
update okapi_tile_status
set status=2
where
(".implode(" or ", $alternatives).")
and status=1
");
}
# And that's all. That should do the trick.
}
private static function update_geocache_attributes_in_cached_tiles(&$row)
{
# Update all attributes (for all levels). Note, that we don't need to
# update location ($row[1] and $row[2]) - this method is called ONLY
# when location stayed untouched!
Db::execute("
update okapi_tile_caches
set
@ -250,11 +237,11 @@ class ReplicateListener
cache_id = '".mysql_real_escape_string($row[0])."'
");
}
private static function handle_geocache_delete($c)
{
# Simply delete the cache at all zoom levels.
$cache_id = Db::select_value("
select cache_id
from caches

View File

@ -34,26 +34,26 @@ class WebService
* testing/debugging the tile renderer.
*/
private static $USE_ETAGS_CACHE = true;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging the tile renderer.
*/
private static $USE_IMAGE_CACHE = true;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging. Grep the code to check when this flag is used.
*/
private static $USE_OTHER_CACHE = true;
public static function options()
{
return array(
'min_auth_level' => 3
);
}
private static function require_uint($request, $name, $min_value = 0)
{
$val = $request->get_parameter($name);
@ -64,18 +64,18 @@ class WebService
throw new InvalidParam($name, "Expecting non-negative integer.");
return $ret;
}
public static function call(OkapiRequest $request)
{
$checkpointA_started = microtime(true);
# Make sure the request is internal.
if (!in_array($request->consumer->key, array('internal', 'facade')))
throw new BadRequest("Your Consumer Key has not been allowed to access this method.");
# zoom, x, y - required tile-specific parameters.
$zoom = self::require_uint($request, 'z');
if ($zoom > 21)
throw new InvalidParam('z', "Maximum value for this parameter is 21.");
@ -85,18 +85,18 @@ class WebService
throw new InvalidParam('x', "Should be in 0..".((1<<$zoom) - 1).".");
if ($y >= 1<<$zoom)
throw new InvalidParam('y', "Should be in 0..".((1<<$zoom) - 1).".");
# Now, we will create a search set (or use one previously created).
# Instead of creating a new OkapiInternalRequest object, we will pass
# the current request directly. We can do that, because we inherit all
# of the "save" method's parameters.
$search_set = OkapiServiceRunner::call('services/caches/search/save', $request);
$set_id = $search_set['set_id'];
# Get caches which are present in the result set AND within the tile
# (+ those around the borders).
$rs = TileTree::query_fast($zoom, $x, $y, $set_id);
$rows = array();
if ($rs !== null)
@ -110,19 +110,19 @@ class WebService
$checkpointB_started = microtime(true);
# Add dynamic, user-related flags.
if (count($rows) > 0)
{
# Load user-related cache ids.
$cache_key = "tileuser/".$request->token->user_id;
$user = self::$USE_OTHER_CACHE ? Cache::get($cache_key) : null;
if ($user === null)
{
$user = array();
# Ignored caches.
$rs = Db::query("
select cache_id
from cache_ignore
@ -131,9 +131,9 @@ class WebService
$user['ignored'] = array();
while (list($cache_id) = mysql_fetch_row($rs))
$user['ignored'][$cache_id] = true;
# Found caches.
$rs = Db::query("
select distinct cache_id
from cache_logs
@ -147,7 +147,7 @@ class WebService
$user['found'][$cache_id] = true;
# Own caches.
$rs = Db::query("
select distinct cache_id
from caches
@ -156,12 +156,12 @@ class WebService
$user['own'] = array();
while (list($cache_id) = mysql_fetch_row($rs))
$user['own'][$cache_id] = true;
Cache::set($cache_key, $user, 30);
}
# Add extra flags to geocaches.
foreach ($rows as &$row_ref)
{
# Add the "found" flag (to indicate that this cache needs
@ -174,36 +174,36 @@ class WebService
$row_ref[6] |= TileTree::$FLAG_OWN; # $row[6] is "flags"
}
}
# Compute the image hash/fingerprint. This will be used both for ETags
# and internal cache ($cache_key).
$tile = new DefaultTileRenderer($zoom, $rows);
$image_fingerprint = $tile->get_unique_hash();
# Start creating response.
$response = new OkapiHttpResponse();
$response->content_type = $tile->get_content_type();
$response->cache_control = "Cache-Control: private, max-age=600";
$response->etag = 'W/"'.$image_fingerprint.'"';
# Check if the request didn't include the same ETag.
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointB", null,
microtime(true) - $checkpointB_started);
$checkpointC_started = microtime(true);
if (self::$USE_ETAGS_CACHE && ($request->etag == $response->etag))
{
# Hit. Report the content was unmodified.
$response->etag = null;
$response->status = "304 Not Modified";
return $response;
}
# Check if the image was recently rendered and is kept in image cache.
$cache_key = "tile/".$image_fingerprint;
$response->body = self::$USE_IMAGE_CACHE ? Cache::get($cache_key) : null;
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointC", null,
@ -212,12 +212,12 @@ class WebService
if ($response->body !== null)
{
# Hit. We will use the cached version of the image.
return $response;
}
# Miss. Render the image. Cache the result.
$response->body = $tile->render();
Cache::set_scored($cache_key, $response->body);
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointD", null,

View File

@ -17,10 +17,10 @@ interface TileRenderer
* throw an Exception on every subsequent call.
*/
public function get_unique_hash();
/** Get the content type of the data returned by the render method. */
public function get_content_type();
/**
* Render the image. This function will be called only once, after calling
* get_unique_hash method.
@ -35,23 +35,23 @@ class DefaultTileRenderer implements TileRenderer
* whenever you alter anything in the drawing algorithm.
*/
private static $VERSION = 56;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging a new set of static icons.
*/
private static $USE_STATIC_IMAGE_CACHE = true;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging a new captions renderer.
*/
private static $USE_CAPTIONS_CACHE = true;
private $zoom;
private $rows_ref;
private $im;
/**
* Takes the zoom level and the list of geocache descriptions. Note, that
* $rows_ref can be altered after calling render. If you don't want it to,
@ -62,7 +62,7 @@ class DefaultTileRenderer implements TileRenderer
$this->zoom = $zoom;
$this->rows_ref = &$rows_ref;
}
public function get_unique_hash()
{
return md5(json_encode(array(
@ -72,21 +72,21 @@ class DefaultTileRenderer implements TileRenderer
$this->rows_ref
)));
}
public function get_content_type()
{
return "image/png";
}
public function render()
{
# Preprocess the rows.
if ($this->zoom >= 5)
$this->decide_which_get_captions();
# Make a background.
$this->im = imagecreatetruecolor(256, 256);
imagealphablending($this->im, false);
if ($this->zoom >= 13) $opacity = 15;
@ -94,33 +94,33 @@ class DefaultTileRenderer implements TileRenderer
$transparent = imagecolorallocatealpha($this->im, 0, 0, 0, 127 - $opacity);
imagefilledrectangle($this->im, 0, 0, 256, 256, $transparent);
imagealphablending($this->im, true);
# Draw the caches.
foreach ($this->rows_ref as &$row_ref)
$this->draw_cache($row_ref);
# Return the result.
ob_start();
imagesavealpha($this->im, true);
imagepng($this->im);
imagedestroy($this->im);
return ob_get_clean();
}
private static function get_image($name, $opacity=1, $brightness=0,
$contrast=0, $r=0, $g=0, $b=0)
{
static $locmem_cache = array();
# Check locmem cache.
$key = "$name/$opacity/$brightness/$contrast/$r/$g/$b";
if (!isset($locmem_cache[$key]))
{
# Miss. Check default cache. WRTODO: upgrade to normal Cache?
try
{
$cache_key = "tilesrc/".Okapi::$revision."/".self::$VERSION."/".$key;
@ -135,11 +135,11 @@ class DefaultTileRenderer implements TileRenderer
catch (Exception $e)
{
# Miss again (or error decoding). Read the image from PNG.
$locmem_cache[$key] = imagecreatefrompng($GLOBALS['rootpath']."okapi/static/tilemap/$name.png");
# Apply all wanted effects.
if ($opacity != 1)
self::change_opacity($locmem_cache[$key], $opacity);
if ($contrast != 0)
@ -148,12 +148,12 @@ class DefaultTileRenderer implements TileRenderer
imagefilter($locmem_cache[$key], IMG_FILTER_BRIGHTNESS, $brightness);
if (($r != 0) || ($g != 0) || ($b != 0))
{
imagefilter($locmem_cache[$key], IMG_FILTER_GRAYSCALE);
imagefilter($locmem_cache[$key], IMG_FILTER_GRAYSCALE);
imagefilter($locmem_cache[$key], IMG_FILTER_COLORIZE, $r, $g, $b);
}
# Cache the result.
ob_start();
imagegd2($locmem_cache[$key]);
$gd2 = ob_get_clean();
@ -169,7 +169,7 @@ class DefaultTileRenderer implements TileRenderer
private static function change_opacity($im, $ratio)
{
imagealphablending($im, false);
$w = imagesx($im);
$h = imagesy($im);
@ -182,7 +182,7 @@ class DefaultTileRenderer implements TileRenderer
imagesetpixel($im, $x, $y, $new_color);
}
}
imagealphablending($im, true);
}
@ -197,7 +197,7 @@ class DefaultTileRenderer implements TileRenderer
$this->draw_cache_large($cache_struct);
# Put caption (this flag is set only when there is plenty of space around).
if ($cache_struct[6] & TileTree::$FLAG_DRAW_CAPTION)
{
$caption = $this->get_caption($cache_struct[0]);
@ -210,13 +210,13 @@ class DefaultTileRenderer implements TileRenderer
private function draw_cache_large(&$cache_struct)
{
list($cache_id, $px, $py, $status, $type, $rating, $flags, $count) = $cache_struct;
$found = $flags & TileTree::$FLAG_FOUND;
$own = $flags & TileTree::$FLAG_OWN;
$new = $flags & TileTree::$FLAG_NEW;
# Prepare vars.
if ($own) {
$key = 'large_outer_own';
$a = 1; $br = 0; $c = 0;
@ -236,29 +236,29 @@ class DefaultTileRenderer implements TileRenderer
$a = 1; $br = 0; $c = 0;
$r = 0; $g = 0; $b = 0;
}
# Put the outer marker (indicates the found/new/own status).
$outer_marker = self::get_image($key, $a);
$width = 40;
$height = 32;
$center_x = 12;
$center_y = 26;
$markercenter_x = 12;
$markercenter_y = 12;
if ($count > 1)
imagecopy($this->im, $outer_marker, $px - $center_x + 3, $py - $center_y - 2, 0, 0, $width, $height);
imagecopy($this->im, $outer_marker, $px - $center_x, $py - $center_y, 0, 0, $width, $height);
# Put the inner marker (indicates the type).
$inner_marker = self::get_image("large_inner_".self::get_type_suffix(
$type, true), $a, $br, $c, $r, $g, $b);
imagecopy($this->im, $inner_marker, $px - 7, $py - 22, 0, 0, 16, 16);
# If the cache is unavailable, mark it with X.
# If the cache is unavailable, mark it with X.
if (($status != 1) && ($count == 1))
{
@ -266,9 +266,9 @@ class DefaultTileRenderer implements TileRenderer
: "status_archived", $a);
imagecopy($this->im, $icon, $px - 1, $py - $center_y - 4, 0, 0, 16, 16);
}
# Put the rating smile. :)
if ($status == 1)
{
if ($rating >= 4.2)
@ -289,15 +289,15 @@ class DefaultTileRenderer implements TileRenderer
// imagecopy($this->im, $icon, $px - 7, $py - $center_y - 8, 0, 0, 16, 16);
// }
}
# Mark found caches with V.
if ($found)
{
$icon = self::get_image("found", 0.7*$a, $br, $c, $r, $g, $b);
imagecopy($this->im, $icon, $px - 2, $py - $center_y - 3, 0, 0, 16, 16);
}
}
/**
@ -335,43 +335,43 @@ class DefaultTileRenderer implements TileRenderer
$lines[] = trim($line);
return implode("\n", $lines);
}
/**
* Return 64x26 bitmap with the caption (name) for the given geocache.
*/
private function get_caption($cache_id)
{
# Check cache.
$cache_key = "tilecaption/".self::$VERSION."/".$cache_id;
$gd2 = self::$USE_CAPTIONS_CACHE ? Cache::get($cache_key) : null;
if ($gd2 === null)
{
# We'll work with 16x bigger image to get smoother interpolation.
$im = imagecreatetruecolor(64*4, 26*4);
imagealphablending($im, false);
imagealphablending($im, false);
$transparent = imagecolorallocatealpha($im, 255, 255, 255, 127);
imagefilledrectangle($im, 0, 0, 64*4, 26*4, $transparent);
imagealphablending($im, true);
# Get the name of the cache.
$name = Db::select_value("
select name
from caches
where cache_id = '".mysql_real_escape_string($cache_id)."'
");
# Split the name into a couple of lines.
//$font = $GLOBALS['rootpath'].'util.sec/bt.ttf';
$font = $GLOBALS['rootpath'].'okapi/static/tilemap/tahoma.ttf';
$size = 25;
$lines = explode("\n", self::wordwrap($font, $size, 64*4 - 6*2, $name));
# For each line, compute its (x, y) so that the text is centered.
$y = 0;
$positions = array();
foreach ($lines as $line)
@ -392,44 +392,44 @@ class DefaultTileRenderer implements TileRenderer
imagettftext($im, $size, 0, $offset_x + $x, $offset_y + $y, $color, $font, $line);
}
};
# Draw an outline.
$outline_color = imagecolorallocatealpha($im, 255, 255, 255, 80);
for ($x=0; $x<=12; $x+=3)
for ($y=$size-3; $y<=$size+9; $y+=3)
$drawer($x, $y, $outline_color);
# Add a slight shadow effect (on top of the outline).
$drawer(9, $size + 3, imagecolorallocatealpha($im, 0, 0, 0, 110));
# Draw the caption.
$drawer(6, $size + 3, imagecolorallocatealpha($im, 150, 0, 0, 40));
# Resample.
imagealphablending($im, false);
imagealphablending($im, false);
$small = imagecreatetruecolor(64, 26);
imagealphablending($small, false);
imagecopyresampled($small, $im, 0, 0, 0, 0, 64, 26, 64*4, 26*4);
# Cache it!
ob_start();
imagegd2($small);
$gd2 = ob_get_clean();
Cache::set_scored($cache_key, $gd2);
}
return imagecreatefromstring($gd2);
}
private function draw_cache_medium(&$cache_struct)
{
list($cache_id, $px, $py, $status, $type, $rating, $flags, $count) = $cache_struct;
$found = $flags & TileTree::$FLAG_FOUND;
$own = $flags & TileTree::$FLAG_OWN;
$new = $flags & TileTree::$FLAG_NEW;
@ -437,9 +437,9 @@ class DefaultTileRenderer implements TileRenderer
$a = .35;
else
$a = 1;
# Put the marker (indicates the type).
$marker = self::get_image("medium_".self::get_type_suffix($type, false), $a);
$width = 14;
$height = 14;
@ -458,7 +458,7 @@ class DefaultTileRenderer implements TileRenderer
imagecopy($this->im, $marker, $px - $center_x, $py - $center_y, 0, 0, $width, $height);
}
# If the cache is unavailable, mark it with X.
# If the cache is unavailable, mark it with X.
if (($status != 1) && ($count == 1))
{
@ -467,9 +467,9 @@ class DefaultTileRenderer implements TileRenderer
imagecopy($this->im, $icon, $px - ($center_x - $markercenter_x) - 6,
$py - ($center_y - $markercenter_y) - 8, 0, 0, 16, 16);
}
# Put small versions of rating icons.
if ($status == 1)
{
if ($rating >= 4.2)
@ -485,18 +485,18 @@ class DefaultTileRenderer implements TileRenderer
}
}
}
if ($own)
{
# Mark own caches with additional overlay.
$overlay = self::get_image("medium_overlay_own");
imagecopy($this->im, $overlay, $px - $center_x, $py - $center_y, 0, 0, $width, $height);
}
elseif ($found)
{
# Mark found caches with V.
$icon = self::get_image("found", 0.7*$a);
imagecopy($this->im, $icon, $px - ($center_x - $markercenter_x) - 7,
$py - ($center_y - $markercenter_y) - 9, 0, 0, 16, 16);
@ -504,7 +504,7 @@ class DefaultTileRenderer implements TileRenderer
elseif ($new)
{
# Mark new caches with additional overlay.
$overlay = self::get_image("medium_overlay_new");
imagecopy($this->im, $overlay, $px - $center_x, $py - $center_y, 0, 0, $width, $height);
}
@ -530,15 +530,15 @@ class DefaultTileRenderer implements TileRenderer
}
return 'other';
}
private function draw_cache_tiny(&$cache_struct)
{
list($cache_id, $px, $py, $status, $type, $rating, $flags, $count) = $cache_struct;
$found = $flags & TileTree::$FLAG_FOUND;
$own = $flags & TileTree::$FLAG_OWN;
$new = $flags & TileTree::$FLAG_NEW;
$marker = self::get_image("tiny_".self::get_type_suffix($type, false));
$width = 10;
$height = 10;
@ -546,9 +546,9 @@ class DefaultTileRenderer implements TileRenderer
$center_y = 6;
$markercenter_x = 5;
$markercenter_y = 6;
# Put the marker. If cache covers more caches, then put two markers instead of one.
if ($count > 1)
{
imagecopy($this->im, $marker, $px - $center_x + 3, $py - $center_y - 2, 0, 0, $width, $height);
@ -559,7 +559,7 @@ class DefaultTileRenderer implements TileRenderer
imagecopy($this->im, $marker, $px - $center_x, $py - $center_y, 0, 0, $width, $height);
}
# If the cache is unavailable, mark it with X.
# If the cache is unavailable, mark it with X.
if (($status != 1) && ($count == 1))
{
@ -582,11 +582,11 @@ class DefaultTileRenderer implements TileRenderer
# A single geocache placed in square (x, y) gets the caption only
# when there are no other geocaches in any of the adjacent squares.
# This is efficient and yields acceptable results.
$matrix = array();
for ($i=0; $i<12; $i++)
$matrix[] = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
foreach ($this->rows_ref as &$row_ref)
{
$mx = ($row_ref[1] + 64) >> 5;
@ -605,7 +605,7 @@ class DefaultTileRenderer implements TileRenderer
if ($matrix[$mx][$my] > 0) # cache_id
{
# Check all adjacent squares.
if ( ($matrix[$mx-1][$my-1] === 0)
&& ($matrix[$mx-1][$my ] === 0)
&& ($matrix[$mx-1][$my+1] === 0)
@ -619,7 +619,7 @@ class DefaultTileRenderer implements TileRenderer
}
}
}
foreach ($this->rows_ref as &$row_ref)
if (in_array($row_ref[0], $selected_cache_ids))
$row_ref[6] |= TileTree::$FLAG_DRAW_CAPTION;

View File

@ -25,13 +25,13 @@ class TileTree
public static $FLAG_STAR = 0x01;
public static $FLAG_HAS_TRACKABLES = 0x02;
public static $FLAG_NOT_YET_FOUND = 0x04;
# Dynamic flags (added at runtime).
public static $FLAG_FOUND = 0x0100;
public static $FLAG_OWN = 0x0200;
public static $FLAG_NEW = 0x0400;
public static $FLAG_DRAW_CAPTION = 0x0800;
/**
* Return null if not computed, 1 if computed and empty, 2 if computed and not empty.
*/
@ -46,7 +46,7 @@ class TileTree
and y = '".mysql_real_escape_string($y)."'
");
}
/**
* Return MySQL's result set iterator over all caches which are present
* in the given result set AND in the given tile.
@ -62,27 +62,27 @@ class TileTree
{
# First, we check if the cache-set for this tile was already computed
# (and if it was, was it empty).
$status = self::get_tile_status($zoom, $x, $y);
if ($status === null) # Not yet computed.
{
# Note, that computing the tile does not involve taking any
# search parameters.
$status = self::compute_tile($zoom, $x, $y);
}
if ($status === 1) # Computed and empty.
{
# This tile was already computed and it is empty.
return null;
}
# If we got here, then the tile is computed and not empty (status 2).
$tile_upper_x = $x << 8;
$tile_leftmost_y = $y << 8;
return Db::query("
select
otc.cache_id,
@ -106,14 +106,14 @@ class TileTree
z21x >> (3 + (21 - $zoom))
");
}
/**
* Precache the ($zoom, $x, $y) slot in the okapi_tile_caches table.
*/
public static function compute_tile($zoom, $x, $y)
{
$time_started = microtime(true);
# Note, that multiple threads may try to compute tiles simulatanously.
# For low-level tiles, this can be expensive. WRTODO: Think of some
# appropriate locks.
@ -121,33 +121,33 @@ class TileTree
$status = self::get_tile_status($zoom, $x, $y);
if ($status !== null)
return $status;
if ($zoom === 0)
{
# When computing zoom zero, we don't have a parent to speed up
# the computation. We need to use the caches table. Note, that
# zoom level 0 contains *entire world*, so we don't have to use
# any WHERE condition in the following query.
# This can be done a little faster (without the use of internal requests),
# but there is *no need* to - this query is run seldom and is cached.
$params = array();
$params['status'] = "Available|Temporarily unavailable|Archived"; # we want them all
$params['limit'] = "10000000"; # no limit
$internal_request = new OkapiInternalRequest(new OkapiInternalConsumer(), null, $params);
$internal_request->skip_limits = true;
$response = OkapiServiceRunner::call("services/caches/search/all", $internal_request);
$cache_codes = $response['results'];
$internal_request = new OkapiInternalRequest(new OkapiInternalConsumer(), null, array(
'cache_codes' => implode('|', $cache_codes),
'fields' => 'internal_id|code|name|location|type|status|rating|recommendations|founds|trackables_count'
));
$internal_request->skip_limits = true;
$caches = OkapiServiceRunner::call("services/caches/geocaches", $internal_request);
foreach ($caches as $cache)
{
$row = self::generate_short_row($cache);
@ -171,18 +171,18 @@ class TileTree
else
{
# We will use the parent tile to compute the contents of this tile.
$parent_zoom = $zoom - 1;
$parent_x = $x >> 1;
$parent_y = $y >> 1;
$status = self::get_tile_status($parent_zoom, $parent_x, $parent_y);
if ($status === null) # Not computed.
{
$time_started = microtime(true);
$status = self::compute_tile($parent_zoom, $parent_x, $parent_y);
}
if ($status === 1) # Computed and empty.
{
# No need to check.
@ -197,11 +197,11 @@ class TileTree
$right_z21x = ((($parent_x + 1) << 1) << $scale) + $margin;
$top_z21y = (($parent_y << 1) << $scale) - $margin;
$bottom_z21y = ((($parent_y + 1) << 1) << $scale) + $margin;
# Choose the right quarter.
# |1 2|
# |3 4|
if ($x & 1) # 2 or 4
$left_z21x = $parentcenter_z21x - $margin;
else # 1 or 3
@ -210,9 +210,9 @@ class TileTree
$top_z21y = $parentcenter_z21y - $margin;
else # 1 or 2
$bottom_z21y = $parentcenter_z21y + $margin;
# Cache the result.
Db::execute("
replace into okapi_tile_caches (
z, x, y, cache_id, z21x, z21y, status, type, rating, flags
@ -245,9 +245,9 @@ class TileTree
$status = 1;
}
}
# Mark tile as computed.
Db::execute("
replace into okapi_tile_status (z, x, y, status)
values (
@ -257,10 +257,10 @@ class TileTree
'".mysql_real_escape_string($status)."'
);
");
return $status;
}
/**
* Convert OKAPI's cache object to a short database row to be inserted
* into okapi_tile_caches table. Returns the list of the following attributes:
@ -280,7 +280,7 @@ class TileTree
return array($cache['internal_id'], $z21x, $z21y, Okapi::cache_status_name2id($cache['status']),
Okapi::cache_type_name2id($cache['type']), $cache['rating'], $flags);
}
private static function latlon_to_z21xy($lat, $lon)
{
$offset = 128 << 21;

View File

@ -22,22 +22,22 @@ class WebService
return array(
'min_auth_level' => 3
);
}
}
public static function call(OkapiRequest $request)
{
# User is already verified (via OAuth), but we need to verify the
# cache code (check if it exists). We will simply call a geocache method
# on it - this will also throw a proper exception if it doesn't exist.
$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' => 'internal_id')));
# watched
if ($tmp = $request->get_parameter('watched'))
{
if (!in_array($tmp, array('true', 'false', 'unchanged')))
@ -58,9 +58,9 @@ class WebService
and user_id = '".mysql_real_escape_string($request->token->user_id)."';
");
}
# ignored
if ($tmp = $request->get_parameter('ignored'))
{
if (!in_array($tmp, array('true', 'false', 'unchanged')))

View File

@ -3,7 +3,7 @@
<issue-id>166</issue-id>
<desc>
<p>This method allows your users to mark the geocache as <b>watched</b> or
<b>ignored</b>, or to do modify user's personal <b>notes</b> saved along the geocache.
<b>ignored</b>.
Read the docs on separate parameters for details.</p>
</desc>
<req name='cache_code'>

View File

@ -26,7 +26,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
$search_params = SearchAssistant::get_common_search_params($request);

View File

@ -18,7 +18,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
# You may wonder, why there are no parameters like "bbox" or "center" in the
@ -27,7 +27,7 @@ class WebService
# make the documentation very fuzzy. That's why they were intentionally
# left out of the "search/all" method, and put in separate (individual) ones.
# It's much easier to grasp their meaning this way.
$tmp = $request->get_parameter('bbox');
if (!$tmp)
throw new ParamMissing('bbox');
@ -49,9 +49,9 @@ class WebService
throw new InvalidParam('bbox', "Latitudes have to be within -90..90 range.");
if ($bbeast > 180 || $bbeast < -180 || $bbwest > 180 || $bbwest < -180)
throw new InvalidParam('bbox', "Longitudes have to be within -180..180 range.");
# Construct SQL conditions for the specified bounding box.
$where_conds = array();
$where_conds[] = "caches.latitude between '".mysql_real_escape_string($bbsouth)."' and '".mysql_real_escape_string($bbnorth)."'";
if ($bbeast > $bbwest)
@ -65,23 +65,23 @@ class WebService
# For example, $bbwest = 179 and $bbeast = -179.
$where_conds[] = "(caches.longitude > '".mysql_real_escape_string($bbwest)."' or caches.longitude < '".mysql_real_escape_string($bbeast)."')";
}
#
# In the method description, we promised to return caches ordered by the *rough*
# distance from the center of the bounding box. We'll use ORDER BY with a simplified
# distance formula and combine it with the LIMIT clause to get the best results.
#
$center_lat = ($bbsouth + $bbnorth) / 2.0;
$center_lon = ($bbwest + $bbeast) / 2.0;
$center_lon = ($bbwest + $bbeast) / 2.0;
$search_params = SearchAssistant::get_common_search_params($request);
$search_params['where_conds'] = array_merge($where_conds, $search_params['where_conds']);
$search_params['order_by'][] = Okapi::get_distance_sql($center_lat, $center_lon,
"caches.latitude", "caches.longitude"); # not replaced; added to the end!
$result = SearchAssistant::get_common_search_result($search_params);
return Okapi::formatted_response($request, $result);
}
}

View File

@ -19,7 +19,7 @@ class WebService
'min_auth_level' => 1
);
}
/**
* Returns one of: array('cache_code', 'OPXXXX'), array('internal_id', '12345'),
* array('uuid', 'A408C3...') or null.
@ -27,7 +27,7 @@ class WebService
private static function get_cache_key($url)
{
# Determine our own domain.
static $host = null;
static $length = null;
if ($host == null)
@ -37,9 +37,9 @@ class WebService
$host = substr($host, 4);
$length = strlen($host);
}
# Parse the URL
$uri = parse_url($url);
if ($uri == false)
return null;
@ -75,11 +75,11 @@ class WebService
}
return null;
}
public static function call(OkapiRequest $request)
{
# Retrieve the list of URLs to check.
$tmp = $request->get_parameter('urls');
if (!$tmp)
throw new ParamMissing('urls');
@ -89,9 +89,9 @@ class WebService
if (!in_array($as_dict, array('true', 'false')))
throw new InvalidParam('as_dict');
$as_dict = ($as_dict == 'true');
# Generate the lists of keys.
$results = array();
$urls_with = array(
'cache_code' => array(),
@ -106,14 +106,14 @@ class WebService
else
$results[$url_ref] = null;
}
# Include 'cache_code' references.
foreach ($urls_with['cache_code'] as $url => $cache_code)
$results[$url] = $cache_code;
# Include 'internal_id' references.
$internal_ids = array_values($urls_with['internal_id']);
if (count($internal_ids) > 0)
{
@ -135,9 +135,9 @@ class WebService
$results[$url] = null;
}
}
# Include 'uuid' references.
$uuids = array_values($urls_with['uuid']);
if (count($uuids) > 0)
{
@ -159,9 +159,9 @@ class WebService
$results[$url] = null;
}
}
# Format the results according to the 'as_dict' parameter.
if ($as_dict)
return Okapi::formatted_response($request, $results);
else

View File

@ -18,7 +18,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
# You may wonder, why there are no parameters like "bbox" or "center" in the
@ -27,7 +27,7 @@ class WebService
# make the documentation very fuzzy. That's why they were intentionally
# left out of the "search/all" method, and put in separate (individual) ones.
# It's much easier to grasp their meaning this way.
$tmp = $request->get_parameter('center');
if (!$tmp)
throw new ParamMissing('center');
@ -45,18 +45,18 @@ class WebService
throw new InvalidParam('center', "Latitudes have to be within -90..90 range.");
if ($center_lon > 180 || $center_lon < -180)
throw new InvalidParam('center', "Longitudes have to be within -180..180 range.");
#
# In the method description, we promised to return caches ordered by the *rough*
# distance from the center point. We'll use ORDER BY with a simplified distance
# formula and combine it with the LIMIT clause to get the best results.
#
$distance_formula = Okapi::get_distance_sql($center_lat, $center_lon, "caches.latitude", "caches.longitude");
# 'radius' parameter is optional. If not given, we'll have to calculate the
# distance for every cache in the database.
$where_conds = array();
$radius = null;
if ($tmp = $request->get_parameter('radius'))
@ -69,18 +69,18 @@ class WebService
$radius *= 1000; # this one is given in kilemeters, converting to meters!
$where_conds[] = "$distance_formula <= '".mysql_real_escape_string($radius)."'";
}
$search_params = SearchAssistant::get_common_search_params($request);
$search_params['where_conds'] = array_merge($where_conds, $search_params['where_conds']);
$search_params['order_by'][] = $distance_formula; # not replaced; added to the end!
$result = SearchAssistant::get_common_search_result($search_params);
if ($radius == null)
{
# 'more' is meaningless in this case, we'll remove it.
unset($result['more']);
unset($result['more']);
}
return Okapi::formatted_response($request, $result);
}
}

View File

@ -20,7 +20,7 @@ class WebService
'min_auth_level' => 1
);
}
/**
* Get [set_id, date_created, expires] for the given params_hash
* (or [null, null, null] if not found).
@ -43,11 +43,11 @@ class WebService
return array(null, null, null);
return array($tmp['id'], $tmp['date_created'], $tmp['expires']);
}
public static function call(OkapiRequest $request)
{
# "Cache control" parameters.
$tmp = $request->get_parameter('min_store');
if ($tmp === null) $tmp = "300";
$min_store = intval($tmp);
@ -60,9 +60,9 @@ class WebService
$ref_max_age = intval($tmp);
if (("$ref_max_age" !== $tmp) || ($ref_max_age < 300))
throw new InvalidParam('ref_max_age', "Has to be >=300.");
# Search params.
$search_params = SearchAssistant::get_common_search_params($request);
$tables = array_merge(
array('caches'),
@ -73,43 +73,43 @@ class WebService
$search_params['where_conds']
);
unset($search_params);
# Generate, or retrieve an existing set, and return the result.
$result = self::get_set($tables, $where_conds, $min_store, $ref_max_age);
return Okapi::formatted_response($request, $result);
}
public static function get_set($tables, $where_conds, $min_store, $ref_max_age)
{
# Compute the "params hash".
$params_hash = md5(serialize(array($tables, $where_conds)));
# Check if there exists an entry for this hash, which also meets the
# given freshness criteria.
list($set_id, $date_created, $expires) = self::find_param_set($params_hash, $ref_max_age);
if ($set_id === null)
{
# To avoid generating the same results by multiple threads at once
# (the "tile" method uses the "save" method, so the problem is
# quite real!), we will acquire a write-lock here.
$lock = OkapiLock::get("search-results-writer");
$lock->acquire();
try
{
# Make sure we were the first to acquire the lock.
list($set_id, $date_created, $expires) = self::find_param_set($params_hash, $ref_max_age);
if ($set_id === null)
{
# We are in the first thread which have acquired the lock.
# We will proceed with result-set creation. Other threads
# will be waiting until we finish.
Db::execute("
insert into okapi_search_sets (params_hash, date_created, expires)
values (
@ -129,13 +129,13 @@ class WebService
from ".implode(", ", $tables)."
where (".implode(") and (", $where_conds).")
");
# Lock barrier, to make sure the data is visible by other
# sessions. See http://bugs.mysql.com/bug.php?id=36618
Db::execute("lock table okapi_search_results write");
Db::execute("unlock tables");
Db::execute("
update okapi_search_sets
set params_hash = '".mysql_real_escape_string($params_hash)."'
@ -150,15 +150,15 @@ class WebService
catch (Exception $e)
{
# SQL error? Make sure the lock is released and rethrow.
$lock->release();
throw $e;
}
}
# If we got an old set, we may need to expand its lifetime in order to
# meet user's "min_store" criterium.
if (time() + $min_store > $expires)
{
Db::execute("
@ -167,7 +167,7 @@ class WebService
where id = '".mysql_real_escape_string($set_id)."'
");
}
return array(
'set_id' => "$set_id",
'generated_at' => date('c', $date_created),

View File

@ -19,7 +19,7 @@ class SearchAssistant
* given OKAPI request. Most cache search methods share a common set
* of filtering parameters recognized by this method. It returns
* a dictionary of the following structure:
*
*
* - "where_conds" - list of additional WHERE conditions to be ANDed
* to the rest of your SQL query,
* - "offset" - value of the offset parameter to be used in the LIMIT clause,
@ -32,16 +32,16 @@ class SearchAssistant
{
$where_conds = array('true');
$extra_tables = array();
# At the beginning we have to set up some "magic e$Xpressions".
# We will use them to make our query run on both OCPL and OCDE databases.
if (Settings::get('OC_BRANCH') == 'oc.pl')
{
# OCPL's 'caches' table contains some fields which OCDE's does not
# (topratings, founds, notfounds, last_found, votes, score). If
# we're being run on OCPL installation, we will simply use them.
$X_TOPRATINGS = 'caches.topratings';
$X_FOUNDS = 'caches.founds';
$X_NOTFOUNDS = 'caches.notfounds';
@ -57,22 +57,22 @@ class SearchAssistant
# additional table in our query (along with a proper WHERE
# condition) and we will map the field expressions to
# approriate places.
$extra_tables[] = 'stat_caches';
$where_conds[] = 'stat_caches.cache_id = caches.cache_id';
$X_TOPRATINGS = 'stat_caches.toprating';
$X_FOUNDS = 'stat_caches.found';
$X_NOTFOUNDS = 'stat_caches.notfound';
$X_LAST_FOUND = 'stat_caches.last_found';
$X_VOTES = '0'; // no support for ratings
$X_VOTES = '0'; // no support for ratings
$X_SCORE = '0'; // no support for ratings
}
#
# type
#
if ($tmp = $request->get_parameter('type'))
{
$operator = "in";
@ -96,11 +96,11 @@ class SearchAssistant
}
$where_conds[] = "caches.type $operator ('".implode("','", array_map('mysql_real_escape_string', $types))."')";
}
#
# size2
#
if ($tmp = $request->get_parameter('size2'))
{
$operator = "in";
@ -128,7 +128,7 @@ class SearchAssistant
#
# status - filter by status codes
#
$tmp = $request->get_parameter('status');
if ($tmp == null) $tmp = "Available";
$codes = array();
@ -144,11 +144,11 @@ class SearchAssistant
}
}
$where_conds[] = "caches.status in ('".implode("','", array_map('mysql_real_escape_string', $codes))."')";
#
# owner_uuid
#
if ($tmp = $request->get_parameter('owner_uuid'))
{
$operator = "in";
@ -171,11 +171,11 @@ class SearchAssistant
$user_ids[] = $user['internal_id'];
$where_conds[] = "caches.user_id $operator ('".implode("','", array_map('mysql_real_escape_string', $user_ids))."')";
}
#
# terrain, difficulty, size, rating - these are similar, we'll do them in a loop
#
foreach (array('terrain', 'difficulty', 'size', 'rating') as $param_name)
{
if ($tmp = $request->get_parameter($param_name))
@ -247,11 +247,11 @@ class SearchAssistant
}
}
}
#
# min_rcmds
#
if ($tmp = $request->get_parameter('min_rcmds'))
{
if ($tmp[strlen($tmp) - 1] == '%')
@ -270,33 +270,33 @@ class SearchAssistant
throw new InvalidParam('min_rcmds', "'$tmp'");
$where_conds[] = "$X_TOPRATINGS >= '".mysql_real_escape_string($tmp)."'";
}
#
# min_founds
#
if ($tmp = $request->get_parameter('min_founds'))
{
if (!is_numeric($tmp))
throw new InvalidParam('min_founds', "'$tmp'");
$where_conds[] = "$X_FOUNDS >= '".mysql_real_escape_string($tmp)."'";
}
#
# max_founds
#
if ($tmp = $request->get_parameter('max_founds'))
{
if (!is_numeric($tmp))
throw new InvalidParam('max_founds', "'$tmp'");
$where_conds[] = "$X_FOUNDS <= '".mysql_real_escape_string($tmp)."'";
}
#
# modified_since
#
if ($tmp = $request->get_parameter('modified_since'))
{
$timestamp = strtotime($tmp);
@ -305,11 +305,11 @@ class SearchAssistant
else
throw new InvalidParam('modified_since', "'$tmp' is not in a valid format or is not a valid date.");
}
#
# found_status
#
if ($tmp = $request->get_parameter('found_status'))
{
if ($request->token == null)
@ -323,11 +323,11 @@ class SearchAssistant
$where_conds[] = "caches.cache_id $operator ('".implode("','", array_map('mysql_real_escape_string', $found_cache_ids))."')";
}
}
#
# found_by
#
if ($tmp = $request->get_parameter('found_by'))
{
try {
@ -339,11 +339,11 @@ class SearchAssistant
$found_cache_ids = self::get_found_cache_ids($user['internal_id']);
$where_conds[] = "caches.cache_id in ('".implode("','", array_map('mysql_real_escape_string', $found_cache_ids))."')";
}
#
# not_found_by
#
if ($tmp = $request->get_parameter('not_found_by'))
{
try {
@ -355,11 +355,11 @@ class SearchAssistant
$found_cache_ids = self::get_found_cache_ids($user['internal_id']);
$where_conds[] = "caches.cache_id not in ('".implode("','", array_map('mysql_real_escape_string', $found_cache_ids))."')";
}
#
# watched_only
#
if ($tmp = $request->get_parameter('watched_only'))
{
if ($request->token == null)
@ -380,7 +380,7 @@ class SearchAssistant
#
# exclude_ignored
#
if ($tmp = $request->get_parameter('exclude_ignored'))
{
if ($request->token == null)
@ -396,11 +396,11 @@ class SearchAssistant
$where_conds[] = "cache_id not in ('".implode("','", array_map('mysql_real_escape_string', $ignored_cache_ids))."')";
}
}
#
# exclude_my_own
#
if ($tmp = $request->get_parameter('exclude_my_own'))
{
if ($request->token == null)
@ -410,26 +410,26 @@ class SearchAssistant
if ($tmp == 'true')
$where_conds[] = "caches.user_id != '".mysql_real_escape_string($request->token->user_id)."'";
}
#
# name
#
if ($tmp = $request->get_parameter('name'))
{
# WRTODO: Make this more user-friendly. See:
# http://code.google.com/p/opencaching-api/issues/detail?id=121
if (strlen($tmp) > 100)
throw new InvalidParam('name', "Maximum length of 'name' parameter is 100 characters");
$tmp = str_replace("*", "%", str_replace("%", "%%", $tmp));
$tmp = str_replace("*", "%", str_replace("%", "%%", $tmp));
$where_conds[] = "caches.name LIKE '".mysql_real_escape_string($tmp)."'";
}
#
# with_trackables_only
#
if ($tmp = $request->get_parameter('with_trackables_only'))
{
if (!in_array($tmp, array('true', 'false'), 1))
@ -444,11 +444,11 @@ class SearchAssistant
";
}
}
#
# ftf_hunter
#
if ($tmp = $request->get_parameter('ftf_hunter'))
{
if (!in_array($tmp, array('true', 'false'), 1))
@ -458,15 +458,15 @@ class SearchAssistant
$where_conds[] = "caches.founds = 0";
}
}
#
# set_and
#
if ($tmp = $request->get_parameter('set_and'))
{
# Check if the set exists.
$exists = Db::select_value("
select 1
from okapi_search_sets
@ -478,22 +478,22 @@ class SearchAssistant
$where_conds[] = "osr_and.cache_id = caches.cache_id";
$where_conds[] = "osr_and.set_id = '".mysql_real_escape_string($tmp)."'";
}
#
# limit
#
$limit = $request->get_parameter('limit');
if ($limit == null) $limit = "100";
if (!is_numeric($limit))
throw new InvalidParam('limit', "'$limit'");
if ($limit < 1 || (($limit > 500) && (!$request->skip_limits)))
throw new InvalidParam('limit', "Has to be between 1 and 500.");
#
# offset
#
$offset = $request->get_parameter('offset');
if ($offset == null) $offset = "0";
if (!is_numeric($offset))
@ -502,17 +502,17 @@ class SearchAssistant
throw new BadRequest("The sum of offset and limit may not exceed 500.");
if ($offset < 0 || $offset > 499)
throw new InvalidParam('offset', "Has to be between 0 and 499.");
#
# order_by
#
$order_clauses = array();
$order_by = $request->get_parameter('order_by');
if ($order_by != null)
{
$order_by = explode('|', $order_by);
foreach ($order_by as $field)
foreach ($order_by as $field)
{
$dir = 'asc';
if ($field[0] == '-')
@ -537,15 +537,15 @@ class SearchAssistant
$order_clauses[] = "($cl) $dir";
}
}
# To avoid join errors, put each of the $where_conds in extra paranthesis.
$tmp = array();
foreach($where_conds as $cond)
$tmp[] = "(".$cond.")";
$where_conds = $tmp;
unset($tmp);
$ret_array = array(
'where_conds' => $where_conds,
'offset' => (int)$offset,
@ -556,13 +556,13 @@ class SearchAssistant
return $ret_array;
}
/**
* Search for caches using given conditions and options. Return
* an array in a "standard" format of array('results' => list of
* cache codes, 'more' => boolean). This method takes care of the
* 'more' variable in an appropriate way.
*
*
* The $options parameter include:
* - where_conds - list of additional WHERE conditions to be ANDed
* to the rest of your SQL query,
@ -581,10 +581,10 @@ class SearchAssistant
array('caches.wp_oc is not null'),
$options['where_conds']
);
# We need to pull limit+1 items, in order to properly determine the
# value of "more" variable.
$cache_codes = Db::select_column("
select caches.wp_oc
from ".implode(", ", $tables)."
@ -592,7 +592,7 @@ class SearchAssistant
".((count($options['order_by']) > 0) ? "order by ".implode(", ", $options['order_by']) : "")."
limit ".($options['offset']).", ".($options['limit'] + 1).";
");
if (count($cache_codes) > $options['limit'])
{
$more = true;
@ -600,16 +600,16 @@ class SearchAssistant
} else {
$more = false;
}
$result = array(
'results' => $cache_codes,
'more' => $more,
);
return $result;
}
/**
* Get the list of cache IDs which were found by given user.
/**
* Get the list of cache IDs which were found by given user.
* Parameter needs to be *internal* user id, not uuid.
*/
private static function get_found_cache_ids($internal_user_id)

View File

@ -9,6 +9,7 @@ use okapi\OkapiServiceRunner;
use okapi\OkapiRequest;
use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\BadRequest;
class WebService
{
@ -18,7 +19,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
# Check search method
@ -35,7 +36,7 @@ class WebService
$search_params = json_decode($search_params, true);
if (!is_array($search_params))
throw new InvalidParam('search_params', "Should be a JSON-encoded dictionary");
# Check retrieval method
$retr_method = $request->get_parameter('retr_method');
if (!$retr_method)
@ -48,14 +49,17 @@ class WebService
$retr_params = json_decode($retr_params, true);
if (!is_array($retr_params))
throw new InvalidParam('retr_params', "Should be a JSON-encoded dictionary");
self::map_values_to_strings($search_params);
self::map_values_to_strings($retr_params);
# Wrapped?
$wrap = $request->get_parameter('wrap');
if ($wrap == null) throw new ParamMissing('wrap');
if (!in_array($wrap, array('true', 'false')))
throw new InvalidParam('wrap');
$wrap = ($wrap == 'true');
# Run search method
try
{
@ -67,7 +71,7 @@ class WebService
throw new InvalidParam('search_params', "Search method responded with the ".
"following error message: ".$e->getMessage());
}
# Run retrieval method
try
{
@ -80,7 +84,7 @@ class WebService
throw new InvalidParam('retr_params', "Retrieval method responded with the ".
"following error message: ".$e->getMessage());
}
if ($wrap)
{
# $retr_result might be a PHP object, but also might be a binary response
@ -102,4 +106,16 @@ class WebService
return Okapi::formatted_response($request, $retr_result);
}
}
private static function map_values_to_strings(&$dict)
{
foreach (array_keys($dict) as $key)
{
$val = $dict[$key];
if (is_numeric($val) || is_string($val))
$dict[$key] = (string)$val;
else
throw new BadRequest("Invalid value format for key: ".$key);
}
}
}

View File

@ -21,10 +21,10 @@ class WebService
'min_auth_level' => 1
);
}
private static $valid_field_names = array(
'uuid', 'cache_code', 'date', 'user', 'type', 'was_recommended', 'comment',
'internal_id',
'images', 'internal_id',
);
public static function call(OkapiRequest $request)
@ -37,7 +37,7 @@ class WebService
}
else
$log_uuids = explode("|", $log_uuids);
if ((count($log_uuids) > 500) && (!$request->skip_limits))
throw new InvalidParam('log_uuids', "Maximum allowed number of referenced ".
"log entries is 500. You provided ".count($log_uuids)." UUIDs.");
@ -49,7 +49,7 @@ class WebService
foreach ($fields as $field)
if (!in_array($field, self::$valid_field_names))
throw new InvalidParam('fields', "'$field' is not a valid field code.");
$rs = Db::query("
select
cl.id, c.wp_oc as cache_code, cl.uuid, cl.type,
@ -71,6 +71,7 @@ class WebService
and c.status in (1,2,3)
");
$results = array();
$log_id2uuid = array(); /* Maps logs' internal_ids to uuids */
while ($row = mysql_fetch_assoc($rs))
{
$results[$row['uuid']] = array(
@ -85,30 +86,60 @@ class WebService
'type' => Okapi::logtypeid2name($row['type']),
'was_recommended' => $row['was_recommended'] ? true : false,
'comment' => $row['text'],
'images' => array(),
'internal_id' => $row['id'],
);
$log_id2uuid[$row['id']] = $row['uuid'];
}
mysql_free_result($rs);
# fetch images
if (in_array('images', $fields))
{
$rs = Db::query("
select object_id, uuid, url, title, spoiler
from pictures
where
object_type = 1
and object_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($log_id2uuid)))."')
and display = 1 /* currently is always 1 for logpix */
and unknown_format = 0
order by date_created
");
while ($row = mysql_fetch_assoc($rs))
{
$results[$log_id2uuid[$row['object_id']]]['images'][] =
array(
'uuid' => $row['uuid'],
'url' => $row['url'],
'thumb_url' => Settings::get('SITE_URL') . 'thumbs.php?uuid=' . $row['uuid'],
'caption' => $row['title'],
'is_spoiler' => ($row['spoiler'] ? true : false),
);
}
mysql_free_result($rs);
}
# Check which UUIDs were not found and mark them with null.
foreach ($log_uuids as $log_uuid)
if (!isset($results[$log_uuid]))
$results[$log_uuid] = null;
# Remove unwanted fields.
foreach (self::$valid_field_names as $field)
if (!in_array($field, $fields))
foreach ($results as &$result_ref)
unset($result_ref[$field]);
# Order the results in the same order as the input codes were given.
$ordered_results = array();
foreach ($log_uuids as $log_uuid)
$ordered_results[$log_uuid] = $results[$log_uuid];
return Okapi::formatted_response($request, $ordered_results);
}
}

View File

@ -24,7 +24,7 @@ class WebService
if (!$log_uuid) throw new ParamMissing('log_uuid');
$fields = $request->get_parameter('fields');
if (!$fields) $fields = "date|user|type|comment";
$results = OkapiServiceRunner::call('services/logs/entries', new OkapiInternalRequest(
$request->consumer, $request->token, array('log_uuids' => $log_uuid,
'fields' => $fields)));

View File

@ -40,6 +40,20 @@
in this log entry,</p>
</li>
<li><b>comment</b> - HTML string, text entered with the log entry,</li>
<li>
<p><b>images</b> - list of dictionaries, each dictionary represents one
image saved along with the log; each dictionary has the following
structure:</p>
<ul>
<li><b>uuid</b> - UUID of the image,</li>
<li><b>url</b> - URL of the image,</li>
<li><b>thumb_url</b> - URL of a small (thumb) version of the image,</li>
<li><b>caption</b> - plain-text string, caption of the image,</li>
<li><b>is_spoiler</b> - boolean, if <b>true</b> then the image is
a spoiler image and should not be displayed to the user unless
the user explicitly asks for it.</li>
</ul>
</li>
<li>
<p><b>internal_id</b> - undocumented, you <u>should not</u> use
this unless you really know you need to. Internal IDs are

View File

@ -27,7 +27,7 @@ class WebService
if (!$cache_code) throw new ParamMissing('cache_code');
$fields = $request->get_parameter('fields');
if (!$fields) $fields = "uuid|date|user|type|comment";
$offset = $request->get_parameter('offset');
if (!$offset) $offset = "0";
if ((((int)$offset) != $offset) || ((int)$offset) < 0)
@ -37,15 +37,15 @@ class WebService
if ($limit == "none") $limit = "999999999";
if ((((int)$limit) != $limit) || ((int)$limit) < 0)
throw new InvalidParam('limit', "Expecting non-negative integer or 'none'.");
# Check if code exists and retrieve cache ID (this will throw
# a proper exception on invalid code).
$cache = OkapiServiceRunner::call('services/caches/geocache', new OkapiInternalRequest(
$request->consumer, null, array('cache_code' => $cache_code, 'fields' => 'internal_id')));
# Cache exists. Getting the uuids of its logs.
$log_uuids = Db::select_column("
select uuid
from cache_logs
@ -55,9 +55,9 @@ class WebService
order by date desc
limit $offset, $limit
");
# Getting the logs themselves. Formatting as an ordered list.
$internal_request = new OkapiInternalRequest(
$request->consumer, $request->token, array('log_uuids' => implode("|", $log_uuids),
'fields' => $fields));
@ -66,7 +66,7 @@ class WebService
$results = array();
foreach ($log_uuids as $log_uuid)
$results[] = $logs[$log_uuid];
return Okapi::formatted_response($request, $results);
}
}

View File

@ -16,7 +16,7 @@ use okapi\services\caches\search\SearchAssistant;
use okapi\BadRequest;
/**
/**
* This exception is thrown by WebService::_call method, when error is detected in
* user-supplied data. It is not a BadRequest exception - it does not imply that
* the Consumer did anything wrong (it's the user who did). This exception shouldn't
@ -31,9 +31,9 @@ class WebService
return array(
'min_auth_level' => 3
);
}
/**
}
/**
* Publish a new log entry and return log entry uuid. Throws
* CannotPublishException or BadRequest on errors.
*/
@ -43,15 +43,23 @@ class WebService
# CannotPublishException and standard BadRequest/InvalidParam exceptions!
# Notice, that this is "_call" method, not the usual "call" (see below
# for "call").
$cache_code = $request->get_parameter('cache_code');
if (!$cache_code) throw new ParamMissing('cache_code');
$logtype = $request->get_parameter('logtype');
if (!$logtype) throw new ParamMissing('logtype');
if (!in_array($logtype, array('Found it', "Didn't find it", 'Comment')))
throw new InvalidParam('logtype', "'$logtype' in not a valid logtype code.");
$comment = $request->get_parameter('comment');
if (!$comment) $comment = "";
$comment_format = $request->get_parameter('comment_format');
if (!$comment_format) $comment_format = "auto";
if (!in_array($comment_format, array('auto', 'html', 'plaintext')))
throw new InvalidParam('comment_format', $comment_format);
$tmp = $request->get_parameter('when');
if ($tmp)
{
@ -64,10 +72,12 @@ class WebService
}
else
$when = time();
$on_duplicate = $request->get_parameter('on_duplicate');
if (!$on_duplicate) $on_duplicate = "silent_success";
if (!in_array($on_duplicate, array('silent_success', 'user_error', 'continue')))
throw new InvalidParam('on_duplicate', "Unknown option: '$on_duplicate'.");
$rating = $request->get_parameter('rating');
if ($rating !== null && (!in_array($rating, array(1,2,3,4,5))))
throw new InvalidParam('rating', "If present, it must be an integer in the 1..5 scale.");
@ -78,11 +88,12 @@ class WebService
# We will remove the rating request and change the success message
# (which will be returned IF the rest of the query will meet all the
# requirements).
self::$success_message .= " ".sprintf(_("However, your cache rating was ignored, because %s does not have a rating system."),
Okapi::get_normalized_site_name());
$rating = null;
}
$recommend = $request->get_parameter('recommend');
if (!$recommend) $recommend = 'false';
if (!in_array($recommend, array('true', 'false')))
@ -90,6 +101,7 @@ class WebService
$recommend = ($recommend == 'true');
if ($recommend && $logtype != 'Found it')
throw new BadRequest(_("Recommending is allowed only for 'Found it' logtypes."));
$needs_maintenance = $request->get_parameter('needs_maintenance');
if (!$needs_maintenance) $needs_maintenance = 'false';
if (!in_array($needs_maintenance, array('true', 'false')))
@ -102,26 +114,26 @@ class WebService
Okapi::get_normalized_site_name());
$needs_maintenance = false;
}
# Check if cache exists and retrieve cache internal ID (this will throw
# a proper exception on invalid cache_code). Also, get the user object.
$cache = OkapiServiceRunner::call('services/caches/geocache', new OkapiInternalRequest(
$request->consumer, null, array('cache_code' => $cache_code,
'fields' => 'internal_id|status|owner|type|req_passwd')));
$user = OkapiServiceRunner::call('services/users/by_internal_id', new OkapiInternalRequest(
$request->consumer, $request->token, array('internal_id' => $request->token->user_id,
'fields' => 'is_admin|uuid|internal_id|caches_found|rcmds_given')));
# Various integrity checks.
if ($cache['type'] == 'Event' && $logtype != 'Comment')
throw new CannotPublishException(_('This cache is an Event cache. You cannot "Find it"! (But - you may "Comment" on it.)'));
if ($logtype == 'Comment' && strlen(trim($comment)) == 0)
throw new CannotPublishException(_("Your have to supply some text for your comment."));
# Password check.
if ($logtype == 'Found it' && $cache['req_passwd'])
{
$valid_password = Db::select_value("
@ -135,24 +147,90 @@ class WebService
if (strtolower($supplied_password) != strtolower($valid_password))
throw new CannotPublishException(_("Invalid password!"));
}
# Very weird part (as usual). OC stores HTML-lized comments in the database
# (it doesn't really matter if 'text_html' field is set to FALSE). OKAPI must
# save it in HTML either way. However, escaping plain-text doesn't work.
# If we put "&lt;b&gt;" in, it still gets converted to "<b>" before display!
# NONE of this process is documented. There doesn't seem to be ANY way to force
# OCPL to treat a string as either plain-text nor HTML. It's always something
# in between! In other words, we cannot guarantee how it gets displayed in
# the end. Since text_html=0 doesn't add <br/>s on its own, we can only add
# proper <br/>s and hope it's okay. We will remove the original $comment
# variable from our namespace and act on the "pseudoencoded" one.
$PSEUDOENCODED_comment = htmlspecialchars($comment, ENT_QUOTES);
$PSEUDOENCODED_comment = nl2br($PSEUDOENCODED_comment);
# Prepare our comment to be inserted into the database. This may require
# some reformatting which depends on the current OC installation.
if (Settings::get('OC_BRANCH') == 'oc.de')
{
# OCDE stores all comments in HTML format, while the 'text_html' field
# indicates their *original* format as delivered by the user. This
# allows processing the 'text' field contents without caring about the
# original format, while still being able to re-create the comment in
# its original form. It requires us to HTML-encode plaintext comments
# and to indicate this by setting 'html_text' to FALSE.
#
# For user-supplied HTML comments, OCDE requires us to do additional
# HTML purification prior to the insertion into the database.
if ($comment_format == 'plaintext')
{
# If we would like to be compatible with old OCDE/OC.nl installation,
# "$comment_format == 'auto'" should go here, too. But we must choose
# to resemble either old OCDE or OCPL behaviour and opt for OCPL compatibility.
$formatted_comment = htmlspecialchars($comment, ENT_QUOTES);
$formatted_comment = nl2br($formatted_comment);
$value_for_text_html_field = 0;
}
else
{
if ($comment_format == 'auto')
{
# This does not make sense on HTML comments, but it resembles the
# OCPL implementation and is needed for full compatibility with existing
# OKAPI clients.
$formatted_comment = nl2br($comment);
}
else
$formatted_comment = $comment;
# NOTICE: We are including EXTERNAL OCDE library here! This
# code does not belong to OKAPI!
require_once $GLOBALS['rootpath'] . '../lib/htmlpurifier-4.2.0/library/HTMLPurifier.auto.php';
$purifier = new \HTMLPurifier();
$formatted_comment = $purifier->purify($formatted_comment);
$value_for_text_html_field = 1;
}
}
else
{
# OCPL is even weirder. It also stores HTML-lized comments in the database
# (it doesn't really matter if 'text_html' field is set to FALSE). OKAPI must
# save it in HTML either way. However, escaping plain-text doesn't work!
# If we put "&lt;b&gt;" in, it still gets converted to "<b>" before display!
# NONE of this process is documented within OCPL code. OKAPI uses a dirty
# "hack" to save PLAINTEXT comments (let us hope the hack will remain valid).
#
# OCPL doesn't require HTML purification prior to the database insertion.
# HTML seems to be purified dynamically, before it is displayed.
if ($comment_format == 'plaintext')
{
$formatted_comment = htmlspecialchars($comment, ENT_QUOTES);
$formatted_comment = nl2br($formatted_comment);
$formatted_comment = str_replace("&amp;", "&amp;#38;", $formatted_comment);
$formatted_comment = str_replace("&lt;", "&amp;#60;", $formatted_comment);
$formatted_comment = str_replace("&gt;", "&amp;#62;", $formatted_comment);
$value_for_text_html_field = 0; // WRTODO: get rid of
}
elseif ($comment_format == 'auto')
{
$formatted_comment = nl2br($comment);
$value_for_text_html_field = 1;
}
else
{
$formatted_comment = $comment;
$value_for_text_html_field = 1;
}
}
unset($comment);
# Duplicate detection.
if ($on_duplicate != 'continue')
{
# Attempt to find a log entry made by the same user, for the same cache, with
@ -161,7 +239,7 @@ class WebService
# DO NOT guarantee that duplicate detection will succeed. If it doesn't,
# nothing bad happens (user will just post two similar log entries).
# Keep this simple!
$duplicate_uuid = Db::select_value("
select uuid
from cache_logs
@ -170,7 +248,7 @@ class WebService
and cache_id = '".mysql_real_escape_string($cache['internal_id'])."'
and type = '".mysql_real_escape_string(Okapi::logtypename2id($logtype))."'
and date = from_unixtime('".mysql_real_escape_string($when)."')
and text = '".mysql_real_escape_string($PSEUDOENCODED_comment)."'
and text = '".mysql_real_escape_string($formatted_comment)."'
".((Settings::get('OC_BRANCH') == 'oc.pl') ? "and deleted = 0" : "")."
limit 1
");
@ -187,9 +265,9 @@ class WebService
}
}
}
# Check if already found it (and make sure the user is not the owner).
if (($logtype == 'Found it') || ($logtype == "Didn't find it"))
{
$has_already_found_it = Db::select_value("
@ -206,12 +284,12 @@ class WebService
if ($user['uuid'] == $cache['owner']['uuid'])
throw new CannotPublishException(_("You are the owner of this cache. You may submit \"Comments\" only!"));
}
# Check if the user has already rated the cache. BTW: I don't get this one.
# If we already know, that the cache was NOT found yet, then HOW could the
# user submit a rating for it? Anyway, I will stick to the procedure
# found in log.php. On the bright side, it's fail-safe.
if ($rating)
{
$has_already_rated = Db::select_value("
@ -224,13 +302,13 @@ class WebService
if ($has_already_rated)
throw new CannotPublishException(_("You have already rated this cache once. Your rating cannot be changed."));
}
# If user wants to recommend...
if ($recommend)
{
# Do the same "fail-safety" check as we did for the rating.
$already_recommended = Db::select_value("
select 1
from cache_rating
@ -240,39 +318,39 @@ class WebService
");
if ($already_recommended)
throw new CannotPublishException(_("You have already recommended this cache once."));
# Check the number of recommendations.
$founds = $user['caches_found'] + 1; // +1, because he'll find THIS ONE in a moment, right?
$rcmds_left = floor($founds / 10.0) - $user['rcmds_given'];
if ($rcmds_left <= 0)
throw new CannotPublishException(_("You don't have any recommendations to give. Find more caches first!"));
}
# If user checked the "needs_maintenance" flag, we will shuffle things a little...
if ($needs_maintenance)
{
# If we're here, then we also know that the "Needs maintenance" log type is supported
# by this OC site. However, it's a separate log type, so we might have to submit
# two log types together:
if ($logtype == 'Comment')
{
# If user submits a "Comment", we'll just change its type to "Needs maintenance".
# Only one log entry will be issued.
$logtype = 'Needs maintenance';
$second_logtype = null;
$second_PSEUDOENCODED_comment = null;
$second_formatted_comment = null;
}
elseif ($logtype == 'Found it')
{
# If "Found it", then we'll issue two log entries: one "Found it" with the
# original comment, and second one "Needs maintenance" with empty comment.
$second_logtype = 'Needs maintenance';
$second_PSEUDOENCODED_comment = "";
$second_formatted_comment = "";
}
elseif ($logtype == "Didn't find it")
{
@ -281,10 +359,10 @@ class WebService
# will have an empty comment. We will move the comment to the second
# "Needs maintenance" log entry. (It's okay for this behavior to change
# in the future, but it seems natural to me.)
$second_logtype = 'Needs maintenance';
$second_PSEUDOENCODED_comment = $PSEUDOENCODED_comment;
$PSEUDOENCODED_comment = "";
$second_formatted_comment = $formatted_comment;
$formatted_comment = "";
}
else
throw new Exception();
@ -293,33 +371,37 @@ class WebService
{
# User didn't check the "Needs maintenance" flag OR "Needs maintenance" log type
# isn't supported by this server.
$second_logtype = null;
$second_PSEUDOENCODED_comment = null;
$second_formatted_comment = null;
}
# Finally! Insert the rows into the log entries table. Update
# cache stats and user stats.
$log_uuid = self::insert_log_row($request->consumer->key, $cache['internal_id'], $user['internal_id'], $logtype, $when, $PSEUDOENCODED_comment);
$log_uuid = self::insert_log_row(
$request->consumer->key, $cache['internal_id'], $user['internal_id'], $logtype,
$when, $formatted_comment, $value_for_text_html_field);
self::increment_cache_stats($cache['internal_id'], $when, $logtype);
self::increment_user_stats($user['internal_id'], $logtype);
if ($second_logtype != null)
{
# Reminder: This will never be called while SUPPORTS_LOGTYPE_NEEDS_MAINTENANCE is off.
self::insert_log_row($request->consumer->key, $cache['internal_id'], $user['internal_id'], $second_logtype, $when + 1, $second_PSEUDOENCODED_comment);
self::insert_log_row(
$request->consumer->key, $cache['internal_id'], $user['internal_id'], $second_logtype,
$when + 1, $second_formatted_comment, $value_for_text_html_field);
self::increment_cache_stats($cache['internal_id'], $when + 1, $second_logtype);
self::increment_user_stats($user['internal_id'], $second_logtype);
}
# Save the rating.
if ($rating)
{
# This code will be called for OCPL branch only. Earlier, we made sure,
# to set $rating to null, if we're running on OCDE.
# OCPL has a little strange way of storing cumulative rating. Instead
# of storing the sum of all ratings, OCPL stores the computed average
# and update it using multiple floating-point operations. Moreover,
@ -329,7 +411,7 @@ class WebService
# the rating can never be changed. It surely feels quite inconsistent,
# but presumably has some deep logic into it. See also here (Polish):
# http://wiki.opencaching.pl/index.php/Oceny_skrzynek
switch ($rating)
{
case 1: $db_score = -2.0; break;
@ -355,47 +437,57 @@ class WebService
);
");
}
# Save recommendation.
if ($recommend)
{
# Both OCPL and OCDE don't update any stats regarding the number of recommendations
# (or - if they do - they do so using triggers). In other words, this is the only
# query we have to execute here, regarding the recommendation.
Db::execute("
insert into cache_rating (user_id, cache_id)
values (
'".mysql_real_escape_string($user['internal_id'])."',
'".mysql_real_escape_string($cache['internal_id'])."'
);
");
if (Db::field_exists('cache_rating', 'rating_date'))
{
Db::execute("
insert into cache_rating (user_id, cache_id, rating_date)
values (
'".mysql_real_escape_string($user['internal_id'])."',
'".mysql_real_escape_string($cache['internal_id'])."',
from_unixtime('".mysql_real_escape_string($when)."')
);
");
}
else
{
Db::execute("
insert into cache_rating (user_id, cache_id)
values (
'".mysql_real_escape_string($user['internal_id'])."',
'".mysql_real_escape_string($cache['internal_id'])."'
);
");
}
}
# We need to delete the copy of stats-picture for this user. Otherwise,
# the legacy OC code won't detect that the picture needs to be refreshed.
$filepath = Okapi::get_var_dir().'/images/statpics/statpic'.$user['internal_id'].'.jpg';
if (file_exists($filepath))
unlink($filepath);
# Success. Return the uuid.
return $log_uuid;
}
private static $success_message = null;
public static function call(OkapiRequest $request)
{
# This is the "real" entry point. A wrapper for the _call method.
$langpref = $request->get_parameter('langpref');
if (!$langpref) $langpref = "en";
# Error messages thrown via CannotPublishException exceptions should be localized.
# They will be delivered for end user to display in his language.
Okapi::gettext_domain_init(explode("|", $langpref));
try
{
@ -421,7 +513,7 @@ class WebService
return Okapi::formatted_response($request, $result);
}
private static function increment_cache_stats($cache_internal_id, $when, $logtype)
{
if (Settings::get('OC_BRANCH') == 'oc.de')
@ -432,7 +524,7 @@ class WebService
else
{
# OCPL doesn't use triggers for this. We need to update manually.
if ($logtype == 'Found it')
{
Db::execute("
@ -465,7 +557,7 @@ class WebService
}
}
}
private static function increment_user_stats($user_internal_id, $logtype)
{
if (Settings::get('OC_BRANCH') == 'oc.de')
@ -476,7 +568,7 @@ class WebService
else
{
# OCPL doesn't have triggers for this. We need to update manually.
switch ($logtype)
{
case 'Found it': $field_to_increment = 'founds_count'; break;
@ -493,7 +585,7 @@ class WebService
");
}
}
private static function create_uuid()
{
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
@ -504,29 +596,33 @@ class WebService
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
private static function insert_log_row($consumer_key, $cache_internal_id, $user_internal_id, $logtype, $when, $PSEUDOENCODED_comment)
private static function insert_log_row(
$consumer_key, $cache_internal_id, $user_internal_id, $logtype, $when,
$formatted_comment, $text_html
)
{
$log_uuid = self::create_uuid();
Db::execute("
insert into cache_logs (uuid, cache_id, user_id, type, date, text, last_modified, date_created, node)
insert into cache_logs (uuid, cache_id, user_id, type, date, text, text_html, last_modified, date_created, node)
values (
'".mysql_real_escape_string($log_uuid)."',
'".mysql_real_escape_string($cache_internal_id)."',
'".mysql_real_escape_string($user_internal_id)."',
'".mysql_real_escape_string(Okapi::logtypename2id($logtype))."',
from_unixtime('".mysql_real_escape_string($when)."'),
'".mysql_real_escape_string($PSEUDOENCODED_comment)."',
'".mysql_real_escape_string($formatted_comment)."',
'".mysql_real_escape_string($text_html)."',
now(),
now(),
'".mysql_real_escape_string(Settings::get('OC_NODE_ID'))."'
);
");
$log_internal_id = Db::last_insert_id();
# Store additional information on consumer_key which have created this log entry.
# (Maybe we'll want to display this somewhere later.)
Db::execute("
insert into okapi_cache_logs (log_id, consumer_key)
values (
@ -534,7 +630,7 @@ class WebService
'".mysql_real_escape_string($consumer_key)."'
);
");
return $log_uuid;
}
}

View File

@ -14,10 +14,18 @@
log types which are used for Event Caches (we are planning to add this).</p>
</req>
<opt name='comment'>
<p>Text to be submitted with the log entry. Plain-text (no HTML).</p>
<p><b>Note:</b> Due to <a href='http://code.google.com/p/opencaching-api/issues/detail?id=124'>some issues</a>
(which we cannot currently fix), you MAY be allowed to use some basic HTML tags (on some OC installations).
However, you should not (this may stop working at any time).</p>
<p>Text to be submitted with the log entry.</p>
</opt>
<opt name='comment_format' default='auto'>
<p>Indicates the format of your <b>comment</b>. Three values allowed:
<b>auto</b>, <b>html</b> or <b>plaintext</b>. Usually, you should not
use the <b>auto</b> option, because its exact behavior is unspecified
and may depend on the installation
(<a href='https://code.google.com/p/opencaching-api/issues/detail?id=124'>more info</a>).</p>
<p><b>Important note:</b> The subset of allowed HTML elements is left undefined
and may change in the future. For future-compatibility, you should use only
basic formatting tags.</p>
</opt>
<opt name='when'>
<p>A date and time string. This should be in ISO 8601 format (currently any

View File

@ -39,14 +39,14 @@ class WebService
$offset = intval($offset);
if ($offset < 0)
throw new InvalidParam('offset', "'$offset'");
# Check if user exists and retrieve user's ID (this will throw
# a proper exception on invalid UUID).
$user = OkapiServiceRunner::call('services/users/user', new OkapiInternalRequest(
$request->consumer, null, array('user_uuid' => $user_uuid, 'fields' => 'internal_id')));
# User exists. Retrieving logs.
$rs = Db::query("
select cl.id, cl.uuid, cl.type, unix_timestamp(cl.date) as date, cl.text,
c.wp_oc as cache_code
@ -70,7 +70,7 @@ class WebService
'comment' => $row['text']
);
}
return Okapi::formatted_response($request, $results);
}
}

View File

@ -17,7 +17,7 @@ class WebService
'token_type' => 'request'
);
}
public static function call(OkapiRequest $request)
{
$verifier = $request->get_parameter('oauth_verifier');
@ -26,9 +26,9 @@ class WebService
# We require the 1.0a flow (throw an error when there is no oauth_verifier).
throw new ParamMissing("oauth_verifier");
}
$new_token = Okapi::$data_store->new_access_token($request->token, $request->consumer, $verifier);
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
$response->body = $new_token;

View File

@ -17,7 +17,7 @@ class WebService
'min_auth_level' => 0
);
}
public static function call(OkapiRequest $request)
{
$token_key = $request->get_parameter('oauth_token');
@ -28,12 +28,12 @@ class WebService
if (!$interactivity) $interactivity = 'minimal';
if (!in_array($interactivity, array('minimal', 'confirm_user')))
throw new InvalidParam('interactivity', $interactivity);
# Redirect to the "apps" folder. This is done there (not here)
# because: 1) we don't want any cookie or session-handling
# done in the "services" folder. 2) "services" don't display
# any interactive webpages, they just return the result.
return new OkapiRedirectResponse(Settings::get('SITE_URL')."okapi/apps/authorize".
"?oauth_token=".$token_key.(($langpref != null) ? "&langpref=".$langpref : "").
"&interactivity=".$interactivity);

View File

@ -16,7 +16,7 @@ class WebService
'min_auth_level' => 2
);
}
public static function call(OkapiRequest $request)
{
$callback = $request->get_parameter('oauth_callback');
@ -25,9 +25,9 @@ class WebService
# We require the 1.0a flow (throw an error when there is no oauth_callback).
throw new ParamMissing("oauth_callback");
}
$new_token = Okapi::$data_store->new_request_token($request->consumer, $callback);
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
$response->body = $new_token."&oauth_callback_confirmed=true";

View File

@ -23,7 +23,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
require_once('replicate_common.inc.php');
@ -31,23 +31,23 @@ class WebService
$since = $request->get_parameter('since');
if ($since === null) throw new ParamMissing('since');
if ((int)$since != $since) throw new InvalidParam('since');
# Let's check the $since parameter.
if (!ReplicateCommon::check_since_param($since))
throw new BadRequest("The 'since' parameter is too old. You must update your database more frequently.");
# Select a best chunk for the given $since, get the chunk from the database (or cache).
list($from, $to) = ReplicateCommon::select_best_chunk($since);
$clog_entries = ReplicateCommon::get_chunk($from, $to);
$result = array(
'changelog' => &$clog_entries,
'revision' => $to + 0,
'more' => $to < ReplicateCommon::get_revision(),
);
return Okapi::formatted_response($request, $result);
}
}

View File

@ -20,7 +20,7 @@ class WebService
'min_auth_level' => 1
);
}
private static function count_calls($consumer_key, $days)
{
return (
@ -43,18 +43,18 @@ class WebService
")
);
}
public static function call(OkapiRequest $request)
{
require_once('replicate_common.inc.php');
$data = Cache::get("last_fulldump");
if ($data == null)
throw new BadRequest("No fulldump found. Try again later. If this doesn't help ".
"contact site administrator and/or OKAPI developers.");
# Check consumer's quota
$please = $request->get_parameter('pleeaase');
if ($please != 'true')
{
@ -68,7 +68,7 @@ class WebService
if ($not_good)
throw new BadRequest("No more please. Seriously, dude...");
}
$response = new OkapiHttpResponse();
$response->content_type = $data['meta']['content_type'];
$response->content_disposition = 'attachment; filename="'.$data['meta']['public_filename'].'"';

View File

@ -24,7 +24,7 @@ class WebService
'min_auth_level' => 1
);
}
public static function call(OkapiRequest $request)
{
require_once('replicate_common.inc.php');
@ -46,7 +46,7 @@ class WebService
} else {
$result['latest_fulldump'] = null;
}
return Okapi::formatted_response($request, $result);
}
}

View File

@ -21,7 +21,7 @@ class ReplicateCommon
private static $chunk_size = 200;
private static $logged_cache_fields = 'code|names|location|type|status|url|owner|founds|notfounds|size|size2|oxsize|difficulty|terrain|rating|rating_votes|recommendations|req_passwd|descriptions|hints|images|trackables_count|trackables|alt_wpts|last_found|last_modified|date_created|date_hidden';
private static $logged_log_entry_fields = 'uuid|cache_code|date|user|type|was_recommended|comment';
/** Return current (greatest) changelog revision number. */
public static function get_revision()
{
@ -42,7 +42,7 @@ class ReplicateCommon
return $cache;
}
/**
* Compare two dictionaries. Return the $new dictionary with all unchanged
* keys removed. Only the changed ones will remain.
@ -64,7 +64,7 @@ class ReplicateCommon
$changed[$key] = $new[$key];
return $changed;
}
/** Check for modifications in the database and update the changelog table accordingly. */
public static function update_clog_table()
{
@ -72,19 +72,19 @@ class ReplicateCommon
$last_update = Okapi::get_var('last_clog_update');
if ($last_update === null)
$last_update = Db::select_value("select date_add(now(), interval -1 day)");
# Usually this will be fast. But, for example, if admin changes ALL the
# caches, this will take forever. But we still want it to finish properly
# without interruption.
set_time_limit(0);
ignore_user_abort(true);
ignore_user_abort(true);
# Get the list of modified cache codes. Split it into groups of N cache codes.
# Note that we should include ALL cache codes in this particular query, not
# only "status in (1,2,3)". This way, when the cache changes its status, e.g.
# from 3 to 6, changelog will include a proper "delete" statement.
$cache_codes = Db::select_column("
select wp_oc
from caches
@ -92,17 +92,17 @@ class ReplicateCommon
");
$cache_code_groups = Okapi::make_groups($cache_codes, 50);
unset($cache_codes);
# For each group, update the changelog table accordingly.
foreach ($cache_code_groups as $cache_codes)
{
self::generate_changelog_entries('services/caches/geocaches', 'geocache', 'cache_codes',
'code', $cache_codes, self::$logged_cache_fields, false, true, null);
}
# Same as above, for log entries.
$offset = 0;
while (true)
{
@ -129,7 +129,7 @@ class ReplicateCommon
# So the above queries won't detect them. We need to run one more.
# We will assume there are not so many of them and we don't have to
# split them in groups as we did above.
$DELETED_uuids = Db::select_column("
select uuid
from cache_logs_archived
@ -138,9 +138,9 @@ class ReplicateCommon
self::generate_changelog_entries('services/logs/entries', 'log', 'log_uuids',
'uuid', $DELETED_uuids, self::$logged_log_entry_fields, false, true, 3600);
}
# Update state variables.
Okapi::set_var("last_clog_update", $now);
$revision = Db::select_value("select max(id) from okapi_clog");
Okapi::set_var("clog_revision", $revision);
@ -149,7 +149,7 @@ class ReplicateCommon
/**
* Scan the database and compare the current values of old entries to
* the cached values of the same entries. If differences found, update
* okapi_syncbase accordingly, and email the admins.
* okapi_syncbase accordingly, and email the OKAPI developers.
*
* Currently, only caches are checked (log entries are not).
*/
@ -157,12 +157,12 @@ class ReplicateCommon
{
set_time_limit(0);
ignore_user_abort(true);
# We will SKIP the entries which have been modified SINCE one day ago.
# Such entries might have not been seen by the update_clog_table() yet
# (e.g. other long-running cronjob is preventing update_clog_table from
# running).
$cache_codes = Db::select_column("
select wp_oc
from caches
@ -170,11 +170,12 @@ class ReplicateCommon
");
$cache_code_groups = Okapi::make_groups($cache_codes, 50);
unset($cache_codes);
# For each group, get the changelog entries, but don't store them
# (the "fulldump" mode). Instead, just update the okapi_syncbase column.
$sum = 0;
$two_examples = array();
foreach ($cache_code_groups as $cache_codes)
{
$entries = self::generate_changelog_entries(
@ -186,6 +187,14 @@ class ReplicateCommon
if ($entry['object_type'] != 'geocache')
continue;
$cache_code = $entry['object_key']['code'];
# We will story the first and the last entry in the $two_examples
# vars which is to be emailed to OKAPI developers.
if (count($two_examples) == 0)
$two_examples[0] = $entry; /* The first entry */
$two_examples[1] = $entry; /* The last entry */
Db::execute("
update caches
set okapi_syncbase = now()
@ -196,16 +205,20 @@ class ReplicateCommon
}
if ($sum > 0)
{
$message = (
"Number of invalid entries scheduled to be fixed: $sum\n".
"Approx revision of the first one: ".Okapi::get_var('clog_revision')."\n\n".
"Two examples:\n\n".print_r($two_examples, true)
);
Okapi::mail_from_okapi(
"rygielski@mimuw.edu.pl",
"verify_clog_consistency",
"Number of invalid entries fixed: $sum\n\n".
print_r(Db::select_all("select * from okapi_vars"), true)
"verify_clog_consistency - ".Okapi::get_normalized_site_name(),
$message, true
);
}
}
/**
* Generate OKAPI changelog entries. This method will call $feeder_method OKAPI
* service with the following parameters: array($feeder_keys_param => implode('|', $key_values),
@ -223,7 +236,7 @@ class ReplicateCommon
$key_name, $key_values, $fields, $fulldump_mode, $use_cache, $cache_timeout = 86400)
{
# Retrieve the previous versions of all objects from OKAPI cache.
if ($use_cache)
{
$cache_keys1 = array();
@ -242,10 +255,10 @@ class ReplicateCommon
unset($cache_keys1);
unset($cache_keys2);
}
# Get the current values for objects. Compare them with their previous versions
# and generate changelog entries.
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
$current_values = OkapiServiceRunner::call($feeder_method, new OkapiInternalRequest(
new OkapiInternalConsumer(), null, array($feeder_keys_param => implode("|", $key_values),
@ -310,7 +323,7 @@ class ReplicateCommon
}
}
}
if ($fulldump_mode)
{
return $entries;
@ -318,7 +331,7 @@ class ReplicateCommon
else
{
# Save the entries to the clog table.
if (count($entries) > 0)
{
$data_values = array();
@ -329,9 +342,9 @@ class ReplicateCommon
values ('".implode("'),('", array_map('mysql_real_escape_string', $data_values))."');
");
}
# Update the values kept in OKAPI cache.
if ($use_cache)
{
Cache::set_many($cached_values1, $cache_timeout);
@ -339,7 +352,7 @@ class ReplicateCommon
}
}
}
/**
* Check if the 'since' parameter is up-do-date. If it is not, then it means
* that the user waited too long and he has to download the fulldump again.
@ -350,16 +363,16 @@ class ReplicateCommon
select id from okapi_clog where id > '".mysql_real_escape_string($since)."' limit 1
");
if ($first_id === null)
return true; # okay, since points to the newest revision
return true; # okay, since points to the newest revision
if ($first_id == $since + 1)
return true; # okay, revision $since + 1 is present
# If we're here, then this means that $first_id > $since + 1.
# Revision $since + 1 is already deleted, $since must be too old!
return false;
}
/**
* Select best chunk for a given $since parameter. This function will try to select
* one chunk for different values of $since parameter, this is done in order to
@ -377,7 +390,7 @@ class ReplicateCommon
# which we probably already have in cache (and this includes the 50 which the
# user wants), then we'll give him 80. If user wants less than half of what we
# have (ex. 30), then we'll give him only his 30.
if ($current_revision - $since > $since - $last_chunk_cut)
return array($last_chunk_cut + 1, $current_revision);
else
@ -386,7 +399,7 @@ class ReplicateCommon
$prev_chunk_cut = $since - ($since % self::$chunk_size);
return array($prev_chunk_cut + 1, $prev_chunk_cut + self::$chunk_size);
}
/**
* Return changelog chunk, starting at $from, ending as $to.
*/
@ -396,9 +409,9 @@ class ReplicateCommon
return array();
if ($to - $from > self::$chunk_size)
throw new Exception("You should not get chunksize bigger than ".self::$chunk_size." entries at one time.");
# Check if we already have this chunk in cache.
$cache_key = 'clog_chunk#'.$from.'-'.$to;
$chunk = Cache::get($cache_key);
if ($chunk === null)
@ -414,23 +427,23 @@ class ReplicateCommon
{
$chunk[] = unserialize(gzinflate($row['data']));
}
# Cache timeout depends on the chunk starting and ending point. Chunks
# which start and end on the boundries of chunk_size should be cached
# longer (they can be accessed even after 10 days). Other chunks won't
# be ever accessed after the next revision appears, so there is not point
# in storing them that long.
if (($from % self::$chunk_size === 0) && ($to % self::$chunk_size === 0))
$timeout = 10 * 86400;
else
$timeout = 86400;
Cache::set($cache_key, $chunk, $timeout);
}
return $chunk;
}
/**
* Generate a new fulldump file and put it into the OKAPI cache table.
* Return the cache key.
@ -438,22 +451,22 @@ class ReplicateCommon
public static function generate_fulldump()
{
# First we will create temporary files, then compress them in the end.
$revision = self::get_revision();
$generated_at = date('c', time());
$dir = Okapi::get_var_dir()."/okapi-db-dump";
$i = 1;
$json_files = array();
# Cleanup (from a previous, possibly unsuccessful, execution)
shell_exec("rm -f $dir/*");
shell_exec("rmdir $dir");
shell_exec("mkdir $dir");
shell_exec("chmod 777 $dir");
# Geocaches
$cache_codes = Db::select_column("select wp_oc from caches");
$cache_code_groups = Okapi::make_groups($cache_codes, 200);
unset($cache_codes);
@ -473,10 +486,10 @@ class ReplicateCommon
$i++;
}
unset($cache_code_groups);
# Log entries. We cannot load all the uuids at one time, this would take
# too much memory. Hence the offset/limit loop.
$offset = 0;
while (true)
{
@ -508,9 +521,9 @@ class ReplicateCommon
$i++;
}
}
# Package data.
$metadata = array(
'revision' => $revision,
'data_files' => $json_files,
@ -521,32 +534,32 @@ class ReplicateCommon
),
);
file_put_contents("$dir/index.json", json_encode($metadata));
# Compute uncompressed size.
$size = filesize("$dir/index.json");
foreach ($json_files as $filename)
$size += filesize("$dir/$filename");
# Create JSON archive. We use tar options: -j for bzip2, -z for gzip
# (bzip2 is MUCH slower).
$use_bzip2 = true;
$dumpfilename = "okapi-dump.tar.".($use_bzip2 ? "bz2" : "gz");
shell_exec("tar --directory $dir -c".($use_bzip2 ? "j" : "z")."f $dir/$dumpfilename index.json ".implode(" ", $json_files). " 2>&1");
# Delete temporary files.
shell_exec("rm -f $dir/*.json");
# Move the archive one directory upwards, replacing the previous one.
# Remove the temporary directory.
shell_exec("mv -f $dir/$dumpfilename ".Okapi::get_var_dir());
shell_exec("rmdir $dir");
# Update the database info.
$metadata['meta']['filepath'] = Okapi::get_var_dir().'/'.$dumpfilename;
$metadata['meta']['content_type'] = ($use_bzip2 ? "application/octet-stream" : "application/x-gzip");
$metadata['meta']['public_filename'] = 'okapi-dump-r'.$metadata['revision'].'.tar.'.($use_bzip2 ? "bz2" : "gz");

View File

@ -25,9 +25,9 @@ class WebService
$internal_id = $request->get_parameter('internal_id');
if (!$internal_id) throw new ParamMissing('internal_id');
$fields = $request->get_parameter('fields');
# There's no need to validate the fields parameter.
$results = OkapiServiceRunner::call('services/users/by_internal_ids', new OkapiInternalRequest(
$request->consumer, $request->token, array('internal_ids' => $internal_id,
'fields' => $fields)));

View File

@ -31,10 +31,10 @@ class WebService
$fields = $request->get_parameter('fields');
if (!$fields)
throw new ParamMissing('fields');
# There's no need to validate the fields parameter as the 'users'
# method does this (it will raise a proper exception on invalid values).
$rs = Db::query("
select user_id, uuid
from user
@ -46,12 +46,12 @@ class WebService
$internalid2useruuid[$row['user_id']] = $row['uuid'];
}
mysql_free_result($rs);
# Retrieve data on given user_uuids.
$id_results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest(
$request->consumer, $request->token, array('user_uuids' => implode("|", array_values($internalid2useruuid)),
'fields' => $fields)));
# Map user_uuids to internal_ids. Also check which internal_ids were not found
# and mark them with null.
$results = array();
@ -62,7 +62,7 @@ class WebService
else
$results[$internal_id] = $id_results[$internalid2useruuid[$internal_id]];
}
return Okapi::formatted_response($request, $results);
}
}

View File

@ -28,9 +28,9 @@ class WebService
$username = $request->get_parameter('username');
if (!$username) throw new ParamMissing('username');
$fields = $request->get_parameter('fields');
# There's no need to validate the fields parameter.
$results = OkapiServiceRunner::call('services/users/by_usernames', new OkapiInternalRequest(
$request->consumer, $request->token, array('usernames' => $username,
'fields' => $fields)));

View File

@ -31,10 +31,10 @@ class WebService
$fields = $request->get_parameter('fields');
if (!$fields)
throw new ParamMissing('fields');
# There's no need to validate the fields parameter as the 'users'
# method does this (it will raise a proper exception on invalid values).
$rs = Db::query("
select username, uuid
from user
@ -46,12 +46,12 @@ class WebService
$username2useruuid[$row['username']] = $row['uuid'];
}
mysql_free_result($rs);
# Retrieve data on given user_uuids.
$id_results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest(
$request->consumer, $request->token, array('user_uuids' => implode("|", array_values($username2useruuid)),
'fields' => $fields)));
# Map user_uuids to usernames. Also check which usernames were not found
# and mark them with null.
$results = array();
@ -62,7 +62,7 @@ class WebService
else
$results[$username] = $id_results[$username2useruuid[$username]];
}
return Okapi::formatted_response($request, $results);
}
}

View File

@ -41,10 +41,10 @@ class WebService
}
}
$fields = $request->get_parameter('fields');
# There's no need to validate the fields parameter as the 'users'
# method does this (it will raise a proper exception on invalid values).
$results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest(
$request->consumer, $request->token, array('user_uuids' => $user_uuid,
'fields' => $fields)));

View File

@ -19,10 +19,10 @@ class WebService
'min_auth_level' => 1
);
}
private static $valid_field_names = array('uuid', 'username', 'profile_url', 'internal_id', 'is_admin',
'caches_found', 'caches_notfound', 'caches_hidden', 'rcmds_given');
public static function call(OkapiRequest $request)
{
$user_uuids = $request->get_parameter('user_uuids');
@ -78,21 +78,21 @@ class WebService
$results[$row['uuid']] = $entry;
}
mysql_free_result($rs);
# caches_found, caches_notfound, caches_hidden
if (in_array('caches_found', $fields) || in_array('caches_notfound', $fields) || in_array('caches_hidden', $fields)
|| in_array('rcmds_given', $fields))
{
# We will load all these stats together. Then we may remove these which
# the user doesn't need.
$extras = array();
if (Settings::get('OC_BRANCH') == 'oc.pl')
{
# OCPL stores user stats in 'user' table.
$rs = Db::query("
select user_id, founds_count, notfounds_count, hidden_count
from user
@ -102,7 +102,7 @@ class WebService
else
{
# OCDE stores user stats in 'stat_user' table.
$rs = Db::query("
select
u.user_id,
@ -116,7 +116,7 @@ class WebService
where u.user_id in ('".implode("','", array_map('mysql_real_escape_string', array_keys($id2uuid)))."')
");
}
while ($row = mysql_fetch_assoc($rs))
{
$extras[$row['user_id']] = array();;
@ -126,7 +126,7 @@ class WebService
$extra_ref['caches_hidden'] = 0 + $row['hidden_count'];
}
mysql_free_result($rs);
if (in_array('rcmds_given', $fields))
{
$rs = Db::query("
@ -143,9 +143,9 @@ class WebService
$extra_ref['rcmds_given'] = isset($rcmds_counts[$user_id]) ? 0 + $rcmds_counts[$user_id] : 0;
}
}
# "Apply" only those fields which the consumer wanted.
foreach (array('caches_found', 'caches_notfound', 'caches_hidden', 'rcmds_given') as $field)
{
if (!in_array($field, $fields))
@ -154,13 +154,13 @@ class WebService
$result_ref[$field] = $extras[$uuid2id[$uuid]][$field];
}
}
# Check which user IDs were not found and mark them with null.
foreach ($user_uuids as $user_uuid)
if (!isset($results[$user_uuid]))
$results[$user_uuid] = null;
return Okapi::formatted_response($request, $results);
}
}

View File

@ -197,7 +197,7 @@ final class Settings
throw new Exception("IMAGES_DIR cannot be null. Please provide a valid directory.");
foreach ($dict as $k => $v)
if ((strpos($k, '_DIR') !== false) && ($k[strlen($k) - 1] == '/'))
throw new Exception("All *_DIR settings may not end with a slash. Check $k.");
throw new Exception("None of the *_DIR settings may end with a slash. Check $k.");
$notnull = array('OC_COOKIE_NAME', 'DB_SERVER', 'DB_NAME', 'DB_USERNAME', 'SITE_URL', 'OC_NODE_ID');
foreach ($notnull as $k)
if ($dict[$k] === null)

View File

@ -26,6 +26,7 @@ class OkapiUrls
'^devel/dbstruct$' => 'devel/dbstruct',
'^devel/cronreport$' => 'devel/cronreport',
'^devel/tilereport$' => 'devel/tilereport',
'^devel/clogentry$' => 'devel/clogentry',
# For debugging TileMap performance only.
// '^tilestress$' => 'tilestress',

View File

@ -22,13 +22,13 @@ class View
$locales = array();
foreach (Locales::$languages as $lang => $attrs)
$locales[$attrs['locale']] = $attrs;
# Current implementation of the "interactivity" parameter is: If developer
# wants to "confirm_user", then just log out the current user before we
# continue.
$force_relogin = (isset($_GET['interactivity']) && $_GET['interactivity'] == 'confirm_user');
$token = Db::select_row("
select
t.`key` as `key`,
@ -45,14 +45,14 @@ class View
and t.consumer_key = c.`key`
and t.user_id is null
");
$callback_concat_char = (strpos($token['callback'], '?') === false) ? "?" : "&";
if (!$token)
{
# Probably Request Token has expired. This will be usually viewed
# by the user, who knows nothing on tokens and OAuth. Let's be nice then!
$vars = array(
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
'token' => $token,
@ -69,14 +69,14 @@ class View
Okapi::gettext_domain_restore();
return $response;
}
# Determine which user is logged in to OC.
require_once($GLOBALS['rootpath']."okapi/lib/oc_session.php");
$OC_user_id = OCSession::get_user_id();
# Ensure a user is logged in (or force re-login).
if ($force_relogin || ($OC_user_id == null))
{
if ($force_relogin)
@ -85,14 +85,14 @@ class View
# The logout.php DOES NOT support the "target" parameter, so we
# can't just call it. The only thing that comes to mind is...
# Destroy EVERYTHING.
$past = time() - 86400;
foreach ($_COOKIE as $key => $value)
setcookie($key, $value, $past, '/');
}
# We should be logged out now. Let's login again.
$after_login = "okapi/apps/authorize?oauth_token=$token_key".(($langpref != Settings::get('SITELANG'))?"&langpref=".$langpref:"");
$login_url = Settings::get('SITE_URL')."login.php?target=".urlencode($after_login)
."&langpref=".$langpref;
@ -117,11 +117,11 @@ class View
{
# Not yet authorized, but user have just submitted the authorization form.
# WRTODO: CSRF protection
if ($_POST['authorization_result'] == 'granted')
{
Db::execute("
insert into okapi_authorizations (consumer_key, user_id)
insert ignore into okapi_authorizations (consumer_key, user_id)
values (
'".mysql_real_escape_string($token['consumer_key'])."',
'".mysql_real_escape_string($OC_user_id)."'
@ -133,7 +133,7 @@ class View
{
# User denied access. Nothing sensible to do now. Will try to report
# back to the Consumer application with an error.
if ($token['callback']) {
return new OkapiRedirectResponse($token['callback'].$callback_concat_char."error=access_denied");
} else {
@ -162,17 +162,17 @@ class View
return $response;
}
}
# User granted access. Now we can authorize the Request Token.
Db::execute("
update okapi_tokens
set user_id = '".mysql_real_escape_string($OC_user_id)."'
where `key` = '".mysql_real_escape_string($token_key)."';
");
# Redirect to the callback_url.
if ($token['callback']) {
return new OkapiRedirectResponse($token['callback'].$callback_concat_char."oauth_token=".$token_key."&oauth_verifier=".$token['verifier']);
} else {

View File

@ -32,7 +32,7 @@
<a href='<?= $vars['okapi_base_url'] ?>'><img src='<?= $vars['okapi_base_url'] ?>static/logo-xsmall.gif' alt='OKAPI' style='float: right; margin-left: 10px;'></a>
<a href='/'><img src="/images/oc_logo.png" alt='OpenCaching' style='float: left; margin-right: 10px'></a>
<a class='opencaching'><?= $vars['site_name'] ?></a>
<div style='float: right; clear: right; margin: 10px 0 10px 30px'>
Choose your language:
<select id='langpref' style='border: 1px solid #ccc'>
@ -41,7 +41,7 @@
<? } ?>
</select>
</div>
<? if (isset($vars['token_expired']) && $vars['token_expired']) { ?>
<h1 style='clear: both'><?= _("Expired request") ?></h1>
<p><?= _("Unfortunately, the request has expired. Please try again.") ?></p>

View File

@ -18,7 +18,7 @@ class View
$verifier = isset($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : '';
$langpref = isset($_GET['langpref']) ? $_GET['langpref'] : Settings::get('SITELANG');
$langprefs = explode("|", $langpref);
$token = Db::select_row("
select
c.`key` as consumer_key,
@ -39,7 +39,7 @@ class View
# just redirect to the OpenCaching main page.
return new OkapiRedirectResponse(Settings::get('SITE_URL'));
}
$vars = array(
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
'token' => $token,

View File

@ -19,20 +19,20 @@
.okapi .pin { margin: 20px 20px 20px 0; background: #eee; border: 1px solid #ccc; padding: 20px 40px; text-align: center; font-size: 24px; }
</style>
<body>
<div class='okapi'>
<a href='<?= $vars['okapi_base_url'] ?>'><img src='<?= $vars['okapi_base_url'] ?>static/logo-xsmall.gif' alt='OKAPI' style='float: right; margin-left: 10px;'></a>
<a href='/'><img src="/images/oc_logo.png" alt='OpenCaching' style='float: left; margin-right: 10px'></a>
<a class='opencaching'><?= $vars['site_name'] ?></a>
<h1 style='clear: both'><?= _("Access successfully granted") ?></h1>
<?= sprintf(_("
<p><b>You've just granted %s application access to your %s account.</b>
To complete the operation, go back to %s and enter the following PIN code:</p>
"), $vars['token']['consumer_name'], $vars['site_name'], $vars['token']['consumer_name']) ?>
<div class='pin'><?= $vars['verifier'] ?></div>
<p><a href='/'><?= $vars['site_name'] ?></a></p>
</div>

View File

@ -17,9 +17,9 @@ class View
{
$langpref = isset($_GET['langpref']) ? $_GET['langpref'] : Settings::get('SITELANG');
$langprefs = explode("|", $langpref);
# Determine which user is logged in to OC.
require_once($GLOBALS['rootpath']."okapi/lib/oc_session.php");
$OC_user_id = OCSession::get_user_id();
@ -29,9 +29,9 @@ class View
$login_url = Settings::get('SITE_URL')."login.php?target=".urlencode($after_login);
return new OkapiRedirectResponse($login_url);
}
# Get the list of authorized apps.
$rs = Db::query("
select c.`key`, c.name, c.url
from
@ -49,7 +49,7 @@ class View
while ($row = mysql_fetch_assoc($rs))
$vars['apps'][] = $row;
mysql_free_result($rs);
$response = new OkapiHttpResponse();
$response->content_type = "text/html; charset=utf-8";
ob_start();

View File

@ -25,7 +25,7 @@
<a href='<?= $vars['okapi_base_url'] ?>'><img src='<?= $vars['okapi_base_url'] ?>static/logo-xsmall.gif' alt='OKAPI' style='float: right; margin-left: 10px;'></a>
<a href='/'><img src="<?= $vars['okapi_base_url'] ?>static/oc_logo.png" alt='OpenCaching' style='float: left; margin-right: 10px'></a>
<a class='opencaching'><?= $vars['site_name'] ?></a>
<h1 style='clear: both'><?= _("Your external applications") ?></h1>
<? if (count($vars['apps']) > 0) { ?>
<?= sprintf(_("

View File

@ -16,23 +16,23 @@ class View
public static function call()
{
# Determine which user is logged in to OC.
require_once($GLOBALS['rootpath']."okapi/lib/oc_session.php");
$OC_user_id = OCSession::get_user_id();
# Ensure a user is logged in.
if ($OC_user_id == null)
{
$after_login = "okapi/apps/"; # it is correct, if you're wondering
$login_url = Settings::get('SITE_URL')."login.php?target=".urlencode($after_login);
return new OkapiRedirectResponse($login_url);
}
$consumer_key = isset($_REQUEST['consumer_key']) ? $_REQUEST['consumer_key'] : '';
# Just remove app (if it doesn't exist - nothing wrong will happen anyway).
Db::execute("
delete from okapi_tokens
where
@ -45,9 +45,9 @@ class View
user_id = '".mysql_real_escape_string($OC_user_id)."'
and consumer_key = '".mysql_real_escape_string($consumer_key)."'
");
# Redirect back to the apps page.
return new OkapiRedirectResponse(Settings::get('SITE_URL')."okapi/apps/");
}
}

View File

@ -26,13 +26,13 @@ class View
ignore_user_abort(true);
set_time_limit(0);
header("Content-Type: text/plain; charset=utf-8");
# Uncomment the following if you want to debug a specific cronjob. It will be run
# every 5 minutes (run 'crontab -e' to change or disable it) AND additionally
# every time you visit http://yoursite/okapi/cron5
# require_once($GLOBALS['rootpath']."okapi/cronjobs.php"); CronJobController::force_run("JOB_NAME"); die();
Okapi::execute_cron5_cronjobs();
}
}

View File

@ -1,4 +1,4 @@
View in this directory are NOT services. They can disappear at any time.
Views in this directory are NOT services. They can disappear at any time.
These views help us develop OKAPI in a proper way across different OC
installations. They do not (or at least should not) expose any sensitive

View File

@ -21,17 +21,17 @@ class View
{
# This is a hidden page for OKAPI developers. It will list all
# attributes defined in this OC installation (and some other stuff).
ob_start();
print "Cache Types:\n\n";
foreach (self::get_all_cachetypes() as $id => $name)
print "$id: $name\n";
print "\nLog Types:\n\n";
foreach (self::get_all_logtypes() as $id => $name)
print "$id: $name\n";
print "\nAttributes:\n\n";
$dict = Okapi::get_all_atribute_names();
foreach ($dict as $internal_id => $langs)
@ -46,6 +46,19 @@ class View
foreach ($langkeys as $langkey)
print " $langkey: ".$langs[$langkey]."\n";
}
foreach ($dict as $internal_id => $langs)
{
print "<attr okapi_attr_id=\"TODO\">\n";
print "\t<groundspeak id=\"TODO\" inc=\"TODO\" name=\"TODO\" />\n";
print "\t<opencaching site_url=\"SITEURLTODO\" id=\"$internal_id\" />\n";
$langkeys = array_keys($langs);
usort($langkeys, function($a, $b) {
return ($a == "en") ? -1 : (($a == $b) ? 0 : (($a < $b) ? -1 : 1));
});
foreach ($langkeys as $langkey)
print "\t<name lang=\"$langkey\">".$langs[$langkey]."</name>\n";
print "</attr>\n";
}
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
@ -61,13 +74,13 @@ class View
if (Settings::get('OC_BRANCH') == 'oc.pl')
{
# OCPL branch does not store cache types in many languages (just two).
$rs = Db::query("select id, en from cache_type order by id");
}
else
{
# OCDE branch uses translation tables.
$rs = Db::query("
select
ct.id,
@ -80,14 +93,14 @@ class View
order by ct.id
");
}
$dict = array();
while ($row = mysql_fetch_assoc($rs)) {
$dict[$row['id']] = $row['en'];
}
return $dict;
}
/**
* Get an array of all site-specific log-types (id => name in English).
*/
@ -96,13 +109,13 @@ class View
if (Settings::get('OC_BRANCH') == 'oc.pl')
{
# OCPL branch does not store cache types in many languages (just two).
$rs = Db::query("select id, en from log_types order by id");
}
else
{
# OCDE branch uses translation tables.
$rs = Db::query("
select
lt.id,
@ -115,7 +128,7 @@ class View
order by lt.id
");
}
$dict = array();
while ($row = mysql_fetch_assoc($rs)) {
$dict[$row['id']] = $row['en'];

View File

@ -0,0 +1,34 @@
<?php
namespace okapi\views\devel\clogentry;
use Exception;
use okapi\Okapi;
use okapi\Cache;
use okapi\Db;
use okapi\OkapiRequest;
use okapi\OkapiRedirectResponse;
use okapi\OkapiHttpResponse;
use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\OkapiServiceRunner;
use okapi\OkapiInternalRequest;
use okapi\Settings;
class View
{
public static function call()
{
$tmp = Db::select_value("
select data
from okapi_clog
where id='".mysql_real_escape_string($_GET['id'])."'
");
$data = unserialize(gzinflate($tmp));
$response = new OkapiHttpResponse();
$response->content_type = "application/json; charset=utf-8";
$response->body = json_encode($data);
return $response;
}
}

View File

@ -181,13 +181,13 @@ class dbStructUpdater
}
return $result;
}
/**
* Gets structured general info about the databases diff :
* array(sourceOrphans=>array(...), destOrphans=>array(...), different=>array(...))
*/
function getDiffInfo($compRes)
{
{
if (!is_array($compRes))
{
return false;
@ -251,10 +251,10 @@ class dbStructUpdater
$info['sourceOrphan'] = true;
}
else
{
{
$destSql = $this->getTabSql($this->destStruct, $tab, true);
$sourceSql = $this->getTabSql($this->sourceStruct, $tab, true);
$diffs = $this->compareSql($sourceSql, $destSql);
$diffs = $this->compareSql($sourceSql, $destSql);
if ($diffs===false)
{
trigger_error('[WARNING] error parsing definition of table "'.$tab.'" - skipped');
@ -262,8 +262,8 @@ class dbStructUpdater
}
elseif (!empty($diffs))//not empty array
{
$info['differs'] = $diffs;
}
$info['differs'] = $diffs;
}
else continue;//empty array
}
$result[$tab] = $info;
@ -302,7 +302,7 @@ class dbStructUpdater
$result = '';
/* create table should be single line in this case*/
//1 - part before database, 2-database name, 3 - part after database
if (preg_match('/(CREATE(?:\s*TEMPORARY)?\s*TABLE\s*(?:IF NOT EXISTS\s*)?)(?:`?(\w+)`?\.)?(`?('.$tab.')`?(\W|$))/i', $struct, $m, PREG_OFFSET_CAPTURE))
if (preg_match('/(CREATE(?:\s*TEMPORARY)?\s*TABLE\s*(?:IF NOT EXISTS\s*)?)(?:`?(\w+)`?\.)?(`?('.$tab.')`?(\W|$))/i', $struct, $m, PREG_OFFSET_CAPTURE))
{
$tableDef = $m[0][0];
$start = $m[0][1];
@ -322,13 +322,13 @@ class dbStructUpdater
if ($database && $removeDatabase)
{
$result = str_replace($tableDef, $m[1][0].$m[3][0], $result);
}
}
return $result;
}
/**
* Splits table sql into indexed array
*
*
*/
function splitTabSql($sql)
{
@ -387,7 +387,7 @@ class dbStructUpdater
*/
function compareSql($sourceSql, $destSql)//$sourceSql, $destSql
{
$result = array();
$result = array();
//split with comma delimiter, not line breaks
$sourceParts = $this->splitTabSql($sourceSql);
if ($sourceParts===false)//error parsing sql
@ -404,13 +404,13 @@ class dbStructUpdater
$sourcePartsIndexed = array();
$destPartsIndexed = array();
foreach($sourceParts as $line)
{
{
$lineInfo = $this->processLine($line);
if (!$lineInfo) continue;
$sourcePartsIndexed[$lineInfo['key']] = $lineInfo['line'];
}
foreach($destParts as $line)
{
{
$lineInfo = $this->processLine($line);
if (!$lineInfo) continue;
$destPartsIndexed[$lineInfo['key']] = $lineInfo['line'];
@ -427,7 +427,7 @@ class dbStructUpdater
$inDest= in_array($key, $destKeys);
$sourceOrphan = $inSource && !$inDest;
$destOrphan = $inDest && !$inSource;
$different = $inSource && $inDest &&
$different = $inSource && $inDest &&
strcasecmp($this->normalizeString($destPartsIndexed[$key]), $this->normalizeString($sourcePartsIndexed[$key]));
if ($sourceOrphan)
{
@ -657,11 +657,11 @@ class dbStructUpdater
$rbs = '\\\\'; //reg - escaped backslash
$regPrefix = "(?<!$rbs)(?:$rbs{2})*";
$reg = $regPrefix.'("|\')|(/\\*)|(\\*/)|(-- )|(\r\n|\r|\n)|';
if ($skipInBrackets)
if ($skipInBrackets)
{
$reg.='(\(|\))|';
}
else
else
{
$reg.='()';
}
@ -730,7 +730,7 @@ class dbStructUpdater
}
return false;
}
/**
* works the same as getDelimPos except returns position of the first occurence of the delimiter starting from
* the end of the string

View File

@ -21,11 +21,11 @@ class View
{
# This is a hidden page for OKAPI developers. It will output a cronjobs
# report. This is useful for debugging.
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
ob_start();
require_once($GLOBALS['rootpath']."okapi/cronjobs.php");
$schedule = Cache::get("cron_schedule");
if ($schedule == null)

View File

@ -14,6 +14,7 @@ use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\OkapiServiceRunner;
use okapi\OkapiInternalRequest;
use okapi\BadRequest;
class View
{
@ -22,22 +23,22 @@ class View
# This is a hidden page for OKAPI developers. It will output a complete
# structure of the database. This is useful for making OKAPI compatible
# across different OC installations.
$user = Settings::get('DB_USERNAME');
$password = Settings::get('DB_PASSWORD');
$dbname = Settings::get('DB_NAME');
$struct = shell_exec("mysqldump --no-data -u$user -p$password $dbname");
# Remove the "AUTO_INCREMENT=..." values. They break the diffs.
$struct = preg_replace("/ AUTO_INCREMENT=([0-9]+)/i", "", $struct);
# This method can be invoked with "compare_to" parameter, which points to
# an alternate database structure (probably generated by the same script
# in other OKAPI instance). When invoked this way, we will attempt to
# generate SQL script which alters LOCAL database is such a way that it
# will become THE OTHER database.
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
if (isset($_GET['compare_to']))
@ -45,7 +46,11 @@ class View
$scheme = parse_url($_GET['compare_to'], PHP_URL_SCHEME);
if (in_array($scheme, array('http', 'https')))
{
$alternate_struct = @file_get_contents($_GET['compare_to']);
try {
$alternate_struct = @file_get_contents($_GET['compare_to']);
} catch (Exception $e) {
throw new BadRequest("Failed to load ".$_GET['compare_to']);
}
$response->body =
"-- Automatically generated database diff. Use with caution!\n".
"-- Differences obtained with help of cool library by Kirill Gerasimenko.\n\n".

View File

@ -20,25 +20,25 @@ class View
public static function call()
{
# Flush the stats, so the page is fresh upon every request.
require_once($GLOBALS['rootpath']."okapi/cronjobs.php");
CronJobController::force_run("StatsWriterCronJob");
# When services/caches/map/tile method is called, it writes some extra
# stats in the okapi_stats_hourly table. This page retrieves and
# formats these stats in a readable manner (for debugging).
$response = new OkapiHttpResponse();
$response->content_type = "text/plain; charset=utf-8";
ob_start();
$start = isset($_GET['start']) ? $_GET['start'] : date(
"Y-m-d 00:00:00", time() - 7*86400);
$end = isset($_GET['end']) ? $_GET['end'] : date("Y-m-d 23:59:59");
print "From: $start\n";
print " To: $end\n\n";
$rs = Db::query("
select
service_name,
@ -51,12 +51,12 @@ class View
and service_name like '%caches/map/tile%'
group by service_name
");
$total_calls = 0;
$total_runtime = 0.0;
$calls = array('A' => 0, 'B' => 0, 'C' => 0, 'D' => 0);
$runtime = array('A' => 0.0, 'B' => 0.0, 'C' => 0.0, 'D' => 0.0);
while (list($name, $c, $r) = mysql_fetch_array($rs))
{
if ($name == 'services/caches/map/tile')
@ -76,10 +76,10 @@ class View
print "All other will count as \"unaccounted for\".\n\n";
$total_calls = $calls['A'];
}
$calls_left = $total_calls;
$runtime_left = $total_runtime;
$perc = function($a, $b) { return ($b > 0) ? sprintf("%.1f", 100 * $a / $b)."%" : "(?)"; };
$avg = function($a, $b) { return ($b > 0) ? sprintf("%.4f", $a / $b)."s" : "(?)"; };
$get_stats = function() use (&$calls_left, &$runtime_left, &$total_calls, &$total_runtime, &$perc)
@ -89,7 +89,7 @@ class View
str_pad($perc($runtime_left, $total_runtime), 7, " ", STR_PAD_LEFT)
);
};
print "%CALLS %TIME Description\n";
print "====== ====== ======================================================================\n";
print $get_stats()." $total_calls responses served. Total runtime: ".sprintf("%.2f", $total_runtime)."s\n";
@ -97,53 +97,53 @@ class View
print " All of these requests needed a TileTree build/lookup. The average runtime of\n";
print " these lookups was ".$avg($runtime['A'], $total_calls).". ".$perc($runtime['A'], $total_runtime)." of total runtime was spent here.\n";
print "\n";
$runtime_left -= $runtime['A'];
print $get_stats()." All calls passed here after ~".$avg($runtime['A'], $total_calls)."\n";
print "\n";
print " Lookup result was then processed and \"image description\" was created. It was\n";
print " passed on to the TileRenderer to compute the ETag hash string. The average runtime\n";
print " of this part was ".$avg($runtime['B'], $total_calls).". ".$perc($runtime['B'], $total_runtime)." of total runtime was spent here.\n";
print "\n";
$runtime_left -= $runtime['B'];
print $get_stats()." All calls passed here after ~".$avg($runtime['A'] + $runtime['B'], $total_calls)."\n";
$etag_hits = $calls['B'] - $calls['C'];
print "\n";
print " $etag_hits of the requests matched the ETag and were served an HTTP 304 response.\n";
print "\n";
$calls_left = $calls['C'];
print $get_stats()." $calls_left calls passed here after ~".$avg($runtime['A'] + $runtime['B'], $total_calls)."\n";
$imagecache_hits = $calls['C'] - $calls['D'];
print "\n";
print " $imagecache_hits of these calls hit the server image cache.\n";
print " ".$perc($runtime['C'], $total_runtime)." of total runtime was spent to find these.\n";
print "\n";
$calls_left = $calls['D'];
$runtime_left -= $runtime['C'];
print $get_stats()." $calls_left calls passed here after ~".$avg($runtime['A'] + $runtime['B'] + $runtime['C'], $total_calls)."\n";
print "\n";
print " These calls required the tile to be rendered. On average, it took\n";
print " ".$avg($runtime['D'], $calls['D'])." to *render* a tile.\n";
print " ".$perc($runtime['D'], $total_runtime)." of total runtime was spent here.\n";
print "\n";
$runtime_left -= $runtime['D'];
print $perc($runtime_left, $total_runtime)." of runtime was unaccounted for (other processing).\n";
print "Average response time was ".$avg($total_runtime, $total_calls).".\n\n";
print "Current okapi_cache score distribution:\n";
$rs = Db::query("
select floor(log2(score)), count(*), sum(length(value))
@ -155,7 +155,7 @@ class View
{
print $count." elements ($size bytes) with score between ".pow(2, $log2)." and ".pow(2, $log2 + 1).".\n";
}
$response->body = ob_get_clean();
return $response;
}

View File

@ -19,7 +19,7 @@ class View
{
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
require_once($GLOBALS['rootpath'].'okapi/views/menu.inc.php');
$vars = array(
'menu' => OkapiMenu::get_menu_html("examples.html"),
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
@ -28,7 +28,7 @@ class View
'okapi_rev' => Okapi::$revision,
'site_name' => Okapi::get_normalized_site_name(),
);
$response = new OkapiHttpResponse();
$response->content_type = "text/html; charset=utf-8";
ob_start();

View File

@ -13,14 +13,14 @@ class View
public static function call()
{
require_once('menu.inc.php');
$vars = array(
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
'menu' => OkapiMenu::get_menu_html(),
'installations' => OkapiMenu::get_installations(),
'okapi_rev' => Okapi::$revision,
);
$response = new OkapiHttpResponse();
$response->status = "404 Not Found";
$response->content_type = "text/html; charset=utf-8";

View File

@ -18,7 +18,7 @@ class View
{
# This is called when someone displays "http://../okapi/" (with no
# html path at the end). We will redirect to the introduction page.
return new OkapiRedirectResponse(Settings::get('SITE_URL').
"okapi/introduction.html");
}

View File

@ -20,7 +20,7 @@ class View
{
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
require_once($GLOBALS['rootpath'].'okapi/views/menu.inc.php');
$vars = array(
'menu' => OkapiMenu::get_menu_html("introduction.html"),
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
@ -30,7 +30,7 @@ class View
'installations' => OkapiMenu::get_installations(),
'okapi_rev' => Okapi::$revision,
);
$response = new OkapiHttpResponse();
$response->content_type = "text/html; charset=utf-8";
ob_start();

View File

@ -18,7 +18,7 @@ class OkapiMenu
return "<a href='".Settings::get('SITE_URL')."okapi/$link_path'".(($current_path == $link_path)
? " class='selected'" : "").">$link_name</a><br>";
}
/** Get HTML-formatted side menu representation. */
public static function get_menu_html($current_path = null)
{
@ -30,16 +30,16 @@ class OkapiMenu
$chunks[] = self::link($current_path, "signup.html", "Sign up");
$chunks[] = self::link($current_path, "examples.html", "Examples");
$chunks[] = "</div>";
# We need a list of all methods. We do not need their descriptions, so
# we won't use the apiref/method_index method to get it, the static list
# within OkapiServiceRunner will do.
$methodnames = OkapiServiceRunner::$all_names;
sort($methodnames);
# We'll break them up into modules, for readability.
$module_methods = array();
foreach ($methodnames as $methodname)
{
@ -52,7 +52,7 @@ class OkapiMenu
}
$modulenames = array_keys($module_methods);
sort($modulenames);
foreach ($modulenames as $modulename)
{
$chunks[] = "<div class='module'>$modulename</div>";
@ -63,7 +63,7 @@ class OkapiMenu
}
return implode("", $chunks);
}
public static function get_installations()
{
$installations = OkapiServiceRunner::call("services/apisrv/installations",

View File

@ -21,7 +21,7 @@ class View
{
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
require_once($GLOBALS['rootpath'].'okapi/views/menu.inc.php');
try
{
$method = OkapiServiceRunner::call('services/apiref/method', new OkapiInternalRequest(
@ -38,7 +38,7 @@ class View
'installations' => OkapiMenu::get_installations(),
'okapi_rev' => Okapi::$revision,
);
$response = new OkapiHttpResponse();
$response->content_type = "text/html; charset=utf-8";
ob_start();

View File

@ -25,7 +25,7 @@ $m = $vars['method'];
<?= $vars['menu'] ?>
</td>
<td class='article'>
<h1>
<?= $m['brief_description'] ?>
<div class='subh1'>:: <b><?= $m['name'] ?></b> method</div>

View File

@ -54,10 +54,10 @@ class View
));
return $response;
}
require_once($GLOBALS['rootpath'].'okapi/service_runner.php');
require_once($GLOBALS['rootpath'].'okapi/views/menu.inc.php');
$vars = array(
'menu' => OkapiMenu::get_menu_html("signup.html"),
'okapi_base_url' => Settings::get('SITE_URL')."okapi/",
@ -69,7 +69,7 @@ class View
? "<a href='".Settings::get('DATA_LICENSE_URL')."'>Data License</a>"
: "Data License",
);
$response = new OkapiHttpResponse();
$response->content_type = "text/html; charset=utf-8";
ob_start();

View File

@ -43,12 +43,12 @@
<?= $vars['menu'] ?>
</td>
<td class='article'>
<h1>
Sign up for an API Key
<div class='subh1'>:: <b>OpenCaching API</b> Reference</div>
</h1>
<form id='signup'>
<table cellspacing='1px' class='signup'>
<tr>
@ -86,7 +86,7 @@
</form>
<h2>Terms of Use</h2>
<p>When using data from <b><?= $vars['site_name'] ?></b> you <b>must</b> attribute the
data back to <?= $vars['site_name'] ?>.</p>
<ul>

View File

@ -29,16 +29,16 @@ class View
print $str;
flush();
}
public static function call()
{
# By default, this view is turned off in the urls.php file.
# This view is for debugging TileMap performace only!
set_time_limit(0);
header("Content-Type: text/plain; charset=utf-8");
$user_id = $_GET['u'];
self::out("Yo. I'm $user_id.\n\n");
@ -64,14 +64,14 @@ class View
$x = rand(0, (1 << $z) - 1);
$y = rand(0, (1 << $z) - 1);
}
$tiles = array();
for ($xx=$x; $xx<$x+4; $xx++)
for ($yy=$y; $yy<$y+4; $yy++)
$tiles[] = array($xx, $yy);
srand();
shuffle($tiles);
foreach ($tiles as $tile)
{
list($x, $y) = $tile;
@ -93,5 +93,5 @@ class View
}
}
}
}

View File

@ -35,7 +35,7 @@ class View
throw $e;
}
}
public static function get_max_version()
{
$max_db_version = 0;
@ -50,7 +50,7 @@ class View
}
return $max_db_version;
}
public static function out($str)
{
print $str;
@ -58,14 +58,37 @@ class View
# Therefore, calling ob_flush would give an error.
flush();
}
public static function call()
{
# First, let's acquire a lock to make sure the update isn't already running.
# We will use one of the existing lock handles, because we don't want to use
# to many of them. See issue 141.
$lock = OkapiLock::get('cronjobs-cron-5');
$lock->acquire();
try
{
self::_call();
$lock->release();
}
catch (Exception $e)
{
# Error occured. Make sure the lock is released and rethrow.
$lock->release();
throw $e;
}
}
private static function _call()
{
ignore_user_abort(true);
set_time_limit(0);
header("Content-Type: text/plain; charset=utf-8");
$current_ver = self::get_current_version();
$max_ver = self::get_max_version();
self::out("Current OKAPI database version: $current_ver\n");
@ -84,7 +107,7 @@ class View
else
{
self::out("Updating to version $max_ver... PLEASE WAIT\n\n");
while ($current_ver < $max_ver)
{
$version_to_apply = $current_ver + 1;
@ -101,15 +124,15 @@ class View
}
self::out("\nDatabase updated.\n\n");
}
self::out("Registering new cronjobs...\n");
# Validate all cronjobs (some might have been added).
Okapi::set_var("cron_nearest_event", 0);
Okapi::execute_prerequest_cronjobs();
self::out("\nUpdate complete.");
}
/**
* Return the list of email addresses of developers who used any of the given
* method names at least once. If $days is not null, then only consumers which
@ -128,7 +151,7 @@ class View
".(($days != null) ? "and sh.period_start > date_add(now(), interval -".$days." day)" : "")."
");
}
private static function ver1()
{
ob_start();
@ -153,7 +176,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver2()
{
Db::execute("
@ -165,7 +188,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver3()
{
Db::execute("
@ -180,7 +203,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver4()
{
Db::execute("
@ -192,7 +215,7 @@ class View
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
");
}
private static function ver5()
{
Db::execute("
@ -210,12 +233,12 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver6()
{
# Removed this update. It seemed dangerous to run such updates on unknown OC installations.
}
private static function ver7()
{
# In fact, this should be "alter cache_logs add column okapi_consumer_key...", but
@ -231,7 +254,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver8()
{
Db::execute("
@ -243,7 +266,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver9() { Db::execute("alter table okapi_consumers modify column `key` varchar(20) not null"); }
private static function ver10() { Db::execute("alter table okapi_consumers modify column secret varchar(40) not null"); }
private static function ver11() { Db::execute("alter table okapi_tokens modify column `key` varchar(20) not null"); }
@ -255,7 +278,7 @@ class View
private static function ver17() { Db::execute("alter table okapi_nonces modify column `key` varchar(255) not null"); }
private static function ver18() { Db::execute("alter table okapi_cache_logs modify column consumer_key varchar(20) not null"); }
private static function ver19() { Db::execute("alter table okapi_vars modify column `var` varchar(32) not null"); }
private static function ver20() { Db::execute("alter table okapi_consumers modify column `key` varchar(20) collate utf8_bin not null"); }
private static function ver21() { Db::execute("alter table okapi_consumers modify column secret varchar(40) collate utf8_bin not null"); }
private static function ver22() { Db::execute("alter table okapi_tokens modify column `key` varchar(20) collate utf8_bin not null"); }
@ -267,7 +290,7 @@ class View
private static function ver28() { Db::execute("alter table okapi_nonces modify column `key` varchar(255) collate utf8_bin not null"); }
private static function ver29() { Db::execute("alter table okapi_cache_logs modify column consumer_key varchar(20) collate utf8_bin not null"); }
private static function ver30() { Db::execute("alter table okapi_vars modify column `var` varchar(32) collate utf8_bin not null"); }
private static function ver31()
{
Db::execute("
@ -281,7 +304,7 @@ class View
) ENGINE=MEMORY DEFAULT CHARSET=utf8
");
}
private static function ver32()
{
Db::execute("
@ -298,7 +321,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8
");
}
private static function ver33()
{
try
@ -310,7 +333,7 @@ class View
// key exists
}
}
private static function ver34()
{
Db::execute("
@ -321,7 +344,7 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8
");
}
private static function ver35()
{
# Inform the admin about the new cronjobs.
@ -336,13 +359,13 @@ class View
"Thanks, OKAPI developers."
);
}
private static function ver36() { Db::execute("alter table okapi_cache modify column `key` varchar(64) not null"); }
private static function ver37() { Db::execute("delete from okapi_vars where var='last_clog_update'"); }
private static function ver38() { Db::execute("alter table okapi_clog modify column data mediumblob"); }
private static function ver39() { Db::execute("delete from okapi_clog"); }
private static function ver40() { Db::execute("alter table okapi_cache modify column value mediumblob"); }
private static function ver41()
{
# Force changelog reset (will be produced one day back)
@ -352,9 +375,9 @@ class View
Okapi::set_var("cron_nearest_event", 0);
Cache::delete('cron_schedule');
}
private static function ver42() { Db::execute("delete from okapi_cache where length(value) = 65535"); }
private static function ver43()
{
$emails = self::get_consumers_of(array('services/replicate/changelog', 'services/replicate/fulldump'), 14);
@ -371,11 +394,11 @@ class View
print "OKAPI Team";
Okapi::mail_from_okapi($emails, "A change in the 'replicate' module.", ob_get_clean());
}
private static function ver44() { Db::execute("alter table caches add column okapi_syncbase timestamp not null after last_modified;"); }
private static function ver45() { Db::execute("update caches set okapi_syncbase=last_modified;"); }
private static function ver46() { /* no longer necessary */ }
private static function ver47()
{
Db::execute("
@ -388,7 +411,7 @@ class View
);
");
}
private static function ver48()
{
ob_start();
@ -401,7 +424,7 @@ class View
print "OKAPI Team";
Okapi::mail_admins("Database modification notice: caches.okapi_syncbase", ob_get_clean());
}
private static function print_common_db_alteration_info()
{
print "-- About OKAPI's database modifications --\n\n";
@ -414,27 +437,27 @@ class View
print "(outside of the \"okapi_\" table-scope). If you have any comments\n";
print "on this procedure, please submit them to our issue tracker.\n\n";
}
private static function ver49() { Db::execute("alter table caches add key okapi_syncbase (okapi_syncbase);"); }
private static function ver50() { /* no longer necessary */ }
private static function ver51()
{
# Before revision 417, OKAPI used to make the following change:
# - Db::execute("alter table cache_logs modify column last_modified timestamp not null;");
# It doesn't do that anymore. Instead, it adds a separate column for itself (okapi_syncbase).
}
private static function ver52()
{
# Before revision 417, OKAPI used to make the following change (on OCDE branch):
# - Db::execute("alter table cache_logs_archived modify column last_modified timestamp not null;");
# It doesn't do that anymore. Instead, it adds a separate column for itself (okapi_syncbase).
}
private static function ver53() { Db::execute("alter table cache_logs add column okapi_syncbase timestamp not null after last_modified;"); }
private static function ver54() { Db::execute("update cache_logs set okapi_syncbase=last_modified;"); }
private static function ver55()
{
if (Settings::get('OC_BRANCH') == 'oc.pl')
@ -444,7 +467,7 @@ class View
}
Db::execute("alter table cache_logs_archived add column okapi_syncbase timestamp not null after last_modified;");
}
private static function ver56()
{
if (Settings::get('OC_BRANCH') == 'oc.pl')
@ -454,7 +477,7 @@ class View
}
Db::execute("update cache_logs_archived set okapi_syncbase=last_modified;");
}
private static function ver57()
{
ob_start();
@ -467,7 +490,7 @@ class View
print "OKAPI Team";
Okapi::mail_admins("Database modification notice: caches.okapi_syncbase", ob_get_clean());
}
private static function ver58()
{
#
@ -521,9 +544,9 @@ class View
}
}
}
private static function ver61() { Db::execute("alter table cache_logs add key okapi_syncbase (okapi_syncbase);"); }
private static function ver62()
{
if (Settings::get('OC_BRANCH') == 'oc.pl')
@ -533,7 +556,7 @@ class View
}
Db::execute("alter table cache_logs_archived add key okapi_syncbase (okapi_syncbase);");
}
private static function ver63()
{
Db::execute("
@ -546,7 +569,7 @@ class View
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
");
}
private static function ver64()
{
Db::execute("
@ -565,14 +588,14 @@ class View
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
");
}
private static function ver65() { Db::execute("alter table okapi_tile_status engine=innodb;"); }
private static function ver66() { Db::execute("alter table okapi_tile_caches engine=innodb;"); }
private static function ver67()
{
# Remove unused locks (these might have been created in previous versions of OKAPI).
for ($z=0; $z<=2; $z++)
for ($x=0; $x<(1<<$z); $x++)
for ($y=0; $y<(1<<$z); $y++)
@ -582,11 +605,11 @@ class View
OkapiLock::get($lockname)->remove();
}
}
private static function ver68()
{
# Once again, remove unused locks.
for ($z=0; $z<=21; $z++)
{
foreach (array("", "-0", "-1") as $suffix)
@ -597,16 +620,16 @@ class View
}
}
}
private static function ver69()
{
# TileTree border margins changed. We need to recalculate all nodes
# but the root.
Db::execute("delete from okapi_tile_caches where z > 0");
Db::execute("delete from okapi_tile_status where z > 0");
}
private static function ver70()
{
Db::execute("
@ -615,14 +638,14 @@ class View
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");
}
private static function ver71() { Db::execute("alter table okapi_cache add column score float(4,2) default null after `key`"); }
private static function ver72() { Db::execute("alter table okapi_cache change column expires expires datetime after score"); }
private static function ver73() { Db::execute("update okapi_cache set score=1, expires=date_add(now(), interval 360 day) where `key` like 'tile/%'"); }
private static function ver74() { Db::execute("update okapi_cache set score=1, expires=date_add(now(), interval 360 day) where `key` like 'tilecaption/%'"); }
private static function ver75() { Db::execute("alter table okapi_cache modify column score float default null"); }
private static function ver76() { Db::execute("update okapi_cache set expires=date_add(now(), interval 100 year) where `key` like 'clog#geocache#%'"); }
private static function ver77()
{
Db::execute("
@ -645,9 +668,10 @@ class View
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
}
private static function ver79() { Db::execute("alter table okapi_search_results engine=MyISAM"); }
private static function ver80() { Db::execute("alter table okapi_search_sets add column date_created datetime not null"); }
private static function ver81() { Db::execute("alter table okapi_search_sets add column expires datetime not null"); }
private static function ver82() { CronJobController::reset_job_schedule("FulldumpGeneratorJob"); }
private static function ver83() { Db::execute("alter table okapi_stats_temp engine=InnoDB"); }
}