Skip to content

Commit

Permalink
Method infotags - visual cues for OCPL/OCDE-specific features
Browse files Browse the repository at this point in the history
  • Loading branch information
wrygiel committed Apr 25, 2017
1 parent 1639233 commit b685264
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 57 deletions.
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -112,6 +112,14 @@ way, that the same change will also work on all other OC sites:
you have OKAPI installed, we will make every effort for future versions of
OKAPIs to be compatible with your site.

You should know that there are two primary branches of Opencaching - we call
them OCPL and OCDE - and all OKAPI methods MUST support both these branches
(unless the particular functionality is available on only one of them). You can
read more about OCPL and OCDE branching
[here](https://github.com/opencaching/opencaching-pl/wiki#brief-introduction-in-english).
You can also read about how OKAPI tries to deal with branch differences
[here](https://opencaching.pl/okapi/introduction.html#oc-branch-differences).


## Requirements ##

Expand Down
17 changes: 17 additions & 0 deletions okapi/core.php
Expand Up @@ -2242,6 +2242,23 @@ public static function update_user_activity($request) {
}
}

/**
* Take a list of "infotags" (as defined in services/apiref/method), and format
* them for being displayed in OKAPI public documentation pages.
*/
public static function format_infotags($infotags) {
$chunks = [];
$url = Settings::get('SITE_URL')."okapi/introduction.html#oc-branch-differences";
foreach ($infotags as $infotag) {
if ($infotag == "ocpl-specific") {
$chunks[] = "<a href='$url' class='infotag infotag-ocpl-specific'>OCPL</a> ";
} elseif ($infotag == "ocde-specific") {
$chunks[] = "<a href='$url' class='infotag infotag-ocde-specific'>OCDE</a> ";
}
}
return implode("", $chunks);
}

# object types in table okapi_submitted_objects
const OBJECT_TYPE_CACHE = 1;
const OBJECT_TYPE_CACHE_DESCRIPTION = 2;
Expand Down
41 changes: 37 additions & 4 deletions okapi/services/apiref/method.php
Expand Up @@ -24,16 +24,29 @@ public static function options()
private static function arg_desc($arg_node)
{
$attrs = $arg_node->attributes();
return array(
$result = array(
'name' => (string)$attrs['name'],
'is_required' => $arg_node->getName() == 'req',
'is_deprecated' => (isset($attrs['class']) && (strpos($attrs['class'], 'deprecated') !== false)),
'class' => 'public',
'infotags' => [],
'description' =>
(isset($attrs['default']) ? ("<p>Default value: <b>".$attrs['default']."</b></p>") : "").
self::get_inner_xml($arg_node),

);
if (isset($attrs['infotags'])) {
foreach (explode(" ", (string)$attrs['infotags']) as $infotag) {
switch ($infotag) {
case 'ocpl-specific':
case 'ocde-specific':
$result['infotags'][] = $infotag;
break;
default:
throw new Exception("Invalid infotag '".$infotag." in $methodname.xml");
}
}
}
return $result;
}

private static function get_inner_xml($node)
Expand All @@ -47,7 +60,7 @@ private static function get_inner_xml($node)

/* Find and replace %okapi:plugins%. */

$s = preg_replace_callback('~%OKAPI:([a-z:/_#]+)%~', array("self", "plugin_callback"), $s);
$s = preg_replace_callback('~%OKAPI:([a-z:/_#-]+)%~', array("self", "plugin_callback"), $s);

return $s;
}
Expand Down Expand Up @@ -78,6 +91,9 @@ private static function get_inner_xml($node)
* <a href="%OKAPI:methodretref:#returned_key%">any text</a> - to reference
* returned value within current method
*
* %OKAPI:infotag:TAGNAME% - to output a HTML with a proper infotag "badge".
* TAGNAME must match one of the infotags defined in services/apiref/method.
*
* NOTE!
*
* Since returned JSON dictionaries are not standardized (they are simply plain
Expand Down Expand Up @@ -120,6 +136,8 @@ public static function plugin_callback($matches)
$result .= $elements[1];
}
return $result;
case 'infotag':
return Okapi::format_infotags([$arr[1]]);
default:
throw new Exception("Unknown plugin: ".$input);
}
Expand Down Expand Up @@ -147,7 +165,8 @@ public static function call(OkapiRequest $request)
'min_auth_level' => $options['min_auth_level'],
'oauth_consumer' => $options['min_auth_level'] >= 2,
'oauth_token' => $options['min_auth_level'] >= 3,
)
),
"infotags" => [],
);
if (!$docs->brief)
throw new Exception("Missing <brief> element in the $methodname.xml file.");
Expand All @@ -167,6 +186,18 @@ public static function call(OkapiRequest $request)
if (!$docs->desc)
throw new Exception("Missing <desc> element in the $methodname.xml file.");
$result['description'] = self::get_inner_xml($docs->desc);
if ($docs->infotags) {
foreach (explode(" ", (string)$docs->infotags) as $infotag) {
switch ($infotag) {
case 'ocpl-specific':
case 'ocde-specific':
$result['infotags'][] = $infotag;
break;
default:
throw new Exception("Invalid infotag '".$infotag." in $methodname.xml");
}
}
}
$result['arguments'] = array();
foreach ($docs->req as $arg) { $result['arguments'][] = self::arg_desc($arg); }
foreach ($docs->opt as $arg) { $result['arguments'][] = self::arg_desc($arg); }
Expand Down Expand Up @@ -207,13 +238,15 @@ public static function call(OkapiRequest $request)
'is_required' => false,
'is_deprecated' => false,
'class' => 'common-formatting',
'infotags' => [],
'description' => "<i>Standard <a href='".Settings::get('SITE_URL')."okapi/introduction.html#common-formatting'>common formatting</a> argument.</i>"
);
$result['arguments'][] = array(
'name' => 'callback',
'is_required' => false,
'is_deprecated' => false,
'class' => 'common-formatting',
'infotags' => [],
'description' => "<i>Standard <a href='".Settings::get('SITE_URL')."okapi/introduction.html#common-formatting'>common formatting</a> argument.</i>"
);
}
Expand Down
26 changes: 26 additions & 0 deletions okapi/services/apiref/method.xml
Expand Up @@ -25,6 +25,26 @@
description of what the method does,
</li>
<li><b>ref_url</b> - URL of the documentation page with method description,</li>
<li>
<p><b>infotags</b> - list of tags. Each tag is represented by its unique code
(string). Existence (or absence) of each tag has its meaning. Currently, the
following tags are allowed:</p>

