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

Contents of /contributions/modules/geonames_cck/geonames_cck.module

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


Revision 1.10 - (show annotations) (download) (as text)
Wed Oct 28 14:16:55 2009 UTC (4 weeks, 3 days ago) by thegreenman
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +650 -743 lines
File MIME type: text/x-php
Some new updates. There are going to be a few bits of School of Everything only code in here.
But it should get things going.
1 <?php
2 /* $Id: geonames_cck.module,v 1.8 2008/08/04 16:34:26 thegreenman Exp $ */
3
4 /**
5 * @file
6 * The geonames_cck module implements basic geolocation tools using geonames web services.
7 *
8 * This module was designed to work with the geomap module. Locations will be output using
9 * Geo microformats, and the geomap module will display any geo data on a google map.
10 *
11 * TODO: Make multiple values work. Some work has been done, but, the form does not currently handle multiples
12 * TODO: Make places work. A stab has been made at "things nearby" but it's not really production level.
13 * TODO: Views integration. Since it's cck, there are come views tools, but not enough to really use.
14 *
15 */
16
17 /**
18 * implementation of hook_perm
19 *
20 * @return array
21 */
22 function geonames_cck_perm() {
23 return array('choose custom icon for node');
24 }
25
26 /**
27 * implementation of hook_menu()
28 * @param boolean $may_cache
29 * @return array
30 */
31 function geonames_cck_menu($may_cache) {
32 if ($may_cache) {
33 $items[] = array(
34 'path' => 'admin/settings/geonamescck/main',
35 'title' => t('Geonames CCK'),
36 'description' => t('Settings for Geonames cck module'),
37 'type' => MENU_DEFAULT_LOCAL_TASK, // optional
38 );
39 }
40 else {
41 /**
42 * Places are not ready yet.
43 *
44 $items[] = array(
45 'path' => 'place',
46 'title' => t('places'),
47 'description' => t('Display a place'),
48 'callback' => 'geonames_cck_place_page',
49 'access' => user_access('access place pages'),
50 'type' => MENU_CALLBACK,
51 );
52 */
53 if (arg(0) == 'node' && is_numeric(arg(1))) {
54 $items[] = array(
55 'path' => 'node/'. arg(1) .'/disambiguate',
56 'title' => t('Where are you based?'),
57 'access' => node_access('update', node_load(arg(1))),
58 'callback' => 'geonames_cck_disambiguate',
59 'callback arguments' => array(arg(1)),
60 'type' => MENU_CALLBACK, // This may work as a local task.
61 );
62 }
63 }
64 return $items;
65 }
66
67 /**
68 * implementation of hook_block()
69 *
70 */
71 function geonames_cck_block($op = 'list', $delta = 0, $edit = array()) {
72 switch ($op) {
73 case 'list':
74 $blocks = array(
75 'locations' => array('info' => t('geonames node locations list')),
76 'geonames_description' => array('info' => t('geonames - description of geonames')),
77 );
78 return $blocks;
79 break;
80 case 'view':
81 if (arg(0) == 'node' && is_numeric(arg(1)) && !arg(2)) {
82 $node = node_load(arg(1));
83
84 switch ($delta) {
85 case 'locations':
86 $block['content'] = theme('geonames_cck_location', $node, true);
87 break;
88 }
89 }
90 if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'disambiguate') {
91 switch ($delta) {
92 case 'geonames_description':
93 $block['content'] = theme('geonames_description');
94 break;
95 }
96 }
97 return $block;
98 break;
99 }
100 }
101
102
103
104 /**
105 * Implementation of hook_field_info().
106 */
107 function geonames_cck_field_info() {
108 return array(
109 'geonames_cck' => array('label' => 'Geonames location'),
110 );
111 }
112
113 /**
114 * Implementation of hook_field_settings().
115 */
116 function geonames_cck_field_settings($op, $field) {
117 switch ($op) {
118 case 'form':
119 $form = array();
120 return $form;
121
122 case 'validate':
123 break;
124
125 case 'save':
126 return array('name', 'geonameId', 'country', 'lat', 'lng', 'adminName1', 'adminCode1', 'adminName2', 'adminCode2', 'fcode', 'icon', 'shadow');
127
128 case 'database columns':
129 $columns = array(
130 'name' => array('type' => 'varchar', 'length' => 250, 'not null' => TRUE),
131 'geonameId' => array('type' => 'varchar', 'length' => 20, 'not null' => TRUE),
132 'country' => array('type' => 'varchar', 'length' => 2, 'not null' => TRUE),
133 'lat' => array('type' => 'float', 'length' => 10, 'not null' => FALSE),
134 'lng' => array('type' => 'float', 'length' => 10, 'not null' => FALSE),
135 'adminName1' => array('type' => 'varchar', 'length' => 100, 'not null' => TRUE),
136 'adminCode1' => array('type' => 'varchar', 'length' => 10, 'not null' => TRUE),
137 'adminName2' => array('type' => 'varchar', 'length' => 100, 'not null' => TRUE),
138 'adminCode2' => array('type' => 'varchar', 'length' => 10, 'not null' => TRUE),
139 'fcode' => array('type' => 'varchar', 'length' => 10, 'not null' => FALSE),
140 'icon' => array('type' => 'varchar', 'length' => 250, 'not null' => FALSE),
141 'shadow' => array('type' => 'varchar', 'length' => 250, 'not null' => FALSE),
142 );
143 return $columns;
144 case 'filters':
145 // Filters are managed through the views include file
146 }
147 }
148
149 /**
150 * Implementation of hook_field().
151 */
152 function geonames_cck_field($op, &$node, $field, &$items, $teaser, $page) {
153 switch ($op) {
154 case 'load':
155 break;
156 case 'view':
157 break;
158 case 'insert':
159 break;
160 case 'update':
161 break;
162 case 'delete':
163 break;
164 }
165 return $output;
166 }
167
168 /**
169 * Implementation of hook_widget_info().
170 */
171 function geonames_cck_widget_info() {
172 return array(
173 'geoname_cck' => array(
174 'label' => t('Global Location'),
175 'field types' => array('geonames_cck'),
176 ),
177 );
178 }
179
180
181 /**
182 * Implementation of hook_widget_settings().
183 */
184 function geonames_cck_widget_settings($op, $widget) {
185
186 switch ($op) {
187 case 'form':
188 $form = array();
189 $form['icon'] = array(
190 '#type' => 'textfield',
191 '#title' => t('Custom icon'),
192 '#default_value' => $widget['icon'] ? $widget['icon'] : '',
193 '#required' => FALSE,
194 '#description' => t('To set a default icon for this field, add a full URL of the icon. '. l('List of icons', 'http://www.visual-case.it/cgi-bin/vc/GMapsIcons.pl', array('rel' => 'nofollow', 'target' => 'blank'))),
195 );
196 $form['shadow'] = array(
197 '#type' => 'textfield',
198 '#title' => t('Shadow for custom icon'),
199 '#default_value' => $widget['shadow'] ? $widget['shadow'] : '',
200 '#required' => FALSE,
201 '#description' => t('To set a default icon&apos;s shadow for this field, add a full URL of the shadow'),
202 );
203 return $form;
204
205 case 'validate':
206 $field_names = array('icon', 'shadow');
207 foreach ($field_names as $fname) {
208 if ($widget[$fname]) {
209 if (!valid_url($widget[$fname], true)) {
210 form_set_error($fname, 'invalid URL');
211 }
212 }
213 }
214
215 break;
216
217 case 'save':
218 return array('icon', 'shadow');
219 }
220 }
221
222 /**
223 * Implementation of hook_widget().
224 */
225 function geonames_cck_widget($op, &$node, $field, &$items) {
226 $fieldname = $field['field_name'];
227 switch ($op) {
228 case 'prepare form values':
229 break;
230
231 case 'form':
232 $form = _geonames_cck_widget_form($node, $field, $items);
233 return $form;
234
235 case 'validate':
236 foreach ($items as $delta => $item) {
237 if (!$item['geonameId'] || ($item['name'] != $item['old_name'] OR $item['country'] != $item['old_country'])) {
238 // location has changed
239 $search_results = geoapi_convert_location_to_geo($item['name'], $item['country']);
240 if ( !$search_results -> results ) {
241 // We are not using for_set_error here because we use the disambiguation pages to fix things
242 $values = array('@name' => $item['name']);
243
244 $message = t('Your location "@name" was not found in our locations database.');
245 if ($node->nid) {
246 $values['!link'] = l( t('Find another location?'), 'node/'. $node->nid .'/disambiguate');
247 $message .= ' !link';
248 }
249 drupal_set_message(t( $message , $values ));
250 }
251 }
252 }
253 //icon url
254
255 break;
256 case 'process form values':
257 foreach ($items as $delta => $item) {
258 if ($item['name'] != $item['old_name'] OR $item['country'] != $item['old_country']) {
259 // location has changed
260 // This code currently duplicates the validation code. Not perfect, but results are cached.
261 // TODO: Clean up the validation process
262 $search_results = geoapi_convert_location_to_geo($item['name'], $item['country']);
263 $locations = $search_results -> results;
264 if ($search_results -> total_results_count) {
265 $items[$delta] = _geoapi_convert_geonames_result_to_cck_value($locations[0]);
266 }
267 else {
268 // clear any old values
269 unset($items[$delta]['lat']);
270 unset($items[$delta]['lng']);
271 }
272 }
273 }
274 break;
275 }
276 }
277
278
279 /**
280 * Generate the form for the widget
281 *
282 */
283 function _geonames_cck_widget_form($node, $field, $items) {
284 if ($field['required']) {
285 // make field not required on admin pages
286 $required = (!(arg(0) == 'admin' && arg(1) == 'content')) ? true : false;
287 }
288 $fieldlabel = $field['widget']['label'];
289 $fieldname = $field['field_name'];
290 $fieldname_css = 'edit-'. strtr($field['field_name'], '_', '-');
291 $form = array();
292 $form[$fieldname]['#tree'] = true;
293 $count = 0;
294 $form[$fieldname][$count]=array(
295 'name' => array(
296 '#type' => 'textfield',
297 '#description' => t($field['widget']['description']),
298 //'#title' => t('Location'),
299 '#title' => $fieldlabel,
300 '#size' => 30,
301 '#maxlength' => 250,
302 '#default_value' => $items[$count]['name'] ? $items[$count]['name'] : geoapi_get_default_city(),
303 '#attributes' => array('class' => 'float'),
304 ),
305 'country' => array(
306 '#type' => 'select',
307 '#title' => $fieldlabel .'&nbsp;'. t('country'),
308 '#default_value' => $items[$count]['country'] ? strtoupper($items[$count]['country']) : geoapi_get_default_country(),
309 '#attributes' => array('class' => 'float'),
310 '#options' => geoapi_countries(),
311 '#multiple' => false,
312 '#required' => $required,
313 ),
314 'lat' => array(
315 '#type' => 'hidden',
316 '#default_value' => $items[$count]['lat'],
317 ),
318 'lng' => array(
319 '#type' => 'hidden',
320 '#default_value' => $items[$count]['lng'],
321 ),
322 'old_name' => array(
323 '#type' => 'value',
324 '#value' => $items[$count]['name']
325 ),
326 'old_country' => array(
327 '#type' => 'value',
328 '#value' => $items[$count]['country']
329 ),
330 'icon' => array(
331 '#type' => 'textfield',
332 '#description' => t('If you want to set a custom icon for this location, enter the full URL of the icon here.'. l('List of icons', 'http://www.visual-case.it/cgi-bin/vc/GMapsIcons.pl', array('rel' => 'nofollow', 'target' => 'blank'))),
333 '#title' => $fieldlabel .'&nbsp;'. t('Icon'),
334 '#size' => 30,
335 '#maxlength' => 250,
336 '#default_value' => $items[$count]['icon'] ? check_url($items[$count]['icon']) : '',
337 //'#attributes' => array('class' => 'float'),
338 ),
339 'shadow' => array(
340 '#type' => 'textfield',
341 '#description' => t('If you want to set a custom icon shadow for this location, enter the of the icon here.'),
342 '#title' => $fieldlabel .'&nbsp;'. t('Shadow'),
343 '#size' => 30,
344 '#maxlength' => 250,
345 '#default_value' => $items[$count]['shadow'] ? check_url($items[$count]['shadow']) : '',
346 //'#attributes' => array('class' => 'float'),
347 ),
348 'geonameId' => array(
349 '#type' => 'value',
350 '#value' => $items[$count]['geonameId']
351 ),
352 );
353 return $form;
354 }
355
356
357 /**
358 * Implementation of hook_field_formatter_info().
359 */
360 function geonames_cck_field_formatter_info() {
361 $formatters = array(
362 'default' => array(
363 'label' => t('Default'),
364 'field types' => array('geonames_cck'),
365 ),
366 'no_geotag' => array(
367 'label' => t('No geotag'),
368 'field types' => array('geonames_cck'),
369 ),
370 'geo_hidden' => array(
371 'label' => t('Hidden geo code'),
372 'field types' => array('geonames_cck'),
373 ),
374 'disambiguation' => array(
375 'label' => t('With disambiguation link'),
376 'field types' => array('geonames_cck'),
377 ),
378
379
380 );
381 return $formatters;
382 }
383
384 /**
385 * Implementation of hook_field_formatter().
386 */
387 function geonames_cck_field_formatter($field, $item, $formatter, $node) {
388 if (!isset($item['name'])) {
389 return '';
390 }
391 $field_name = array('icon', 'shadow');
392 $n = count($field_name);
393
394 //get node-specific icon if available
395 for ($i=0; $i<$n; $i++) {
396
397 if ($item[$field_name[$i]]) {
398
399 ${$field_name[$i]} = check_url($item[$field_name[$i]]);
400 }
401 //get widget's default icon if available
402 else if ($field[$field_name[$i]]) {
403
404 ${$field_name[$i]} = check_url($field[$field_name[$i]]);
405 }
406 else{
407
408 ${$field_name[$i]}='';
409 }
410 }
411
412 switch ($formatter) {
413 case 'geo_hidden':
414 $output = theme('geonames_hidden', $item['lat'], $item['lng'], theme('geonames_cck_location_name', $item), $item, $node, $icon, $shadow);
415 break;
416 case 'disambiguation':
417 $output = theme('geonames_location_disambiguation', $item['lat'], $item['lng'], theme('geonames_cck_location_name', $item), $field, $item, $node, $icon, $shadow);
418 break;
419 case 'no_geotag':
420 $output = theme('geonames_location_no_geotag', $item['lat'], $item['lng'], theme('geonames_cck_location_name', $item), $item, $node);
421 break;
422 default:
423 $output = theme('geonames_location', $item['lat'], $item['lng'], theme('geonames_cck_location_name', $item), $item, $node, array(), $icon, $shadow);
424
425 }
426 return $output;
427 }
428
429
430 // =========================== Disambiguation
431
432 /**
433 * Manage the disambiguation for a node. This allows a user to say that the location we selected was incorrect.
434 * This is added to menu as a sub-path for each node.
435 * We look at various parameters to find out which fields to disambigute
436 *
437 * @param integer $nid
438 */
439
440 function geonames_cck_disambiguate($nid) {
441 // first we need to check what field to work with
442 $node = node_load($nid);
443 if ($_REQUEST['field']) {
444 $fields[$_REQUEST['field']] = $_REQUEST['field'];
445 }
446 else {
447 //TODO: Multiple field disambiguation would require some presentation work. (it's also untested)
448 // no field was specified, so lets do them all
449 // we might have more than one geonames field. We need to handle all of them
450 $type_info = content_types($node->type);
451 $all_fields = $type_info['fields'];
452 if (is_array($all_fields)) {
453 foreach ($all_fields as $field) {
454 ($field['type'] == 'geonames_cck') and $fields[$field['field_name']] = $field['field_name'];
455 }
456 }
457 //$fields = geonames_cck_get_all_fields()
458 }
459 // get place and country info from node
460 foreach ($fields as $field) {
461 // if we have a field index, and a new name and country,
462 // then we work only with that field value.
463 // otherwise we have to work with all values
464
465 if ($_REQUEST['field'] && !is_null($_REQUEST['index']) && $_REQUEST['name'] && $_REQUEST['country']) {
466 $indexes = array($_REQUEST['index']); // set the index
467 }
468 else {
469 // set up indexes array to process all field values
470 //for ($count = 0; $count < sizeof($field); $count++) {
471 $indexes[] = 0;
472 //}
473 }
474
475 foreach ($indexes as $index) {
476 // work out which location we need to disambiguate
477 if ($_REQUEST['name']) {
478 $place = check_plain($_REQUEST['name']);
479 }
480 else {
481 $place = $node -> {$field}[$index]['name'];
482 }
483 if ($_REQUEST['country']) {
484 $country = check_plain($_REQUEST['country']);
485 }
486 else {
487 $country = $node -> {$field}[$index]['country'];
488 }
489 if ($place) {
490 $locations = geoapi_convert_location_to_geo($place, $country);
491 }
492 $output .= theme('geonames_cck_disambiguate_page', $place, $country, $locations, $nid, $field, $index, $icon, $shadow);
493 }
494 }
495 return $output;
496 }
497
498 /**
499 * Determine which admin reqion we want to allocate this location to.
500 * Sometimes we want to create boundarys for locations to make things
501 * more intuitive for our users. (London in particular)
502 * TODO: Create an admin interface for this
503 */
504 function geonames_cck_get_admin_region($adminname, $lat, $lng) {
505 $regions = array(
506 'London' => array(
507 'topleft' => array('lat' => 51.655 , 'lng' => 0.186),
508 'bottomright' => array('lat' => 51.352 , 'lng' => -0.453 )
509 )
510 );
511 foreach ($regions as $name => $region) {
512 if ($lat < $region['topleft']['lat'] && $lat>$region['bottomright']['lat']
513 && $lng > $region['bottomright']['lng'] && $lng < $region['topleft']['lng']) {
514 $adminname = $name;
515 }
516 }
517 return $adminname;
518 }
519
520 /**
521 * Define a form for the disambiguation page.
522 * Allows a user to specify location
523 */
524 function geonames_cck_disambiguate_form($city, $country, $nid, $field, $index) {
525 $form['nid'] = array(
526 '#type' => 'value',
527 '#value' => $nid,
528 );
529 $form['field'] = array(
530 '#type' => 'value',
531 '#value' => $field,
532 );
533
534 $form['index'] = array(
535 '#type' => 'value',
536 '#value' => $index,
537 );
538
539 $form['search'] = array(
540 '#description' => '',
541 );
542 $form['search']['name'] = array(
543 '#type' => 'textfield',
544 '#title' => t('Name of city or town'),
545 '#size' => 20,
546 '#maxlength' => 250,
547 '#default_value' => $city,
548 '#attributes' => array('class' => 'float'),
549 );
550 $form['search']['country'] = array(
551 '#type' => 'select',
552 '#title' => t('Country'),
553 '#default_value' => strtoupper($country),
554 '#attributes' => array('class' => 'float'),
555 '#options' => geoapi_countries_trim(30),
556 '#multiple' => false,
557 );
558 $form['search']['submit'] = array(
559 '#type' => 'submit',
560 '#value' => t('Find my town'), // Note: translating button labels can be troublesome in Drupal 5. Everything works fine at the moment but developers extending this module should bear this in mind.
561 );
562 $form['#action'] = url('node/'.$nid.'/disambiguate');
563 // We use the get method here to make this form just send it's data back to the page
564 $form['#method'] = 'get';
565 return $form;
566 }
567
568 // =============helpers ========================//
569
570
571 /**
572 * Get the names of all geonames_cck fields.
573 *
574 * @return $array A list of field objects
575 */
576 function geonames_cck_get_all_fields() {
577 $fields = content_fields();
578 if (is_array($fields)) {
579 foreach ($fields as $field) {
580 ($field['type'] == 'geonames_cck') and $results[$field['field_name']] = $field;
581 }
582 }
583 return $results;
584 }
585
586 /**
587 * Get the names of all geonames_cck_fields in option format.
588 * The output of this function can be used to populate select lists
589 *
590 */
591 function geonames_cck_get_all_fields_options() {
592 $fields = geonames_cck_get_all_fields();
593 $options = array();
594 if (is_array($fields)) {
595 foreach ($fields as $field) {
596 $options[$field['field_name']] = $field['field_name'];
597 }
598 }
599 return $options;
600 }
601
602 /**
603 * retuns an array of locations for a node
604 *
605 * @param unknown_type $node
606 * @return array List of locations
607 */
608 function geonames_cck_get_all_node_locations($node) {
609 static $locations = array();
610 if (!$locations[$node->nid]) {
611 $locations[$node->nid] = array();
612 $geo_fields = geonames_cck_get_all_fields();
613 //look for all the defined fields
614 foreach ($geo_fields as $field_name => $field_data) {
615 if (is_array($node->$field_name)) {
616 // this node uses this field
617 foreach ($node->$field_name as $index => $field_data) {
618 $locations[$node->nid][]= $field_data;
619 }
620 }
621 }
622 }
623 return $locations[$node->nid];
624 }
625
626
627
628 /**
629 * Disables multiple field option (temporary)
630 */
631 function geonames_cck_form_alter($form_id, &$form) {
632 if (!user_access('choose custom icon for node')) {
633 //remove icon option if the user has no permission
634 unset($form['field_geonames_location'][0]['icon']);
635 unset($form['field_geonames_location'][0]['shadow']);
636 }
637 if (arg(0)=='admin' && arg(1)=='content' && arg(2)=='types' && $form['field_type']['#value']=='geonames_cck') {
638 //unset($form['field']['multiple']);
639 $form['field']['multiple']['#disabled'] = true;
640 $form['field']['multiple']['#description']=t('Sorry, this option is not yet available for geonames CCK');
641 }
642 }
643
644
645 // =================== includes ================= //
646
647 include_once('geonames_cck.theme.inc');
648 if (module_exists('views')) {
649 require_once(drupal_get_path('module', 'geonames_cck') .'/geonames_cck.views.inc');
650 }

  ViewVC Help
Powered by ViewVC 1.1.2