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

Contents of /contributions/modules/gmapfield/gmapfield.module

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


Revision 1.1 - (show annotations) (download) (as text)
Thu Nov 6 06:07:19 2008 UTC (12 months, 2 weeks ago) by binarybill
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
File MIME type: text/x-php
Initial cmooit of GMap Field module.
1 <?php
2 // $Id$
3 /**
4 * @file
5 * Main module file for the GMap Field Module
6 *
7 * @notes
8 *
9 * @todo
10 */
11
12
13 /**
14 * Implementation of hook_theme().
15 */
16 function gmapfield_theme() {
17 return array(
18 'gmapfield_formatter_default' => array(
19 'arguments' => array('element' => NULL),
20 ),
21 'gmapfield_text' => array(
22 'arguments' => array('element' => NULL),
23 ),
24 'gmapfield_marker' => array(
25 'arguments' => array('element' => NULL),
26 ),
27 'gmapfield_markerwindow' => array(
28 'arguments' => array('location' => NULL, 'node' => NULL),
29 ),
30 );
31 }
32
33
34 /**
35 * Implementation of hook_help().
36 */
37 function gmapfield_help($page, $arg) {
38 switch ($page) {
39 case 'admin/help#gmapfield':
40 return t('Create CCK field for GMaps. Input a GMap Macro and then it will be centered at the node\'s coordinates via Location or other CCK fields.');
41 break;
42 }
43 }
44
45
46 /**
47 * Implementation of hook_field_info().
48 */
49 function gmapfield_field_info() {
50 return array(
51 'gmapfield' => array(
52 'label' => t('GMap Field'),
53 'description' => t('Input a GMap Macro and then it will be centered at the node coordinates via Location or other CCK fields.'),
54 'callbacks' => array(
55 'tables' => CONTENT_CALLBACK_DEFAULT,
56 'arguments' => CONTENT_CALLBACK_DEFAULT,
57 ),
58 ),
59 );
60 }
61
62
63 /**
64 * Implementation of hook_field_settings().
65 *
66 * Handle the settings for a field.
67 *
68 * @param $op
69 * The operation to be performed. Possible values:
70 * - "form": Display the field settings form.
71 * - "validate": Check the field settings form for errors.
72 * Note : That operation is currently still supported, but will be
73 * deprecated at some point.
74 * It is recommended to use Forms API validation instead.
75 * - "save": Declare which fields to save back to the database.
76 * - "database columns": Declare the columns that content.module should create
77 * and manage on behalf of the field. If the field module wishes to handle
78 * its own database storage, this should be omitted.
79 * - "filters": Declare the Views filters available for the field.
80 * (this is used in CCK's default Views tables definition)
81 * They always apply to the first column listed in the "database columns"
82 * array.
83 * @param $field
84 * The field on which the operation is to be performed.
85 * @return
86 * This varies depending on the operation.
87 * - "form": an array of form elements to add to
88 * the settings page.
89 * - "validate": no return value. Use form_set_error().
90 * - "save": an array of names of form elements to
91 * be saved in the database.
92 * - "database columns": an array keyed by column name, with arrays of column
93 * information as values. This column information must include "type", the
94 * MySQL data type of the column, and may also include a "sortable" parameter
95 * to indicate to views.module that the column contains ordered information.
96 * TODO: Details of other information that can be passed to the database layer can
97 * be found in the API for the Schema API.
98 * - "filters": an array of 'filters' definitions as expected by views.module
99 * (see Views Documentation).
100 * When providing several filters, it is recommended to use the 'name'
101 * attribute in order to let the user distinguish between them. If no 'name'
102 * is specified for a filter, the key of the filter will be used instead.
103 */
104 function gmapfield_field_settings($op, $field) {
105 switch ($op) {
106 // Case Form
107 case 'form':
108 // Marker fieldset
109 $form['fieldset_markers'] = array(
110 '#type' => 'fieldset',
111 '#title' => t('Marker settings'),
112 '#collapsible' => TRUE,
113 '#collapsed' => FALSE,
114 );
115 // Add Markers
116 $form['fieldset_markers']['add_markers'] = array(
117 '#type' => 'checkbox',
118 '#title' => t('Add Marker(s)'),
119 '#default_value' => isset($field['add_markers']) ? $field['add_markers'] : TRUE,
120 '#description' => t('If checked, this field will add the markers from the selected datasource and center the map accordingly.'),
121 '#required' => TRUE,
122 );
123 // Choose Markers
124 $form['fieldset_markers']['choose_marker'] = array(
125 '#type' => 'checkbox',
126 '#title' => t('Choose Marker Type'),
127 '#default_value' => isset($field['choose_marker']) ? $field['choose_marker'] : TRUE,
128 '#description' => t('If checked and Add Markers is checked, the user can choose which marker to use for the location data that is added to the GMap.'),
129 '#required' => TRUE,
130 );
131 // Default Markers
132 $markers = gmap_get_marker_titles();
133 $form['fieldset_markers']['default_marker'] = array(
134 '#type' => 'select',
135 '#title' => t('Default Marker Type'),
136 '#default_value' => isset($field['default_marker']) ? $field['default_marker'] : 'drupal',
137 '#description' => t('If Add Markers is checked, this will be the marker used on the GMap.'),
138 '#options' => $markers,
139 '#required' => TRUE,
140 );
141
142 // Datasource fieldset
143 $form['fieldset_datasource'] = array(
144 '#type' => 'fieldset',
145 '#title' => t('Data Source settings'),
146 '#collapsible' => TRUE,
147 '#collapsed' => FALSE,
148 );
149 // Chose Coordinate Data Source
150 $coordinate_datasource_options = array(
151 'location' => t('Use Location(s)'),
152 'cck_fields' => t('Choose CCK Fields for Lat and Long (*not currently supported*)'),
153 );
154 $form['fieldset_datasource']['coordinate_datasource'] = array(
155 '#type' => 'radios',
156 '#title' => t('Coordinate Data Source'),
157 '#default_value' => ($field['coordinate_datasource']) ? $field['coordinate_datasource'] : 'location',
158 '#options' => $coordinate_datasource_options,
159 '#description' => t('Choose the data source for map center and marker(s).'),
160 '#required' => TRUE,
161 );
162 // Choose Lat Coordinat Field
163 $lat_field = array(
164 'none' => t('None'),
165 );
166 $form['fieldset_datasource']['lat_field'] = array(
167 '#type' => 'select',
168 '#title' => t('Latitude Field'),
169 '#default_value' => ($field['lat_field']) ? $field['lat_field'] : 'none',
170 '#options' => $lat_field,
171 '#description' => t('CCK Field to use for Latitiude date.'),
172 );
173 // Choose Long Coordinat Field
174 $lon_field = array(
175 'none' => t('None'),
176 );
177 $form['fieldset_datasource']['lon_field'] = array(
178 '#type' => 'select',
179 '#title' => t('Longitude Field'),
180 '#default_value' => ($field['lon_field']) ? $field['lon_field'] : 'none',
181 '#options' => $lon_field,
182 '#description' => t('CCK Field to use for Longitude date.'),
183 );
184
185 return $form;
186
187 // Case Validate
188 case 'validate':
189 break;
190
191 // Case Save
192 case 'save':
193 return array('add_markers', 'choose_marker', 'default_marker', 'coordinate_datasource', 'lat_field', 'lon_field');
194
195 // Case Database Columns
196 case 'database columns':
197 $columns = array(
198 'gmap_macro' => array(
199 'type' => 'text',
200 'size' => 'medium',
201 'not null' => FALSE,
202 'sortable' => TRUE,
203 ),
204 'marker_type' => array(
205 'type' => 'varchar',
206 'length' => 255,
207 'not null' => FALSE,
208 'sortable' => TRUE,
209 ),
210 );
211 return $columns;
212
213 // Case Views Data
214 case 'views data':
215 module_load_include('inc', 'gmapfield', 'views/gmapfield.views');
216 return link_views_content_field_data($field);
217 }
218 }
219
220
221 /**
222 * Implementation of hook_content_is_empty().
223 *
224 * This function tells the content module whether or not to consider
225 * the $item to be empty. This is used by the content module
226 * to remove empty, non-required values before saving them.
227 */
228 function gmapfield_content_is_empty($item, $field) {
229 if (empty($item['gmap_macro'])) {
230 return TRUE;
231 }
232 return FALSE;
233 }
234
235
236 /**
237 * Implementation of hook_field().
238 *
239 * Define the behavior of a field type.
240 *
241 * @param $op
242 * What kind of action is being performed. Possible values:
243 * - "load": The node is about to be loaded from the database. This hook
244 * should be used to load the field.
245 * - "validate": The user has just finished editing the node and is
246 * trying to preview or submit it. This hook can be used to check or
247 * even modify the node. Errors should be set with form_set_error().
248 * - "presave": The user has just finished editing the node and the node has
249 * passed validation. This hook can be used to modify the node.
250 * - "insert": The node is being created (inserted in the database).
251 * - "update": The node is being updated.
252 * - "delete": The node is being deleted.
253 * @param &$node
254 * The node the action is being performed on. This argument is passed by
255 * reference for performance only; do not modify it.
256 * @param $field
257 * The field the action is being performed on.
258 * @param &$node_field
259 * The contents of the field in this node. Changes to this variable will
260 * be saved back to the node object.
261 * @return
262 * This varies depending on the operation.
263 * - The "load" operation should return an object containing extra values
264 * to be merged into the node object.
265 * - The "insert", "update", "delete", "validate", and "presave" operations
266 * have no return value.
267 *
268 * In most cases, only "validate" operations is relevant ; the rest
269 * have default implementations in content_field() that usually suffice.
270 */
271 function gmapfield_field($op, &$node, $field, &$items, $teaser, $page) {
272 switch ($op) {
273 case 'load':
274 foreach ($items as $delta => $item) {
275 _gmapfield_load($items[$delta], $field, $delta, $node);
276 }
277 break;
278
279 case 'validate':
280 foreach($items as $delta => $value) {
281 _gmapfield_validate($items[$delta], $delta, $field, $node);
282 }
283 break;
284
285 case 'presave':
286 foreach($items as $delta => $value) {
287 _gmapfield_presave($items[$delta], $delta, $field, $node);
288 }
289 break;
290
291 case 'sanitize':
292 foreach ($items as $delta => $value) {
293 _gmapfield_sanitize($items[$delta], $delta, $field, $node);
294 }
295 break;
296 }
297 }
298
299
300 /**
301 * Implementation of hook_field_formatter_info().
302 *
303 * The default behavior of formatters is that they will create
304 * a theme for a single field value.
305 *
306 * Setting 'multiple values' to CONTENT_HANDLE_FIELD will create
307 * a formatter that will receive all the values of a field so you
308 * can, for instance, plot all the values on a map or in a graph.
309 *
310 * The 'view' operation (handled by the Content module) constructs the
311 * $node in a way that you can use drupal_render() to display the
312 * formatted output for an individual field.
313 *
314 * i.e. print drupal_render($node->field_foo);
315 *
316 * The code now supports both single value formatters, which theme an
317 * individual item value as has been done in previous version of CCK,
318 * and multiple value formatters, which theme all values for the field
319 * in a single theme. The multiple value formatters could be used, for
320 * instance, to plot field values on a single map or display them
321 * in a graph. Single value formatters are the default, multiple value
322 * formatters can be designated as such in formatter_info().
323 *
324 * The node array will look like:
325 *
326 * 'Single value' formatter:
327 * $node->content['field_foo'] = array(
328 * '#type' => 'content_field',
329 * '#title' => 'label'
330 * '#field_name' => 'field_name',
331 * '#node' => $node,
332 * 'items' =>
333 * 0 => array(
334 * '#theme' => $theme,
335 * '#field_name' => 'field_name',
336 * '#type_name' => $node->type,
337 * '#formatter' => $formatter_name,
338 * '#item' => $items[0],
339 * ),
340 * 1 => array(
341 * '#theme' => $theme,
342 * '#field_name' => 'field_name',
343 * '#type_name' => $node->type,
344 * '#formatter' => $formatter_name,
345 * '#item' => $items[1],
346 * ),
347 * ),
348 * );
349 * 'Multiple value' formatter:
350 * $node->content['field_foo'] = array(
351 * '#type' => 'content_field',
352 * '#title' => 'label'
353 * '#field_name' => 'field_name',
354 * '#node' => $node,
355 * 'items' => array(
356 * '#theme' => $theme,
357 * '#field_name' => 'field_name',
358 * '#type_name' => $node->type,
359 * '#formatter' => $formatter_name,
360 * 0 => array(
361 * '#item' => $items[0],
362 * ),
363 * 1 => array(
364 * '#item' => $items[1],
365 * ),
366 * ),
367 * );
368 */
369 function gmapfield_field_formatter_info() {
370 return array(
371 'default' => array(
372 'label' => t('Default GMap'),
373 'field types' => array('gmapfield'),
374 'multiple values' => CONTENT_HANDLE_CORE,
375 ),
376 );
377 }
378
379
380 /**
381 * Theme function for 'default' gmap field.
382 */
383 function theme_gmapfield_formatter_default($element) {
384 // Get field data
385 $field_name = $element['#field_name'];
386 $field = content_fields($field_name);
387 $marker_type = ($element['#item']['marker_type']) ? $element['#item']['marker_type'] : 'drupal';
388 // Check for macro
389 if ($macro = $element['#item']['gmap_macro']) {
390 // Get location data
391 $location_data = _gmapfield_get_markers($element['#node'], $marker_type, $field);
392 // Get macro array
393 $macro_array = gmap_parse_macro($macro);
394 // Check for location data
395 if ($location_data) {
396 $macro_array = array_merge($macro_array, $location_data);
397 }
398 // Create a themeable array
399 $gmap_array = array('#settings' => $macro_array);
400 return theme('gmap', $gmap_array);
401 } else {
402 return '';
403 }
404 }
405
406
407
408 /**
409 * FAPI theme for an individual gmapfield_text element.
410 *
411 * The textfield is already rendered by the textfield
412 * theme and the HTML output lives in $element['#children'].
413 * Override this theme to make custom changes to the output.
414 *
415 * $element['#field_name'] contains the field name
416 * $element['#delta] is the position of this element in the group
417 */
418 function theme_gmapfield_text($element) {
419 // Field set
420 $fieldset = array(
421 '#type' => 'fieldset',
422 '#title' => $element['#title'],
423 '#description' => $element['#description'],
424 '#collapsible' => TRUE,
425 '#collapsed' => FALSE,
426 '#children' => $element['#children'],
427 );
428 //return $element['#children'];
429 return theme('fieldset', $fieldset);
430 }
431
432
433 /**
434 * Impelemtation of theme hook
435 */
436 function theme_gmapfield_markerwindow($location = NULL, $node = NULL) {
437 return theme('location', $location);
438 }
439
440
441
442 /**
443 * Implementation of hook_widget_info().
444 *
445 * Here we indicate that the content module will handle
446 * the default value and multiple values for these widgets.
447 *
448 * Callbacks can be omitted if default handing is used.
449 * They're included here just so this module can be used
450 * as an example for custom modules that might do things
451 * differently.
452 *
453 * IMPORTANT! - field and widget names will be truncated to 32 characters in
454 * the database and in internal arrays, like content_fields().
455 */
456 function gmapfield_widget_info() {
457 return array(
458 'gmapfield_text' => array(
459 'label' => t('GMap Macro Text Area'),
460 'field types' => array('gmapfield'),
461 'multiple values' => CONTENT_HANDLE_CORE,
462 'callbacks' => array(
463 'default value' => CONTENT_CALLBACK_DEFAULT,
464 ),
465 ),
466 );
467 }
468
469
470 /**
471 * Implementation of FAPI hook_elements().
472 *
473 * Any FAPI callbacks needed for individual widgets can be declared here,
474 * and the element will be passed to those callbacks for processing.
475 *
476 * Drupal will automatically theme the element using a theme with
477 * the same name as the hook_elements key.
478 *
479 * Autocomplete_path is not used by text_widget but other widgets can use it
480 * (see nodereference and userreference).
481 */
482 function gmapfield_elements() {
483 return array(
484 'gmapfield_text' => array(
485 '#input' => TRUE,
486 '#columns' => array('gmap_macro', 'marker_type'),
487 '#delta' => 0,
488 '#process' => array('gmapfield_widget_process'),
489 ),
490 );
491 }
492
493
494 /**
495 * Implementation of hook_widget().
496 *
497 * Attach a single form element to the form. It will be built out and
498 * validated in the callback(s) listed in hook_elements. We build it
499 * out in the callbacks rather than here in hook_widget so it can be
500 * plugged into any module that can provide it with valid
501 * $field information.
502 *
503 * Content module will set the weight, field name and delta values
504 * for each form element. This is a change from earlier CCK versions
505 * where the widget managed its own multiple values.
506 *
507 * If there are multiple values for this field, the content module will
508 * call this function as many times as needed.
509 *
510 * @param $form
511 * the entire form array, $form['#node'] holds node information
512 * @param $form_state
513 * the form_state, $form_state['values'][$field['field_name']]
514 * holds the field's form values.
515 * @param $field
516 * the field array
517 * @param $items
518 * array of default values for this field
519 * @param $delta
520 * the order of this item in the array of subelements (0, 1, 2, etc)
521 *
522 * @return
523 * the form item for a single element for this field
524 */
525 function gmapfield_widget(&$form, &$form_state, $field, $items, $delta = 0) {
526 $element = array();
527 switch ($field['widget']['type']) {
528 case 'gmapfield_text':
529 $element = array(
530 '#type' => $field['widget']['type'],
531 '#default_value' => (($items[$delta]) ? $items[$delta] : $field['gmap_macro_default']),
532 '#title' => $field['widget']['label'],
533 '#weight' => $field['widget']['weight'],
534 '#description' => $field['widget']['description'],
535 '#required' => $field['required'],
536 '#field' => $field,
537 );
538 break;
539 case 'gmapfield_marker':
540 $element = array(
541 '#type' => $field['widget']['type'],
542 '#default_value' => (($items[$delta]) ? $items[$delta] : $field['default_marker']),
543 '#title' => $field['widget']['label'],
544 '#weight' => $field['widget']['weight'],
545 '#description' => $field['widget']['description'],
546 '#required' => $field['required'],
547 '#field' => $field,
548 );
549 break;
550 }
551 return $element;
552 }
553
554
555 /**
556 * Process an individual element.
557 *
558 * Build the form element. When creating a form using FAPI #process,
559 * note that $element['#value'] is already set.
560 *
561 * The $fields array is in $form['#field_info'][$element['#field_name']].
562 */
563 function gmapfield_widget_process($element, $edit, $form_state, $form) {
564 // Get variables
565 $field_name = $element['#field_name'];
566 $field = $form['#field_info'][$field_name];
567 $delta = $element['#delta'];
568
569 // Macro
570 $description = t('Enter a GMap Macro.');
571 if (module_exists('gmap_macro_builder') && user_access('create macro')) {
572 $macro_url = url('map/macro');
573 $description .= ' ' . t('<a href="!macro_url" title="Build a Macro">Build a Macro</a>', array('!macro_url' => $macro_url));
574 }
575 $macro_value = ($element['#value']['gmap_macro']) ? $element['#value']['gmap_macro'] : '[gmap ]';
576 $element['gmap_macro'] = array(
577 '#type' => 'textarea',
578 '#default_value' => $macro_value,
579 '#title' => t('GMap Macro'),
580 '#description' => $description,
581 '#required' => $element['#required'],
582 '#field_name' => $element['#field_name'],
583 '#type_name' => $element['#type_name'],
584 '#delta' => $element['#delta'],
585 '#columns' => $element['#columns'],
586 );
587
588 // Marker
589 // Check if can add markers
590 if ($field['add_markers']) {
591 $markers = gmap_get_marker_titles();
592 // Check if can change marker
593 $disabled = TRUE;
594 $title = t('Choose Marker');
595 $description = t('This will be the marker to be used for the locations of this node.');
596 if ($field['choose_marker']) {
597 $disabled = FALSE;
598 $title = t('Marker');
599 }
600
601 $marker_value = isset($element['#value']['marker_type']) ? $element['#value']['marker_type'] : $field['default_marker'];
602 $element['marker_type'] = array(
603 '#type' => 'select',
604 '#default_value' => $marker_value,
605 '#options' => $markers,
606 '#disabled' => $disabled,
607 '#title' => $title,
608 '#description' => $description,
609 '#required' => $element['#required'],
610 '#field_name' => $element['#field_name'],
611 '#type_name' => $element['#type_name'],
612 '#delta' => $element['#delta'],
613 '#columns' => $element['#columns'],
614 );
615 }
616
617 return $element;
618 }
619
620
621 /*
622 * Function to validate GMap macro strings
623 *
624 * @params $macro
625 * String to validate
626 * @return
627 * Boolean whether macro is valid
628 */
629 function _gmapfield_validate_macro($macro = '') {
630 if (is_string($macro)) {
631 return true;
632 } else {
633 return false;
634 }
635 }
636
637
638 /*
639 * Function to do field load operations
640 *
641 * @params $item
642 * Item that is loading
643 * @params $field
644 * Field data
645 * @params $delta
646 * Which item is loading
647 * @params $node
648 * Node object
649 */
650 function _gmapfield_load(&$item, $field, $delta = 0, $node = NULL) {
651 // Do nothing
652 return true;
653 }
654
655
656 /*
657 * Function to do field validate operations
658 *
659 * @params $item
660 * Item that is loading
661 * @params $delta
662 * Which item is loading
663 * @params $field
664 * Field data
665 * @params $node
666 * Node object
667 */
668 function _gmapfield_validate(&$item, $delta, $field, $node) {
669 // Check for valid GMap Macro
670 if (!_gmapfield_validate_macro($item['gmap_macro'])) {
671 form_set_error($field['field_name'] .']['. $delta. '][title', t('Please enter a valid GMap macro.'));
672 }
673 return true;
674 }
675
676
677 /*
678 * Function to do field load operations
679 *
680 * @params $item
681 * Item that is loading
682 * @params $delta
683 * Which item is loading
684 * @params $field
685 * Field data
686 * @params $node
687 * Node object
688 */
689 function _gmapfield_presave(&$item, $delta, $field, $node) {
690 //Do nothing
691 return true;
692 }
693
694
695 /*
696 * Function to do field load operations
697 *
698 * @params $item
699 * Item that is loading
700 * @params $delta
701 * Which item is loading
702 * @params $field
703 * Field data
704 * @params $node
705 * Node object
706 */
707 function _gmapfield_sanitize(&$item, $delta, $field, $node) {
708 // Check Plain Macro
709 $item['gmap_macro'] = check_plain($item['gmap_macro']);
710 return true;
711 }
712
713
714 /*
715 * Function to get markers
716 *
717 * @params $node
718 * Node object loaded so far
719 * @params $field
720 * Field data
721 * @return
722 * Associative array to merge into a gmap array
723 */
724 function _gmapfield_get_markers($node, $marker, $field) {
725 $loc_data = array();
726
727 // Check if the CCK field wants markers
728 if (empty($field['add_markers'])) {
729 return $loc_data;
730 }
731
732 // Check for datasource
733 if ($field['coordinate_datasource'] == 'location') {
734 // Check Markers
735 $available_markers = gmap_get_marker_titles();
736 if (!array_key_exists($marker, $available_markers)) {
737 $marker = 'drupal';
738 }
739
740 // Check for locations
741 if ($locations = $node->locations) {
742 $count = 0;
743 foreach ($locations as $l) {
744 // Check for lat and long first
745 if ($l['longitude'] != 0 && $l['latitude'] != 0) {
746 // Add markers
747 $loc_data['markers'][] = array(
748 'text' => theme('gmapfield_markerwindow', $l, $node),
749 'longitude' => $l['longitude'],
750 'latitude' => $l['latitude'],
751 'markername' => $marker,
752 'offset' => $count,
753 );
754 // Set center if primary or first location
755 if ($l['is_primary'] || $count == 0) {
756 $loc_data['longitude'] = $l['longitude'];
757 $loc_data['latitude'] = $l['latitude'];
758 }
759 $count += 1;
760 }
761 }
762 }
763 } elseif ($field['coordinate_datasource'] == 'cck_fields') {
764 $loc_data = array();
765 }
766 return $loc_data;
767 }

  ViewVC Help
Powered by ViewVC 1.1.2