<ul>
<li>
<p><b>ocpl-specific</b> - if present, then this method is (currently)
specific to OCPL-branch (as described
<a href='%OKAPI:docurl:oc-branch-differences%'>here</a>).</p>

<p>Please note, that such method may still become a "regular" method in the
future (as support for certain feature gets implemented in other OC
branches).</p>
</li>
<li>
<p><b>ocde-specific</b> - as above, but for OCDE-branch.</p>
</li>
</ul>
</li>
<li>
<p><b>auth_options</b> - a dictionary which describes authentication
requirements for this method, it has a following structure:</p>
Expand Down Expand Up @@ -61,6 +81,12 @@
<p>Currently these values do not mean anything specific. They are used
for different coloring/styling in the documentation pages.</p>
</li>
<li>
<p><b>infotags</b> - list of tags. Currently they may have exactly the
same values as the "method-level" infotags defined above. They indicate
that a particular argument is designed to "work best" on one of the OC
branches only.</p>
</li>
</ul>
</li>
<li><b>returns</b> - HTML-formatted description method's return value.</li>
Expand Down
65 changes: 60 additions & 5 deletions okapi/services/apiref/method_index.php
Expand Up @@ -8,6 +8,9 @@
use okapi\OkapiInternalRequest;
use okapi\OkapiRequest;
use okapi\OkapiServiceRunner;
use okapi\Settings;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;


class WebService
Expand All @@ -21,24 +24,76 @@ public static function options()

