geofield.elements.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. /**
  3. * @file
  4. * Provides FormAPI element callbacks for geofield_latlon and geofield_proximity.
  5. */
  6. /**
  7. * Diameter of the Earth in kilometers.
  8. */
  9. define('GEOFIELD_KILOMETERS', 6371);
  10. /**
  11. * Diameter of the Earth in meters.
  12. */
  13. define('GEOFIELD_METERS', 6371000);
  14. /**
  15. * Diameter of the Earth in miles.
  16. */
  17. define('GEOFIELD_MILES', 3959);
  18. /**
  19. * Diameter of the Earth in yards.
  20. */
  21. define('GEOFIELD_YARDS', 6975175);
  22. /**
  23. * Diameter of the Earth in feet.
  24. */
  25. define('GEOFIELD_FEET', 20925525);
  26. /**
  27. * Diameter of the Earth in nautical miles.
  28. */
  29. define('GEOFIELD_NAUTICAL_MILES', 3444);
  30. /**
  31. * Implements hook_element_info().
  32. */
  33. function geofield_element_info() {
  34. return array(
  35. 'geofield_latlon' => array(
  36. '#input' => TRUE,
  37. '#process' => array('geofield_latlon_element_process'),
  38. '#element_validate' => array('geofield_latlon_element_validate'),
  39. '#theme' => array('geofield_latlon'),
  40. '#theme_wrappers' => array('fieldset'),
  41. ),
  42. 'geofield_bounds' => array(
  43. '#input' => TRUE,
  44. '#process' => array('geofield_bounds_element_process'),
  45. '#element_validate' => array('geofield_bounds_element_validate'),
  46. '#theme' => array('geofield_bounds'),
  47. '#theme_wrappers' => array('fieldset'),
  48. ),
  49. 'geofield_proximity' => array(
  50. '#input' => FALSE,
  51. '#tree' => TRUE,
  52. '#theme_wrappers' => array('geofield_proximity'),
  53. '#process' => array('geofield_proximity_element_process'),
  54. ),
  55. );
  56. }
  57. /**
  58. * Process function for geofield_latlon.
  59. */
  60. function geofield_latlon_element_process($element, &$form_values) {
  61. $element['#tree'] = TRUE;
  62. $element['#input'] = TRUE;
  63. $element['lat'] = array(
  64. '#type' => 'textfield',
  65. '#title' => t('Latitude'),
  66. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  67. '#default_value' => (!empty($element['#default_value']['lat'])) ? $element['#default_value']['lat'] : '',
  68. '#attributes' => array(
  69. 'class' => array('geofield-lat'),
  70. ),
  71. );
  72. $element['lon'] = array(
  73. '#type' => 'textfield',
  74. '#title' => t('Longitude'),
  75. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  76. '#default_value' => (!empty($element['#default_value']['lon'])) ? $element['#default_value']['lon'] : '',
  77. '#attributes' => array(
  78. 'class' => array('geofield-lon'),
  79. ),
  80. );
  81. unset($element['#value']);
  82. // Set this to false always to prevent notices.
  83. $element['#required'] = FALSE;
  84. if (!empty($element['#geolocation']) && $element['#geolocation'] == TRUE) {
  85. $element['#attached']['js'][] = drupal_get_path('module', 'geofield') . '/js/geolocation.js';
  86. $element['geocode'] = array(
  87. '#type' => 'button',
  88. '#value' => t('Find my location'),
  89. '#name' => 'geofield-html5-geocode-button',
  90. );
  91. $element['#attributes']['class'] = array('auto-geocode');
  92. }
  93. return $element;
  94. }
  95. /**
  96. * Element validation function for geofield_latlon.
  97. */
  98. function geofield_latlon_element_validate($element, &$form_values) {
  99. $components = array(
  100. 'lat' => array(
  101. 'title' => 'Latitude',
  102. 'range' => 90,
  103. ),
  104. 'lon' => array(
  105. 'title' => 'Longitude',
  106. 'range' => 180,
  107. ),
  108. );
  109. $allFilled = TRUE;
  110. $anyFilled = FALSE;
  111. foreach ($components as $key => $component) {
  112. if (!empty($element[$key]['#value'])) {
  113. if (!is_numeric($element[$key]['#value'])) {
  114. form_error($element[$key], t('@title: @component_title is not numeric.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  115. }
  116. elseif (abs($element[$key]['#value']) > $component['range']) {
  117. form_error($element[$key], t('@title: @component_title is out of bounds.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  118. }
  119. }
  120. if ($element[$key]['#value'] == '') {
  121. $allFilled = FALSE;
  122. }
  123. else {
  124. $anyFilled = TRUE;
  125. }
  126. }
  127. if ($anyFilled && !$allFilled) {
  128. foreach ($components as $key => $component) {
  129. if ($element[$key]['#value'] == '') {
  130. form_error($element[$key], t('@title: @component_title must be filled too.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  131. }
  132. }
  133. }
  134. }
  135. /**
  136. * Process function for geofield_bounds.
  137. */
  138. function geofield_bounds_element_process($element, &$form_values) {
  139. $element['#tree'] = TRUE;
  140. $element['top'] = array(
  141. '#type' => 'textfield',
  142. '#title' => t('Top'),
  143. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  144. '#default_value' => (!empty($element['#default_value']['top'])) ? $element['#default_value']['top'] : '',
  145. '#attributes' => array(
  146. 'class' => array('geofield-top'),
  147. ),
  148. );
  149. $element['right'] = array(
  150. '#type' => 'textfield',
  151. '#title' => t('Right'),
  152. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  153. '#default_value' => (!empty($element['#default_value']['right'])) ? $element['#default_value']['right'] : '',
  154. '#attributes' => array(
  155. 'class' => array('geofield-right'),
  156. ),
  157. );
  158. $element['bottom'] = array(
  159. '#type' => 'textfield',
  160. '#title' => t('Bottom'),
  161. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  162. '#default_value' => (!empty($element['#default_value']['bottom'])) ? $element['#default_value']['bottom'] : '',
  163. '#attributes' => array(
  164. 'class' => array('geofield-bottom'),
  165. ),
  166. );
  167. $element['left'] = array(
  168. '#type' => 'textfield',
  169. '#title' => t('Left'),
  170. '#required' => (!empty($element['#required'])) ? $element['#required'] : FALSE,
  171. '#default_value' => (!empty($element['#default_value']['left'])) ? $element['#default_value']['left'] : '',
  172. '#attributes' => array(
  173. 'class' => array('geofield-left'),
  174. ),
  175. );
  176. unset($element['#value']);
  177. // Set this to false always to prevent notices.
  178. $element['#required'] = FALSE;
  179. return $element;
  180. }
  181. /**
  182. * Element validation function for geofield_latlon.
  183. */
  184. function geofield_bounds_element_validate($element, &$form_values) {
  185. $components = array(
  186. 'top' => array(
  187. 'title' => 'Top',
  188. 'range' => 90,
  189. ),
  190. 'right' => array(
  191. 'title' => 'Right',
  192. 'range' => 180,
  193. ),
  194. 'bottom' => array(
  195. 'title' => 'Bottom',
  196. 'range' => 90,
  197. ),
  198. 'left' => array(
  199. 'title' => 'Left',
  200. 'range' => 180,
  201. ),
  202. );
  203. $allFilled = TRUE;
  204. $anyFilled = FALSE;
  205. foreach ($components as $key => $component) {
  206. if (!empty($element[$key]['#value'])) {
  207. if (!is_numeric($element[$key]['#value'])) {
  208. form_error($element[$key], t('@title: @component_title is not numeric.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  209. }
  210. elseif (abs($element[$key]['#value']) > $component['range']) {
  211. form_error($element[$key], t('@title: @component_title is out of bounds.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  212. }
  213. }
  214. if ($element[$key]['#value'] == '') {
  215. $allFilled = FALSE;
  216. }
  217. else {
  218. $anyFilled = TRUE;
  219. }
  220. }
  221. if ($anyFilled && !$allFilled) {
  222. foreach ($components as $key => $component) {
  223. if ($element[$key]['#value'] == '') {
  224. form_error($element[$key], t('@title: @component_title must be filled too.', array('@title' => $element['#title'], '@component_title' => $component['title'])));
  225. }
  226. }
  227. }
  228. }
  229. /**
  230. * Process function for the proximity form element
  231. */
  232. function geofield_proximity_element_process($element, &$form_state, $form) {
  233. $element['#attributes'] = array('class' => array('clearfix'));
  234. $element['#tree'] = TRUE;
  235. $element['#attached']['css'] = array(drupal_get_path('module', 'geofield') . '/css/proximity-element.css');
  236. //Create the textfield for distance
  237. $element['distance'] = array(
  238. '#type' => 'textfield',
  239. '#title' => t('Distance'),
  240. '#default_value' => !empty($element['#default_value']['distance']) ? $element['#default_value']['distance'] : '',
  241. '#title_display' => 'invisible',
  242. // Allow decimal numbers as distances
  243. '#element_validate' => array('element_validate_number'),
  244. );
  245. // If #geofield_range is TRUE, create second option for range.
  246. if (!empty($element['#geofield_range']) && $element['#geofield_range'] == TRUE) {
  247. $element['distance2'] = array(
  248. '#type' => 'textfield',
  249. '#title' => t('Distance End'),
  250. '#default_value' => !empty($element['#default_value']['distance2']) ? $element['#default_value']['distance2'] : '',
  251. '#title_display' => 'invisible',
  252. '#element_validate' => array('element_validate_number'),
  253. );
  254. }
  255. //Create dropdown for units
  256. $element['unit'] = array(
  257. '#type' => 'select',
  258. '#options' => geofield_radius_options(),
  259. '#title' => t('Unit'),
  260. '#default_value' => !empty($element['#default_value']['unit']) ? $element['#default_value']['unit'] : GEOFIELD_KILOMETERS,
  261. '#title_display' => 'invisible',
  262. );
  263. //Create textfield for geocoded input
  264. $element['origin'] = array(
  265. '#type' => (!empty($element['#origin_element'])) ? $element['#origin_element'] : 'textfield',
  266. '#title' => t('Origin'),
  267. '#prefix' => '<span class="geofield-proximity-origin-from">' . t('from') . '</span>',
  268. '#title_display' => 'invisible',
  269. '#required' => !empty($element['#required']) ? $element['#required'] : FALSE,
  270. '#default_value' => !empty($element['#default_value']['origin']) ? $element['#default_value']['origin'] : FALSE,
  271. );
  272. if (!empty($element['#origin_options'])) {
  273. $element['origin'] = array_merge($element['origin'], $element['#origin_options']);
  274. }
  275. if (isset($element['#element_validate'])) {
  276. array_push($element['#element_validate'], 'goefield_proximity_search_validate');
  277. }
  278. else {
  279. $element['#element_validate'] = array('geofield_proximity_search_validate');
  280. }
  281. return $element;
  282. }
  283. /**
  284. * Validate the geofield proximity search form item
  285. */
  286. function geofield_proximity_search_validate($element, &$form_state) {
  287. //If the origin is set, ensure the distance is set as well
  288. if (!empty($element['origin']['#value']) && trim($element['origin']['#value']) != '' && empty($element['distance']['#value']))
  289. form_set_error(implode('][', $element['#array_parents']) . '][distance', t('@title must be set when @origin is specified.', array('@title' => $element['distance']['#title'], '@origin' => $element['origin']['#title'])));
  290. }
  291. function geofield_theme($existing, $type, $theme, $path) {
  292. return array(
  293. 'geofield_latlon' => array(
  294. 'arguments' => array('element' => NULL),
  295. 'render element' => 'element',
  296. ),
  297. 'geofield_proximity' => array(
  298. 'render element' => 'element',
  299. ),
  300. );
  301. }
  302. /**
  303. * Theme wrapper for form item
  304. */
  305. function theme_geofield_proximity($vars) {
  306. $element = $vars['element'];
  307. $attributes = !empty($element['#wrapper_attributes']) ? $element['#wrapper_attributes'] : array('class' => array());
  308. $attributes['class'][] = 'geofield-proximity-field-wrapper';
  309. $attributes['class'][] = 'clearfix';
  310. $wrapper_attributes = array();
  311. $wrapper_attributes['class'][] = 'clearfix';
  312. if (isset($element['#children']))
  313. $element['#children'] = '<div id="' . $element['#id'] . '" ' . drupal_attributes($wrapper_attributes) . '>' . $element['#children'] . '</div>';
  314. $output = '<div ' . drupal_attributes($attributes) . '>' . theme('form_element', $element) . '</div>';
  315. return $output;
  316. }
  317. function theme_geofield_latlon($vars) {
  318. return drupal_render($vars['element']['lat']) . drupal_render($vars['element']['lon']) . drupal_render($vars['element']['geocode']);
  319. }
  320. /**
  321. * Returns options for radius of the Earth.
  322. */
  323. function geofield_radius_options() {
  324. return array(
  325. GEOFIELD_KILOMETERS => t('Kilometers'),
  326. GEOFIELD_METERS => t('Meters'),
  327. GEOFIELD_MILES => t('Miles'),
  328. GEOFIELD_YARDS => t('Yards'),
  329. GEOFIELD_FEET => t('Feet'),
  330. GEOFIELD_NAUTICAL_MILES => t('Nautical Miles'),
  331. );
  332. }