/[drupal]/contributions/modules/geonames/geonames.module
ViewVC logotype

Contents of /contributions/modules/geonames/geonames.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (show annotations) (download) (as text)
Sun Dec 14 20:24:49 2008 UTC (11 months, 1 week ago) by lyricnz
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +3 -2 lines
File MIME type: text/x-php
#344156: fix misplaced bracket
1 <?php
2 // $Id
3
4 /**
5 * @file
6 * GeoNames API / by Erlend Eide, erlend(|)edesign.no
7 *
8 * The GeoNames API provides a framework for the GeoNames Services.
9 *
10 * Commercial Services credit cost: http://www.geonames.org/professional-webservices.html
11 *
12 */
13
14 /**
15 * TODO:
16 * - Rewrite logics handling the columns option, and the cache-logics (which currently uses
17 * the columns variable to make somewhat ineffective decisions)
18 * - Move/Remove Google Maps functions from the core module, must be improved...
19 * - Improve handling of exeptions, verboseness, errmessages etc..
20 *
21 * - Countries, admin-names, feature codes/classes etc could be cached and made available in a function so
22 * we can run fill($resultobject) or something, and then convert all keys to names...
23
24 * - Standardise how to query information from the local tables (countryinfo, continent_codes, us_states and featurecodes)
25 * (should just use geonames_query() with the proper options...
26 */
27
28
29 /**
30 * Constants
31 * hrmpf...
32 */
33 DEFINE('GEONAMES_COMMERCIAL', variable_get('geonames_commercial_active', FALSE));
34 DEFINE('GEONAMES_FREE_SERVER_URL', 'http://ws.geonames.org');
35 DEFINE('GEONAMES_CACHE', variable_get('geonames_cache', TRUE));
36 DEFINE('GEONAMES_CACHE_TABLE', variable_get('geonames_cache_table', 'cache'));
37 DEFINE('GEONAMES_CACHE_LIMIT', 86400 * variable_get('geonames_cache_limit', 90));
38
39 require("geonames_config.inc");
40
41 /**
42 * Implementation of hook_cron()
43 */
44 function geonames_cron() {
45 // Update countryinfo table once a month (TODO move this block to somewhere else)...
46 $updated = variable_get('geonames_metadata_updated', 0);
47 $monthago = time() - 2592000;
48 if ($updated < $monthago) {
49 geonames_metadata_update();
50 }
51 }
52
53 /**
54 * Update the metadata tables (Countryinfo and Featurecodes)
55 */
56 function geonames_metadata_update() {
57 $file = drupal_http_request("http://download.geonames.org/export/dump/countryInfo.txt?app=drupal");
58 if ($file->code == 200) {
59 $lines = split("\n", $file->data);
60 foreach ($lines as $line) {
61 if (trim($line) != '' && substr($line, 0, 1) != '#') {
62 // insert or replace the data
63 $a = split("\t", $line);
64 if ($a[4] != 'name') {
65 db_query("REPLACE INTO {geonames_countryinfo} VALUES('%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)",
66 $a[0], $a[1], $a[2], $a[3], $a[4], $a[5], $a[6], $a[7], $a[8], $a[9], $a[10], $a[11]);
67 }
68 }
69 }
70 watchdog("geonames", "Successfully updated the geonames_countryinfo table");
71 }
72 else {
73 watchdog("geonames", "Failed to updated geonames_countryinfo table", WATCHDOG_WARNING);
74 $failed = true;
75 }
76
77 // Feature code tables
78 $file = drupal_http_request("http://download.geonames.org/export/dump/featureCodes.txt?app=drupal");
79 if ($file->code == 200) {
80 $lines = split("\n", $file->data);
81 foreach ($lines as $line) {
82 if (trim($line) != '' && substr($line, 0, 1) != '#') {
83 // insert or replace the data
84 $a = split("\t", $line);
85 $b = split("\.", $a[0]);
86 $c = array($b[0], $b[1], $a[1], $a[2]);
87 db_query("REPLACE INTO {geonames_featurecodes} VALUES('%s', '%s', '%s', '%s')", $c[0], $c[1], $c[2], $c[3]);
88 }
89 }
90 watchdog("geonames", "Successfully updated the geonames_featurecodes table");
91 }
92 else {
93 watchdog("geonames", "Failed to updated geonames_featurecodes table", WATCHDOG_WARNING);
94 $failed = true;
95 }
96
97 if (!$failed) {
98 variable_set('geonames_metadata_updated', time());
99 }
100 }
101
102 /**
103 * Retrieve featureinfo
104 *
105 * @param
106 * A string feature code
107 */
108 function geonames_featureinfo($code) {
109 $code = check_plain($code);
110 $result = db_fetch_array(db_query("SELECT * FROM {geonames_featurecodes} WHERE code = '%s'", $code));
111 return $result;
112 }
113
114 /**
115 * Retrieve Country Info
116 *
117 * @param
118 * A string iso2 country code or country name
119 */
120 function geonames_countryinfo($param) {
121 $param = check_plain($param);
122 $result = db_fetch_array(db_query("SELECT * FROM {geonames_countryinfo} WHERE iso_alpha2 = '%s' OR name = '%s'", $param, $param));
123 return $result;
124 }
125
126 /**
127 * Retrieve Country Info
128 */
129 function geonames_countries($param = NULL, $reset = FALSE) {
130 if (is_array($param)) {
131 $type = key($param);
132 $parameter = $param[$type];
133 }
134
135 if (($cache = cache_get('geonames_countries'. $type)) && !empty($cache->data) && !$reset) {
136 $countries = $cache->data;
137 }
138 else {
139 switch ($type) {
140 case 'continent':
141 $result = db_query("SELECT * FROM {geonames_countryinfo} WHERE continent = '%s' ORDER BY name", $parameter);
142 break;
143 default:
144 $result = db_query("SELECT * FROM {geonames_countryinfo} ORDER BY name");
145 }
146 $countries = array();
147 while ($a = db_fetch_array($result)) {
148 $countries[] = $a;
149 }
150 cache_set('geonames_countries'. $type, $countries, GEONAMES_CACHE_TABLE, time() + 60 * 60 * 1); // Cache for 1 hour
151 }
152 return $countries;
153 }
154
155 /**
156 * Returns the continent codes
157 *
158 * @param
159 * A string, either 'geonameid', 'short' or 'name'
160 *
161 * @return
162 * An array with continent information. If parameter key is passed, it is indexed
163 */
164 function geonames_continent_codes($key = NULL) {
165 $continent_codes = array(
166 array('short' => 'AF', 'name' => 'Africa' , 'geonameid' => '6255146'),
167 array('short' => 'AS', 'name' => 'Asia' , 'geonameid' => '6255147'),
168 array('short' => 'EU', 'name' => 'Europe' , 'geonameid' => '6255148'),
169 array('short' => 'NA', 'name' => 'North America' , 'geonameid' => '6255149'),
170 array('short' => 'OC', 'name' => 'Oceania' , 'geonameid' => '6255151'),
171 array('short' => 'SA', 'name' => 'South America' , 'geonameid' => '6255150'),
172 array('short' => 'AN', 'name' => 'Antarctica' , 'geonameid' => '6255152'),
173 );
174 if ($key == 'geonameid') {
175 // Order with geonamesid as key
176 foreach ($continent_codes as $c) {
177 $codes[$c['geonameid']] = array('short' => $c['short'], 'name' => $c['name']);
178 }
179 }
180 elseif ($key == 'short') {
181 // Order with short as key
182 foreach ($continent_codes as $c) {
183 $codes[$c['short']] = array('geonameid' => $c['geonameid'], 'name' => $c['name']);
184 }
185 }
186 elseif ($key == 'name') {
187 // Order with short as key
188 foreach ($continent_codes as $c) {
189 $codes[$c['name']] = array('geonameid' => $c['geonameid'], 'short' => $c['short']);
190 }
191 }
192 else {
193 $codes = $continent_codes;
194 }
195 return $codes;
196 }
197
198 /**
199 * Return Info about US States
200 */
201 function geonames_us_states($state = NULL) {
202 $states = array(
203 'DC' => 'District of Columbia',
204 'AK' => 'Alaska',
205 'AL' => 'Alabama',
206 'AR' => 'Arkansas',
207 'AZ' => 'Arizona',
208 'CA' => 'California',
209 'CO' => 'Colorado',
210 'CT' => 'Connecticut',
211 'DE' => 'Delaware',
212 'FL' => 'Florida',
213 'GA' => 'Georgia',
214 'HI' => 'Hawaii',
215 'IA' => 'Iowa',
216 'ID' => 'Idaho',
217 'IL' => 'Illinois',
218 'IN' => 'Indiana',
219 'KS' => 'Kansas',
220 'KY' => 'Kentucky',
221 'LA' => 'Louisiana',
222 'MA' => 'Massachusetts',
223 'MD' => 'Maryland',
224 'ME' => 'Maine',
225 'MI' => 'Michigan',
226 'MN' => 'Minnesota',
227 'MO' => 'Missouri',
228 'MS' => 'Mississippi',
229 'MT' => 'Montana',
230 'NC' => 'North Carolina',
231 'ND' => 'North Dakota',
232 'NE' => 'Nebraska',
233 'NH' => 'New Hampshire',
234 'NJ' => 'New Jersey',
235 'NM' => 'New Mexico',
236 'NV' => 'Nevada',
237 'NY' => 'New York',
238 'OH' => 'Ohio',
239 'OK' => 'Oklahoma',
240 'OR' => 'Oregon',
241 'PA' => 'Pennsylvania',
242 'RI' => 'Rhode Island',
243 'SC' => 'South Carolina',
244 'SD' => 'South Dakota',
245 'TN' => 'Tennessee',
246 'TX' => 'Texas',
247 'UT' => 'Utah',
248 'VA' => 'Virginia',
249 'VT' => 'Vermont',
250 'WA' => 'Washington',
251 'WI' => 'Wisconsin',
252 'WV' => 'West Virginia',
253 'WY' => 'Wyoming',
254 );
255 return ($state) ? $states[$state] : $states;
256 }
257
258 /**
259 * GeoNames API function
260 *
261 * See http://www.geonames.org/export/geonames-search.html for full docs.
262 * NOTE: q (in GeoNames docs) = query (API), all other parameters are
263 * equivalently named
264 *
265 * @param $service
266 * string: name of service
267 *
268 * @param $query
269 * associative array with query - optional for some services, mandatory for others
270 *
271 * @param $columns
272 * optional array: returns only the specified columns, ordered
273 *
274 * @return
275 * result object (or FALSE on failure)
276 */
277 function geonames_query($service, $query = array(), $options = NULL) {
278 //function geonames_query($service, $query = array(), $columns = FALSE) {
279
280 if ($query == NULL || $query == FALSE || $query == '') {
281 $query = array();
282 }
283 else if (!is_array($query)) {
284 return FALSE;
285 }
286
287 // start timer
288 $time_start = microtime(TRUE);
289
290 // verify that service exists
291 $services = geonames_config('services');
292 if (!in_array($service, $services)) {
293 drupal_set_message(t('GeoNames API: Unknown Service <b>') . $service .'</b>');
294 return false;
295 };
296
297 // make query and options case insensitive
298 $query = array_change_key_case($query);
299 if (is_array($options)) {
300 $options = array_change_key_case($options);
301 }
302
303 // remove empty-value parameters from query
304 foreach ($query as $key => $val) {
305 if (empty($val)) {
306 unset($query[$key]);
307 }
308 }
309
310 // add default query parameters to query
311 $query_defaults = geonames_config($service, 'query_defaults');
312 if (is_array($query_defaults)) {
313 $query = array_merge($query_defaults, $query);
314 }
315
316 // check if the query contains the required parameteres
317 if (!geonames_query_required_parameters_set($service, $query, geonames_config($service, 'required_parameters_type'))) {
318 return FALSE;
319 }
320
321 if (NULL != $options) {
322 if (!is_array($options)) {
323 // options must be an array
324 return false;
325 }
326 if (gettype($options[0]) === string) {
327 // Backwards compatibility -- $options was $columns
328 $columns = $options;
329 }
330 if ($options['columns']) {
331 $columns = $options['columns'];
332 array_walk($columns, '_aw_val_to_lower'); // lowercase all column values
333 }
334 if ($options['sortby']) {
335 $sortby = $options['sortby'];
336 }
337 if ($options['sortorder']) {
338 $sortorder = $options['sortorder'];
339 }
340 }
341
342 if ($columns == FALSE) {
343 // load service defined columns
344 $columns = geonames_config($service, 'columns');
345 }
346 else{
347 // If a set of fieldnames is passed, verify that they exist
348 if (!$defined_columns = geonames_config($service, 'columns')) {
349 $defined_columns = geonames_fields('full', TRUE); // Load full style
350 }
351 foreach ($columns as $key => $val) {
352 if (!in_array($val, $defined_columns)) {
353 // exclude unknown fieldnames
354 unset($columns[$key]);
355 drupal_set_message(t('Field') .' <i>'. $val .'</i> '. t('is not available for this service, ignoring.'));
356 }
357 }
358 }
359 if ($columns) {
360 if (geonames_config($service, 'detect_style')) {
361 $query['style'] = geonames_query_detect_style($columns);
362 }
363 $fields = $columns;
364 }
365 else {
366 // if columns are not specified, use $query['style'], and if that isn't specified - default to short ;)
367 $fields = isset($query['style']) ? geonames_fields($query['style'], TRUE) : geonames_fields('short', TRUE);
368 }
369
370 // sort query, so cached queries are more likely to get hit
371 asort($query);
372
373 // convert our Query Array to a GeoNames Encoded URI
374 if (!$querystring = geonames_prepare_query_string($service, $query)) {
375 return FALSE;
376 }
377
378 // retrieve results from Cache, or from GeoNames Server
379 $cache_geores = geonames_config($service, 'result_cache_prefix') . $querystring;
380 $cache_geodat = geonames_config($service, 'data_cache_prefix') . $querystring;
381 if (GEONAMES_CACHE) {
382 // if columns are passed, get DATA from cache
383 if (($columns) && ($cache = cache_get($cache_geodat, GEONAMES_CACHE_TABLE)) && (!empty($cache->data))) {
384 $data = $cache->data;
385 $datacache = TRUE;
386 }
387 // if columns are not passed, get RESULT from cache (fastest) - Don't mess with the parenthesises
388 else if ((!$columns) && ($cache = cache_get($cache_geores, GEONAMES_CACHE_TABLE)) && (!empty($cache->data))) {
389 $result = $cache->data;
390 if (variable_get('geonames_reveal_cache', TRUE)) {
391 $result->request['cached'] = 'result';
392 }
393 }
394 }
395
396 // if nothing has been loaded from cache (new query, or cache is disabled) get data from GeoNames
397 if (!$result && !$data) {
398 $data = file_get_contents(geonames_service_url($service) . $querystring .'&app=drupal');
399 if ($data == FALSE) {
400 drupal_set_message(t('Fetching data from GeoNames failed... please try again later.'), 'error');
401 return FALSE;
402 }
403 else {
404 // increase credit counter if commercial server is used
405 if (GEONAMES_COMMERCIAL) {
406 geonames_credits_pay($service);
407 }
408 if (GEONAMES_CACHE && $columns) {
409 cache_set($cache_geodat, $data, GEONAMES_CACHE_TABLE, time() + GEONAMES_CACHE_LIMIT);
410 }
411 }
412 }
413
414 if (!is_object($result) && $data) {
415 // parse the XML data we have received, and add some more properties to the object
416 $result = geonames_parse_xml($data, $fields);
417 $result->service = $service;
418 $result->request['url'] = geonames_service_url($service) . $querystring;
419 $result->request['bytes'] = strlen($data);
420 $result->query = $query;
421 if (variable_get('geonames_reveal_cache', TRUE) && $datacache) {
422 $result->request['cached']= 'data';
423 }
424 if (geonames_config($service, 'pager')) {
425 $result->pager = _prepare_pager_data($query['maxrows'], $query['startrow'], $result->total_results_count);
426 }
427 if (GEONAMES_CACHE && !$columns) {
428 cache_set($cache_geores, $result, GEONAMES_CACHE_TABLE, time() + GEONAMES_CACHE_LIMIT);
429 }
430
431 // If sorting parameters are passed
432 if ($sortby || $sortorder) {
433 _geonames_sort($result->results, $sortby, $sortorder);
434 }
435 }
436
437 $result->request['seconds'] = microtime(TRUE) - $time_start;
438
439 return $result;
440 }
441
442 /**
443 * Returns the Service URL for the specified service
444 *
445 * @param $service
446 * string, i.e. 'search' for the search service
447 * @return url (string)
448 */
449 function geonames_service_url($service) {
450 $service_path = geonames_config($service, 'service_path');
451 if ($service_path) {
452 return (GEONAMES_COMMERCIAL)
453 ? variable_get('geonames_commercial_server', '') .'/'. $service_path .'?username='. variable_get('geonames_commercial_username', '') .'&'
454 : GEONAMES_FREE_SERVER_URL .'/'. $service_path .'?';
455 }
456 return false;
457 }
458
459 /**
460 * Prepare the Query String
461 *
462 * This function verifies the integrity of the query array, and builds the parts
463 * using geonames_prepare_query_string_element
464 *
465 * @param $query
466 * array with query properties
467 *
468 * @return
469 * URL Query String
470 *
471 * @see
472 * geonames_prepare_query_string_element
473 */
474 function geonames_prepare_query_string($service, $query) {
475 foreach ($query as $key => $val) {
476 if (is_array($val)) {
477 // Verify that the parameter is allowed to be an array;
478 if ($arrpar = geonames_config($service, 'array_parameters')) {
479 if (!in_array($key, $arrpar)) {
480 return FALSE;
481 }
482 }
483 }
484 else {
485 $val = array($val); // for convenience in the upcoming foreach loop ;)
486 }
487 // build query string for all elements
488 foreach ($val as $value) {
489 $querystring .= geonames_prepare_query_string_element($service, $key, $value);
490 }
491 }
492 return ($querystring ? substr($querystring, 1, strlen($querystring)) : '&'); // Remove presceding & -- for empty queries, add a harmless & ;)
493 }
494
495 /**
496 * Build Query String Elements
497 *
498 * The function changes keys and values from lowercase and
499 * mixed, to the GeoNames format. The function also filters
500 * out unwanted/disallowed elements from the query.
501 *
502 * @param $service
503 * string (ex: 'search' or 'nearbyplace')
504 *
505 * @see
506 * geonames_prepare_query_string
507 */
508 function geonames_prepare_query_string_element($service, $key, $val) {
509 $parameters = geonames_config($service, 'allowed_parameters');
510 $keys = array_keys($parameters);
511
512 // is the parameter allowed for this service? else just skip it
513 if (in_array($key, $keys)) {
514 // radius is always Maximum 300 km
515 if ($key == 'radius' && $val > 300) {
516 $val = 300;
517 }
518 // uppercase values
519 if ($key == 'style' || $key == 'featureclass' || $key == 'featurecode') {
520 $val = strtoupper($val);
521 }
522 // lowercase values
523 if ($key == 'lang') {
524 $val = strtolower($val);
525 }
526 // url encode string parameters that may contain special characters or spaces
527 if ($key == 'name' || $key == 'query') {
528 $val = urlencode($val);
529 }
530 // switch to GeoNames style parameter names
531 $key = $parameters[$key];
532 if ($val != '') {
533 $qs_element = "&$key=$val";
534 }
535 }
536 return $qs_element;
537 }
538
539 /**
540 * Checks if Required Parameters are Set for $service/$query
541 *
542 * @param $service
543 * string, (for example search or nearbyplace)
544 * @param $query
545 * array (query array)
546 * @param $single
547 * true = one of the paramters required
548 * false = all of the parameters required
549 * @return
550 * boolean TRUE/FALSE
551 */
552 function geonames_query_required_parameters_set($service, $query, $single = 'single') {
553 $required_parameters = geonames_config($service, 'required_parameters');
554 if (!is_array($required_parameters)) {
555 return TRUE; // There are no required parameters ;)
556 }
557 // If the required parameters consists of a set of arrays (several combinations)
558 // one of the sets must return true
559 if (!is_array($required_parameters[0])) {
560 // Convert to array of array
561 $required_parameters = array($required_parameters);
562 }
563
564 $set = false;
565 foreach ($required_parameters as $rp) {
566 if ($set) {
567 return $set; // no need to keep working if we have a confirmed configuration
568 }
569 $count = 0;
570 switch ($single) {
571 case 'single':
572 // One of the defined parameters are required
573 foreach ($rp as $param) {
574 if (!$set) {
575 $set = ($query[$param]) ? TRUE : FALSE;
576 }
577 }
578 break;
579
580 case 'all':
581 // All of the defined parameters required
582 foreach ($rp as $param) {
583 if ($query[$param]) {
584 $count = $count + 1;
585 }
586 }
587 $set = (count($rp) == $count) ? TRUE : FALSE;
588 break;
589 } // end switch ($single)
590 } // end foreach required parameter
591 return $set;
592 }
593
594 /**
595 * Parser for the GeoNames Search Service XML Response
596 *
597 * If fields is specified, the return object will only return the fields
598 * you ask for, in the specified order.
599 *
600 * @param $data
601 * geonames XML document
602 *
603 * @param $fields
604 * optional array of fields that the returned object must contain
605 *
606 * @return
607 * $result object
608 */
609 function geonames_parse_xml($data, $columns = FALSE) {
610 // temporary solution; get stuff from XML tag to distinguish between pay-service and free service
611 preg_match('/<\?xml version="(.*)" encoding="([a-zA-Z\-0-9]*)"(.*)\?>/', $data, $regs);
612 if ($regs[3]) {
613 $subreg = explode('"', $regs[3]);
614 $result->standalone = $subreg[1];
615 }
616
617 $xml = new SimpleXMLElement($data);
618 if (isset($xml) && is_array($xml->status)) {
619 $result->status['message'] = (string) $xml->status['message'];
620 $result->status['value'] = (string) $xml->status['value'];
621 }
622 else {
623 $field_template = array();
624 $field_actual = array();
625 foreach ($xml->children() as $entry) {
626 $fields = array();
627 $entryhasvalues = FALSE;
628 // create structure template based on the $columns passed, this ensures the order is like specified
629 foreach ($columns as $columnname) {
630 $fields[$columnname] = '';
631 }
632 if (!$field_template) {
633 // store template on first loop only
634 $field_template = array_keys($fields);
635 }
636 // build a list of all the attributes in the entry
637 foreach ($entry->children() as $key => $field) {
638 $fieldname = strtolower($key);
639 $field_actual[$fieldname] = '';
640 // TODO!! Either in_array or $[]... "search" needs in_array...
641 if ((!$columns) || in_array($fieldname, $columns) || $fieldname = 'timezone') {
642 $entryhasvalues = TRUE;
643 $fields[$fieldname] = (string) $field;
644 // if timezone is not requested, don't return it! ;)
645 if ($fieldname == 'timezone' && $columns && !in_array($fieldname, $columns)) {
646 unset($fields['timezone']);
647 }
648 foreach ($field->attributes() as $attr) {
649 $attrname = strtolower($attr->getName());
650 $field_actual[$attrname] = '';
651 $fields[$attrname] = (string) $attr;
652 if ($attrname == 'gmtoffset' && $columns && !in_array($attrname, $columns)) {
653 unset($fields['gmtoffset']);
654 }
655 if ($attrname == 'dstoffset' && $columns && !in_array($attrname, $columns)) {
656 unset($fields['dstoffset']);
657 }
658 }
659 }
660 }
661
662 // clean out field names that weren't in the result set from GeoNames
663 if (!$field_diff) {
664 $field_diff = array_diff($field_template, array_keys($field_actual));
665 if (is_array($field_diff)) {
666 $field_clean == true;
667 }
668 }
669 // Clean out the container elements that are not in the result set from geonames!
670 elseif ($field_clean) {
671 foreach ($field_diff as $remove_field) {
672 unset($fields[$remove_field]);
673 }
674 }
675
676 if ($entryhasvalues) {
677 $result->results[] = $fields;
678 }
679 }
680
681 $result->total_results_count = ($xml->totalResultsCount) ? (string) $xml->totalResultsCount : count($result->results);
682 }
683 return $result;
684 }
685
686 /**
687 * Commercial Service: Increase the Credits Counter ;)
688 */
689 function geonames_credits_pay($service) {
690 $cost = (is_numeric($service)) ? $service : geonames_config($service, 'credit_cost');
691 variable_set('geonames_commercial_credits', $cost + variable_get('geonames_commercial_credits', 0));
692 }
693
694 /**
695 * Commercial Service: Reset Credits Counter
696 */
697 function geonames_credits_reset($adminpage = TRUE) {
698 variable_set('geonames_commercial_credits', 0);
699 variable_set('geonames_commercial_credits_since', time());
700 if ($adminpage) {
701 drupal_set_message('Credits and time reset.');
702 drupal_goto('admin/settings/geonames');
703 }
704 }
705
706 /**
707 * Clear the Results and Data Cache
708 */
709 function geonames_cache_clear($adminpage = TRUE) {
710 $res_cache_prefixes = geonames_config('all values', 'result_cache_prefix');
711 $dat_cache_prefixes = geonames_config('all values', 'data_cache_prefix');
712 $cache_prefixes = array_merge($res_cache_prefixes, $dat_cache_prefixes);
713 foreach ($cache_prefixes as $prefix) {
714 cache_clear_all($prefix, GEONAMES_CACHE_TABLE, TRUE);
715 }
716 if ($adminpage) {
717 drupal_set_message('Cache cleared.');
718 drupal_goto('admin/settings/geonames');
719 }
720 }
721
722 /**
723 * Return the Number of Items Currently in the Cache
724 */
725 function _geonames_cache_items() {
726 $res_cache_prefixes = geonames_config('all values', 'result_cache_prefix');
727 $dat_cache_prefixes = geonames_config('all values', 'data_cache_prefix');
728 $cache_prefixes = array_merge($res_cache_prefixes, $dat_cache_prefixes);
729 foreach ($cache_prefixes as $prefix) {
730 $where .= ($where) ? ' OR ' : '';
731 $where .= " cid LIKE '". $prefix ."%' ";
732 }
733 $cache = db_fetch_object(db_query("SELECT count(*) as items FROM {%s} WHERE $where", GEONAMES_CACHE_TABLE));
734 return $cache->items;
735 }
736
737 /**
738 * Implementation of hook_menu().
739 */
740 function geonames_menu() {
741 $items['admin/settings/geonames'] = array(
742 'type' => MENU_NORMAL_ITEM,
743 'title' => t('GeoNames'),
744 'access arguments' => array('administer site configuration'),
745 'page callback' => 'drupal_get_form',
746 'page arguments' => array('geonames_admin_settings'),
747 'description' => t('GeoNames Configuration.'),
748 );
749 $items['admin/settings/geonames/clear_cache'] = array(
750 'type' => MENU_CALLBACK,
751 'access arguments' => array('administer site configuration'),
752 'page callback' => 'geonames_cache_clear',
753 'page arguments' => TRUE,
754 );
755 $items['admin/settings/geonames/reset_credits'] = array(
756 'type' => MENU_CALLBACK,
757 'access arguments' => array('administer site configuration'),
758 'page callback' => 'geonames_credits_reset',
759 'page arguments' => TRUE,
760 );
761 $docpath = variable_get('geonames_documentation_path', 'geonames/docs');
762 $items[$docpath] = array(
763 'title' => t('Geonames Docs'),
764 'access arguments' => array('geonames docs'),
765 'page callback' => 'geonames_docs',
766 'page arguments' => array(),
767 );
768 return $items;
769 }
770
771 function geonames_menu_link() {
772
773 }
774
775 /**
776 * Implementation of hook_access().
777 */
778 function geonames_access($op, $node) {
779 global $user;
780 if ($op == 'view') {
781 return (user_access('geonames docs'));
782 }
783 }
784
785 /**
786 * Implementation of hook_perm().
787 */
788 function geonames_perm() {
789 return array(
790 'geonames docs',
791 );
792 }
793
794 /**
795 * Documentation pages is a form, stupid but it works for now :))
796 */
797 function geonames_docs() {
798 $page = '<div>';
799 $page .= '<div class="messages">'. t('Syntax: $result = geonames_query($service, $query, $options)') .'</div>';
800 $page .= '</div>';
801
802 $element = array(
803 '#collapsible' => true,
804 '#collapsed' => true,
805 '#title' => t('Parameter: $service (string)'),
806 '#value' => '<div>The "Usage Instructions for Installed Services" below, lists all the currently installed services available on your system. Please look there for details on the various services.</div>',
807 );
808 $page .= theme('fieldset', $element);
809
810 $element = array(
811 '#collapsible' => true,
812 '#collapsed' => true,
813 '#title' => t('Parameter: $query (associative array)'),
814 '#value' => '<div>Queries (query parameters) are defined as associative arrays, as the one above. For a list of allowed query parameters, please see the specific service under "Usage Instructions for Installed Services".'.'<div class="messages">'. t('Example: $query = array(\'lat\' => 59.95, \'lng\' => 10.77)') .'</div>'.'This query can be used with all services that require the parameters lat and lng, for example Find Nearby Place Name.<br /><br />
815 The query parameters are usually integers (numbers) or strings (text), but for a few services they are allowed to be arrays (multiple values) in order to be able to pass several values of the same type (for instance countries or featurecodes), in the example below we pass on two countries.'.'<div class="messages">'. t('Example: $query = array(\'name\' => \'Holmen\', \'country\' => array(\'NO\',\'SE\'))') .'</div>If you use this query with the \'search\' service (GeoNames Fulltext Search), you will find places with the name <i>Holmen</i> in <i>Norway</i> or <i>Sweden</i></div>',
816 );
817 $page .= theme('fieldset', $element);
818
819 $element = array(
820 '#collapsible' => true,
821 '#collapsed' => true,
822 '#title' => t('Parameter: $options (associative array) - optional'),
823 '#value' => t('<div>You may use the $option parameter to modify the results. Limit the number of fields returned, order the columns, or sort the rows. The options array has three optional properties: columns, sortby and sortorder (all of them may be used independently).<br /><br /><b>columns</b> is an array of fieldnames ') .'<div class="messages">'. t('Example: $options '."= array('columns' => array('countryname', 'countrycode') )") .'</div>'. t('By passing this option your results will only contain the two columns specified (countryname and countrycode). The result object will also be arranged with your results in the specified order.') . t('<br /><br /><b>sortby</b> is a fieldname (string) ') .'<div class="messages">'. t('Example: $options '."= array('sortby' => 'countryname')") .'</div>'. t('By passing this option your results will be ordered by the countryname.') . t('<br /><br /><b>sortorder</b> is either ASC or DESC (string) ') .'<div class="messages">'. t('Example: $options '."= array('sortorder' => 'DESC')") .'</div>'. t('By passing this option your results will be returned in a descending order (Your list of Countries would start with the letter Z).') . t('<br /><br /><div class="messages">') . t('Multiple Options Example: $options '."= array('sortby' => 'countryname', 'sortorder' => 'DESC')") .'</div>'. t('This option would return your results ordered by Country Name, descending.</div>'),
824 );
825 $page .= theme('fieldset', $element);
826
827 $element = array(
828 '#collapsible' => true,
829 '#collapsed' => true,
830 '#title' => t('Result: $result (object)'),
831 '#value' => '<div>The results object will look something like this: <br />
832 <pre>
833 $result->results = array(
834 [0] = array(\'name\' => \'Oslo\', ...)
835 [1] = array(\'name\' => \'Nydalen\', ...)
836 ...
837 )
838 $result->total_results_count = integer, number of total results
839 $result->service = string service name
840 $result->request = array (
841 \'url\' = URL sent to GeoNames server
842 \'bytes\' = size of data requested from GeoNames server
843 \'seconds\' = time used for request to GeoNames server
844 \'cached\' = if the result is loaded from the cache, this variable is set to the cache type (result or data)
845 )
846 $result->query = array(
847 \'lat\' = 23
848 \'lng\' = 34
849 \'maxrows\' = 10
850 ...
851 )
852 $result->pager = array() - associative array, only available for some services
853 $result->standalone = if set to <i>no</i>, the GeoNames commercial service is used
854 </pre></code>
855 </div>',
856 );
857 $page .= theme('fieldset', $element);
858
859 $page .= '<br /><div>'.t(' In the usage instructions below, <b>bold</b> parameters are ') .'<b>'. t('mandatory') .'</b>'. t('. Parameters that are structured like \'key=value\' are default values for parameters, and may be overridden. Items in ') .'<i>' . t('italics') .'</i> '. t('are ') .'<i>' . t('optional parameters') .'</i>. '. t('Services with multiple bold parameters may be one out of two types; single or all - read the instructions carefully. ') .'<br /><br /><h2>'. t('Usage Instructions for Installed Services') .'</h2></div>';
860
861 $services = geonames_config(); // geonames_config('services', 'keys');
862 // This function used to return the full configuration........ look into how to fix it (fix in other version as well)!
863
864 foreach ($services as $service => $sc) { // service configuration
865 $usage = sprintf("<div class=messages>Usage: \$result = geonames_query('%s', \$query, <i>\$options</i>)</div>", $service);
866
867 // Compile the $query part... this is tricky!
868 // First, check if the required parameters is an array of arrays (how many times do we have to loop?)
869 // count variations of required parameters combinations
870 $variations = (is_array($sc['required_parameters'][0])) ? count($sc['required_parameters']) : 1;
871
872 $usage_query = '';
873 $loops = 0;
874 while ($loops < $variations) {
875
876 // Get $rp
877 $rp = ($variations > 1) ? $sc['required_parameters'][$loops] : $sc['required_parameters'];
878
879 $usage_query .= ($usage_query) ? '<b>or</b><div style="height: 8px"></div>' : '';
880 $usage_query .= '$query = array(';
881
882 $numreqpar = 0; // Count number of request parameters -- used for displaying various strings later
883 if ($rp) {
884 foreach ($rp as $pmtr) {
885 $numreqpar++;
886 $usage_query .= '<b>'. $pmtr .'</b>, ';
887 }
888 }
889
890 if ($sc['query_defaults']) {
891 foreach ($sc['query_defaults'] as $key => $val) {
892 if ($key != 'type') {
893 $usage_query .= $key .'='. $val .', ';
894 }
895 }
896 }
897
898 if (is_array($sc['allowed_parameters'])) {
899 foreach ($sc['allowed_parameters'] as $key => $val) {
900 if ((!is_array($rp) || (is_array($rp) && !in_array($key, $rp))) && (!is_array($sc['query_defaults'] || (is_array($sc['query_defaults']) && !in_array($key, array_keys($sc['query_defaults'])))))) {
901 if ($key != 'type') {
902 $usage_query .= '<i>'. $key .'</i>, ';
903 }
904 }
905 }
906 }
907
908 $query_comments = '';
909 if (is_array($sc['array_parameters'])) {
910 // Some of the parameters are allowed to be arrays...
911 foreach ($sc['array_parameters'] as $arrpar) {
912 $query_comments .= sprintf('<li> %s is allowed to be an array of strings</li>', $arrpar);
913 }
914 }
915
916 // strip the last ', ' from the querystring
917 if (strstr($usage_query, ',')) {
918 $usage_query = substr($usage_query, 0, strlen($usage_query) - 2);
919 }
920 if ($sc['required_parameters_type'] == 'single') {
921 if ($numreqpar > 1) {
922 $query_comments .= '<li> just one of the bold parameters is required </li>';
923 }
924 }
925 if ($sc['required_parameters_type'] == 'all') {
926 if ($numreqpar == 2) {
927 $query_comments .= '<li> both bold parameters are required </li>';
928 }
929 else if ($numreqpar > 2) {
930 $query_comments .= '<li> all bold parameters are required </li>';
931 }
932 }
933
934 // If no comments, add some space with br
935 $query_comments = ($query_comments) ? sprintf("<ul>%s</ul>", $query_comments) : '<div style="height: 6px"></div>';
936
937 $usage_query .= ($usage_query) ? ')'. $query_comments : '';
938
939 $loops++;
940
941 } // End while loop
942
943 $resultrows = '<div>$result->results = array(';
944
945 if ($sc['columns']) {
946 // Columns are defined -- use those
947 foreach ($sc['columns'] as $a) {
948 $resultrows .= $a .', ';
949 }
950 $resultrows = substr($resultrows, 0, strlen($resultrows)-2) .')';
951 }
952 else {
953 // Probably short/medium/long/full syntax... TODO this does not work... DEBUG
954 $gf = geonames_fields('full', true);
955 foreach ($gf as $f) {
956 $resultrows .= $f .', ';
957 }
958 $resultrows = substr($resultrows, 0, strlen($resultrows)-2) .')';
959 $resultrows .= ' -- FULL style';
960 }
961 $resultrows .= '</div>';
962
963 $element = array(
964 '#collapsible' => true,
965 '#collapsed' => true,
966 '#title' => t($sc['service_full_name']),
967 '#value' => '<div>Purpose: '. $sc['description'] . $usage . $usage_query . $resultrows .'</div>',
968 );
969 $page .= theme('fieldset', $element);
970
971 }
972
973 return $page;
974 }
975
976 /**
977 * Admin Settings Page
978 */
979 function geonames_admin_settings() {
980 $form['geonames_docs_path'] = array(
981 '#type' => 'textfield',
982 '#title' => t('Path to GeoNames Documentation'),
983 '#description' => t('Where do you want the documentation to reside?'),
984 '#default_value' => variable_get('geonames_docs_path', 'geonames/docs'),
985 );
986 $form['cache'] = array(
987 '#type' => 'fieldset',
988 '#title' => t('Cache'),
989 '#collapsible' => TRUE,
990 '#collapsed' => TRUE,
991 );
992 $form['cache']['clear_cache'] = array(
993 '#prefix' => '<div>',
994 '#value' => t('There are currently '. _geonames_cache_items() .' items in the cache. ['. l('Clear cache', 'admin/settings/geonames/clear_cache') .']'),
995 '#suffix' => '</div>',
996 );
997 $form['cache']['geonames_cache'] = array(
998 '#type' => 'radios',
999 '#title' => t('Caching'),
1000 '#description' => t('Enable unless you have a good reason to disable it.'),
1001 '#default_value' => variable_get('geonames_cache', TRUE),
1002 '#options' => array(1 => 'Enabled', 0 => 'Disabled'),
1003 );
1004 $form['cache']['geonames_cache_limit'] = array(
1005 '#type' => 'textfield',
1006 '#title' => t('Cache lifetime'),
1007 '#description' => t('Cache items for ... days'),
1008 '#default_value' => variable_get('geonames_cache_limit', 14),
1009 );
1010 $form['cache']['geonames_reveal_cache'] = array(
1011 '#type' => 'radios',
1012 '#title' => t('Reveal Cache'),
1013 '#description' => t("If an item is retrieved from the cache, the") ." request['cached'] ". t("property will be available in the result object if this switch is set to Yes."),
1014 '#default_value' => variable_get('geonames_reveal_cache', TRUE),
1015 '#options' => array(1 => 'Yes', 0 => 'No'),
1016 );
1017 $form['cache']['advanced'] = array(
1018 '#type' => 'fieldset',
1019 '#title' => t('Database Configuration (Advanced)'),
1020 '#collapsible' => TRUE,
1021 '#collapsed' => TRUE,
1022 );
1023 $form['cache']['advanced']['leadingtext'] = array(
1024 '#prefix' => '<div>',
1025 '#value' => t("The GeoNames cache grows pretty rapidly, and a large cache table may slow down other cache requests. The overall performance of your site may be improved by setting up a separate table for caching of GeoNames requests. Create the table manually (structure equivalent to Drupal's cache table), prior to changing the table name here."),
1026 '#suffix' => '</div>'
1027 );
1028 $form['cache']['advanced']['geonames_cache_table'] = array(
1029 '#type' => 'textfield',
1030 '#title' => t('Database Cache Table'),
1031 '#description' => t("Defaults to drupal's") ." <i>cache</i> ". t("table."),
1032 '#default_value' => variable_get('geonames_cache_table', 'cache'),
1033 );
1034
1035 $commercial_collapsed = (variable_get('geonames_commercial_active', FALSE) == TRUE) ? FALSE : TRUE;
1036
1037 $form['commercial'] = array(
1038 '#type' => 'fieldset',
1039 '#title' => t('GeoNames Commercial Webservices'),
1040 '#collapsible' => TRUE,
1041 '#collapsed' => $commercial_collapsed
1042 );
1043 $form['commercial']['leadingtext'] = array(
1044 '#prefix' => '<div>',
1045 '#value' => t('You should always support developers of quality services. Increased performance is cheap, and you should afford it.') .' '. t('Visit the') .' '. l('GeoNames Commercial Webservices', 'http://www.geonames.org/professional-webservices.html') .' '. t('page for more information.'),
1046 '#suffix' => '</div>'
1047 );
1048 if (variable_get('geonames_commercial_active', FALSE) == TRUE) {
1049 // Show the Status and Credits Counter
1050 if (GEONAMES_COMMERCIAL) {
1051 $form['commercial']['counter'] = array(
1052 '#prefix' => '<div><p>',
1053 '#value' => '<b>'. t('Status') .':</b><br />'. t('You have used') .' <i>'. variable_get('geonames_commercial_credits', 0) .'</i> '. t('credits since') .' '. format_date(variable_get('geonames_commercial_credits_since', 'unknown')) .'. ['. l('Reset', 'admin/settings/geonames/reset_credits')
1054 .']',
1055 '#suffix' => '</p></div>'
1056 );
1057 }
1058 }
1059 $form['commercial']['geonames_commercial_active'] = array(
1060 '#type' => 'radios',
1061 '#title' => t('GeoNames Commercial Webservices'),
1062 '#default_value' => variable_get('geonames_commercial_active', 0),
1063 '#options' => array(1 => 'Enabled', 0 => 'Disabled'),
1064 );
1065 $form['commercial']['geonames_commercial_active_copy'] = array(
1066 '#type' => 'value',
1067 '#value' => variable_get('geonames_commercial_active', 0),
1068 );
1069 $form['commercial']['geonames_commercial_server'] = array(
1070 '#type' => 'textfield',
1071 '#title' => t('URL to Commercial Service Server'),
1072 '#description' => 'Example: http://ws.geonames.net - without the trailing slash',
1073 '#default_value' => variable_get('geonames_commercial_server', 'http://ws.geonames.net'),
1074 );
1075 $form['commercial']['geonames_commercial_server_copy'] = array(
1076 '#type' => 'value',
1077 '#value' => variable_get('geonames_commercial_server', 'http://ws.geonames.net'),
1078 );
1079 $form['commercial']['geonames_commercial_username'] = array(
1080 '#type' => 'textfield',
1081 '#title' => t('Username'),
1082 '#default_value' => variable_get('geonames_commercial_username', ''),
1083 );
1084 $form['commercial']['geonames_commercial_username_copy'] = array(
1085 '#type' => 'value',
1086 '#value' => variable_get('geonames_commercial_username', ''),
1087 );
1088
1089 $form['licencing'] = array(
1090 '#prefix' => '<ul><li>',
1091 '#value' => t('Note: You must give credit to ') . l('GeoNames', 'http://www.geonames.org/') . t(' if you are not using the commercial services, for example by including a link to ') . l('their site', 'http://www.geonames.org/') . t(' on your pages. The GeoNames geographical database is released under the') .' '. l('Creative Commons Attribution Licence 3.0', 'http://creativecommons.org/licenses/by/3.0/') .'.',
1092 '#suffix' => '</li></ul>'
1093 );
1094
1095 return system_settings_form($form);
1096 }
1097
1098 /**
1099 * Admin Settings Page : Validation
1100 */
1101 function geonames_admin_settings_validate($form_id, $form_values) {
1102