public static function call(OkapiRequest $request)
{
$methodnames = OkapiServiceRunner::$all_names;
sort($methodnames);
$cache_key = "api_ref/method_index#".md5(implode("#", $methodnames));
$cache_key = self::generateCacheKey();
$results = Cache::get($cache_key);
if ($results == null)
{
$results = array();
foreach ($methodnames as $methodname)
foreach (OkapiServiceRunner::$all_names as $methodname)
{
$info = OkapiServiceRunner::call('services/apiref/method', new OkapiInternalRequest(
new OkapiInternalConsumer(), null, array('name' => $methodname)));
$results[] = array(
'name' => $info['name'],
'short_name' => $info['short_name'],
'brief_description' => $info['brief_description'],
'infotags' => $info['infotags'],
'auth_options' => $info['auth_options'],
);
}
Cache::set($cache_key, $results, 3600);
Cache::set($cache_key, $results, 86400);
}
return Okapi::formatted_response($request, $results);
}

/**
* This method will generate a best cache key to be used, depending on the environment.
*
* We want the method index to return fast, but we also don't want developers to see cached
* results when they are adding (or changing) methods (in dev-environments).
*/
private static function generateCacheKey() {

if (!Settings::get('DEBUG')) {

/* Production. */

if (Okapi::$version_number !== null) {
return "api_ref/method_index#prod#".Okapi::$version_number;
} else {
$methodnames = OkapiServiceRunner::$all_names;
sort($methodnames);
return "api_ref/method_index#".md5(implode("#", $methodnames));
}

} else {

/* Development. */

return (
"api_ref/method_index#dev#".
self::getDirModDateRecursive($GLOBALS['rootpath']."okapi/services")
);
}
}

private static function getDirModDateRecursive($absoluteDir) {
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($absoluteDir, RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);
$max_timestamp = 0;
foreach ($iterator as $item) {
if($item->isDir()) {
$pth = $item->getPath()."/.";
} else {
$pth = $item->getPathname();
}
$timestamp = filemtime($pth);
if ($timestamp > $max_timestamp) {
$max_timestamp = $timestamp;
}
}
return $max_timestamp;
}
}
15 changes: 7 additions & 8 deletions okapi/services/apiref/method_index.xml
Expand Up @@ -2,16 +2,15 @@
<brief>Get a list of OKAPI methods with brief descriptions</brief>
<issue-id>12</issue-id>
<desc>
<p>Get a list of OKAPI methods with brief descriptions.</p>
<p>Get a list of OKAPI methods with brief descriptions and a bunch
of other basic properties.</p>
</desc>
<common-format-params/>
<returns>
A list of dictionaries, each of which contains one API
method description in the following format:
<ul>
<li><b>name</b> - name of a method,</li>
<li><b>brief_description</b> - brief (max 80 characters), single-line,
plain-text description of what the method does.</li>
</ul>
<p>A list of dictionaries. Each dictionary describes one API method,
and contains the following <i>subset</i> of the fields described in the
<a href="%OKAPI:methodref:services/apiref/method%">method</a> method:
<b>name</b>, <b>short_name</b>, <b>brief_description</b>,
<b>ref_url</b>, <b>infotags</b>, <b>auth_options</b>.</p>
</returns>
</xml>
16 changes: 9 additions & 7 deletions okapi/services/caches/formatters/gpx.xml
Expand Up @@ -91,13 +91,15 @@
Such attributes cannot be included as gc:attrs and will be ignored.</p>
</li>
<li>
<p><b>gc_ocde:attrs</b> - attributes will be listed as Groundspeak's
<i>groundspeak:attribute</i> elements (<b>ns_ground</b> has to be <b>true</b>).
Opencaching attributes which have no Geocaching.com counterparts will be
included according to an Opencaching.DE convention, using "makeshift IDs"
which may change in the future.</p>
<p>Note that this option is supported only by some OC sites. Other sites will
handle <b>gc_ocde:attrs</b> in the same way as <b>gc:attrs</b>.</p>
<p>%OKAPI:infotag:ocde-specific% <b>gc_ocde:attrs</b> - attributes will be
listed as Groundspeak's <i>groundspeak:attribute</i> elements (<b>ns_ground</b>
has to be <b>true</b>). Opencaching attributes which have no Geocaching.com
counterparts will be included according to an Opencaching.DE convention, using
"makeshift IDs" which may change in the future.</p>

<p>This alternative is supported only by some OC sites (the ones originating
from the OCDE branch). Other sites will handle <b>gc_ocde:attrs</b> in the same
way as <b>gc:attrs</b>.</p>
</li>
</ul>

Expand Down
12 changes: 6 additions & 6 deletions okapi/services/caches/geocache.xml
Expand Up @@ -267,13 +267,13 @@
<b>null</b> if unknown,</p>
</li>
<li>
<p><b>rating</b> - float (between 1 and 5), an overall rating of the cache,
or <b>null</b> when the geocache does not have a sufficient amount of votes
to have a rating.</p>
<p>%OKAPI:infotag:ocpl-specific% <b>rating</b> - float (between 1 and 5), an
overall rating of the cache, or <b>null</b> when the geocache does not have a
sufficient amount of votes to have a rating.</p>

<p>Please note: some OC installations do not use the
rating/voting system. The <b>rating</b> will always be
<b>null</b> on such installations.</p>
<p><b>Notice:</b> Only OCPL-based Opencaching installations provide ratings for
their geocaches. On OCDE-based installations, this field will always contain
<b>null</b>.</p>
</li>
<li>
<p><b>rating_votes</b> - number of votes submitted for the
Expand Down
2 changes: 1 addition & 1 deletion okapi/services/caches/map/tilerenderer.inc.php
Expand Up @@ -110,7 +110,7 @@ private static function get_image($name, $opacity=1, $brightness=0,
$key = "$name/$opacity/$brightness/$contrast/$r/$g/$b";
if (!isset($locmem_cache[$key]))
{
# Miss. Check default cache. WRTODO: upgrade to normal Cache?
# Miss. Check default cache.

try
{
Expand Down
14 changes: 7 additions & 7 deletions okapi/services/caches/search/all.xml
Expand Up @@ -91,7 +91,7 @@
<p><b>Note:</b> Not all OC servers use all of these. I.e. OCPL does not
have 'nano' nor 'other' caches (but it might have them in the future).</p>
</opt>
<opt name='rating'>
<opt name='rating' infotags="ocpl-specific">
<p>A string "X-Y", where X and Y are integers between 1 and 5, and X &lt;= Y.
Only caches with an overall rating between these numbers (inclusive) will be returned
(1 - poor, 5 - excellent).</p>
Expand All @@ -100,8 +100,8 @@
excluded from results. If you still want unrated caches included, append "|X" suffix
to the value of this parameter (e.g. "3-5|X").</p>

<p><b>Notice:</b> Some OC installations do not provide ratings for their geocaches.
On such installation, this parameter will be ignored.</p>
<p><b>Notice:</b> Only OCPL-based Opencaching installations provide ratings for their
geocaches. On OCDE-based installations, this parameter will be ignored.</p>
</opt>
<opt name='min_rcmds'>
<p>There are two possible value-types for this argument:</p>
Expand Down Expand Up @@ -205,14 +205,14 @@
Boolean. If set to <b>true</b>, only caches which have not yet been
found <b>by anyone</b> will be included.
</opt>
<opt name='powertrail_only' default='false'>
<opt name='powertrail_only' default='false' infotags="ocpl-specific">
<p>Boolean. If set to <b>true</b>, only caches which belong to at
least one <b>active</b> Power Trail will be included.</p>

<p>Power Trails are sets of geocaches. Only some Opencaching sites
implement this feature. If this site does not implement it, and you
will set this parameter to <b>true</b>, then you will <b>always</b>
receive an empty result.</p>
implement this feature (OCPL-branch). If this site does not implement
it, and you will set this parameter to <b>true</b>, then you will
<b>always</b> receive an empty result.</p>
</opt>
<opt name='powertrail_ids'>
<p>Pipe-separated list of Power Trail IDs. If given, then only
Expand Down

0 comments on commit b685264

Please sign in to comment.