More cleanup and documentation updating.
[project/gmap.git] / gmap_plugin_style_gmap.inc
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * GMap style plugin.
7 */
8
9 /**
10 * Style plugin to render a map.
11 *
12 * @ingroup views_style_plugins
13 */
14 class gmap_plugin_style_gmap extends views_plugin_style {
15 /**
16 * Set default options
17 */
18 function option_definition() {
19 $options = parent::option_definition();
20
21 $options['macro'] = array(
22 'default' => '[gmap ]',
23 );
24
25 $options['datasource'] = array(
26 'default' => 'location',
27 );
28
29 $options['markers'] = array('default' => 'static');
30 $options['markertype'] = array('default' => 'drupal');
31
32 $options['latfield'] = array('default' => '');
33 $options['lonfield'] = array('default' => '');
34 $options['markerfield'] = array('default' => '');
35
36 $options['center_on_nodearg'] = array('default' => 0);
37 $options['center_on_nodearg_arg'] = array('default' => '');
38
39 $options['highlight_nodearg'] = array('default' => 0);
40 $options['highlight_nodearg_arg'] = array('default' => '');
41 $options['highlight_nodearg_color'] = array('default' => '#FF0000');
42
43 $options['tooltipenabled'] = array('default' => 0);
44 $options['tooltipfield'] = array('default' => '');
45
46 return $options;
47 }
48
49 function query() {
50 parent::query();
51
52 if ($this->options['datasource'] == 'location') {
53 $table = $this->view->query->ensure_table('location');
54 $this->view->query->add_field($table, 'latitude', 'gmap_lat');
55 $this->view->query->add_field($table, 'longitude', 'gmap_lon');
56 }
57
58 if ($this->options['markers'] == 'nodetype') {
59 $this->view->query->add_field('node', 'type', 'gmap_node_type');
60 }
61 else if ($this->options['markers'] == 'taxonomy') {
62 $this->view->query->add_field('gmap_taxonomy_node', 'marker', 'gmap_node_marker');
63 }
64 else if ($this->options['markers'] == 'userrole') {
65 $this->view->query->add_field('users_roles', 'rid', 'gmap_role_marker');
66 }
67
68 if (isset($this->row_plugin)) {
69 $this->row_plugin->query();
70 }
71 }
72
73 /**
74 * Render the display in this style.
75 */
76 function render() {
77
78 if (isset($this->view->live_preview) && $this->view->live_preview) {
79 return t('GMap views are not compatible with live preview.');
80 }
81
82 if (empty($this->row_plugin)) {
83 vpr('gmap_plugin_style_gmap: Missing row plugin');
84 return;
85 }
86
87 $defaults = gmap_defaults();
88 $lat_field = 'gmap_lat';
89 $lon_field = 'gmap_lon';
90
91 // Determine fieldname for latitude and longitude fields.
92 if ($this->options['datasource'] == 'fields') {
93 $lat_fied_obj = $this->view->display_handler->get_handler('field', $this->options['latfield']);
94 $lon_field_obj = $this->view->display_handler->get_handler('field', $this->options['lonfield']);
95 $lat_field = $lat_fied_obj->field_alias;
96 $lon_field = $lon_field_obj->field_alias;
97 }
98
99 $tooltip_field = '';
100 if ($this->options['tooltipenabled']) {
101 $tooltip_field_obj = $this->view->display_handler->get_handler('field', $this->options['tooltipfield']);
102 $tooltip_field = $tooltip_field_obj->field_alias;
103 }
104
105 // Determine fieldname for marker field.
106 if ($this->options['markers'] == 'field') {
107 $marker_field_obj = $this->view->display_handler->get_handler('field', $this->options['markerfield']);
108 $marker_field = $marker_field_obj->field_alias;
109 }
110
111 $markername = isset($this->options['markertype']) ? $this->options['markertype'] : 'drupal';
112
113 $markertypes = variable_get('gmap_node_markers', array());
114 if ($this->options['markers'] == 'nodetype') {
115 $markertypes = variable_get('gmap_node_markers', array());
116 }
117 else if ($this->options['markers'] == 'userrole') {
118 $markertypes = variable_get('gmap_role_markers', array(DRUPAL_AUTHENTICATED_RID => 'drupal'));
119 }
120
121 // Group the rows according to the grouping field, if specified.
122 $sets = $this->render_grouping($this->view->result, $this->options['grouping']);
123
124 // Render each group separately and concatenate. Plugins may override this
125 // method if they wish some other way of handling grouping.
126 $output = '';
127 foreach ($sets as $title => $records) {
128 $markers = array();
129 $offsets = array();
130 $center_lat = null;
131 $center_lon = null;
132 $center_nid = null;
133 $highlight_nid = null;
134
135 // We search nid argument used to center map
136 if ($this->options['center_on_nodearg'] && $nodehandler = $this->view->display_handler->get_handler('argument', $this->options['center_on_nodearg_arg'])) {
137 $center_nid = $nodehandler->get_value();
138 }
139 if ($this->options['highlight_nodearg'] && $nodehandler = $this->view->display_handler->get_handler('argument', $this->options['highlight_nodearg_arg'])) {
140 $highlight_nid = $nodehandler->get_value();
141 }
142
143 foreach ($records as $row_index => $row) {
144 $this->view->row_index = $row_index;
145 $lat = (float)$row->{$lat_field};
146 $lon = (float)$row->{$lon_field};
147
148 // $row->nid is present in node views, views without node as the base table must include the nid field,
149 // which will be in $row->node_nid if present.
150 // If nid for a row is required use $row_nid.
151 $row_nid = isset($row->nid) ? $row->nid : (isset($row->node_nid) ? $row->node_nid : NULL);
152
153 // If this row will be used as center map then we keep its lon/lat
154 // If there are multiple points on a single node take the first match
155 if (!empty($center_nid) && !empty($row_nid) && $center_nid == $row_nid && ($center_lon === NULL || $center_lat === NULL)) {
156 $center_lon = $lon;
157 $center_lat = $lat;
158 }
159
160 if (!empty($lat) && !empty($lon)) {
161 if ($this->options['markers'] == 'nodetype') {
162 if (isset($markertypes[$row->gmap_node_type])) {
163 $markername = $markertypes[$row->gmap_node_type];
164 }
165 }
166 else if ($this->options['markers'] == 'taxonomy') {
167 if (!empty($row->gmap_node_marker)) {
168 $markername = $row->gmap_node_marker;
169 }
170 }
171 else if ($this->options['markers'] == 'userrole') {
172 if (!empty($row->gmap_role_marker)) {
173 $markername = $markertypes[DRUPAL_AUTHENTICATED_RID];
174 if (isset($markertypes[$row->gmap_role_marker])) {
175 $markername = $markertypes[$row->gmap_role_marker];
176 }
177 }
178 }
179 else if ($this->options['markers'] == 'field') {
180 if (!empty($row->{$marker_field})) {
181 $markername = $row->{$marker_field};
182 }
183 }
184 if (!isset($offsets[$markername])) {
185 $offsets[$markername] = 0;
186 }
187
188 $tooltip = "";
189 if ($this->options['tooltipenabled'] && $row->$tooltip_field) {
190 $tooltip = $row->$tooltip_field;
191 }
192
193 $marker = array(
194 'latitude' => $lat,
195 'longitude' => $lon,
196 'markername' => $markername,
197 'offset' => $offsets[$markername],
198 'opts' => array(
199 'title' => $tooltip,
200 'highlight' => (!empty($highlight_nid) && !empty($row_nid) && $highlight_nid == $row_nid) ? 1 : 0,
201 'highlightcolor' => $this->options['highlight_nodearg_color'],
202 ),
203 );
204 // Marker mode: popup.
205 if ($defaults['markermode'] == 1) {
206 $marker['text'] = $this->row_plugin->render($row);
207 }
208 // Marker mode: link.
209 else if ($defaults['markermode'] == 2) {
210 $marker['link'] = url('node/' . $row_nid);
211 }
212 $markers[] = $marker;
213
214 $offsets[$markername]++;
215 }
216 }
217 if (!empty($markers)) { // Don't draw empty maps.
218 $map = gmap_parse_macro($this->options['macro']);
219
220 // If center lon/lat are not empty they are used to center map
221 if (!empty($center_lon) && !empty($center_lat)) {
222 $map['longitude'] = $center_lon;
223 $map['latitude'] = $center_lat;
224 }
225
226 $map['markers'] = $markers;
227 $output .= theme($this->theme_functions(), $this->view, $this->options, $map, $title);
228 }
229 }
230
231 unset($this->view->row_index);
232 return $output;
233 }
234
235 /**
236 * Render the given style.
237 */
238 function options_form(&$form, &$form_state) {
239 parent::options_form($form, $form_state);
240
241 $field_options = array();
242 $fields = $this->display->handler->get_handlers('field');
243 foreach ($fields as $id => $handler) {
244 $field_options[$id] = $handler->ui_name(FALSE);
245 }
246
247 $argument_options = array();
248 $arguments = $this->display->handler->get_handlers('argument');
249 foreach ($arguments as $id => $handler) {
250 $argument_options[$id] = $handler->ui_name(FALSE);
251 }
252
253 $form['macro'] = array(
254 '#type' => 'textarea',
255 '#title' => t('Macro'),
256 '#rows' => 3,
257 '#default_value' => $this->options['macro'],
258 );
259
260 $form['datasource'] = array(
261 '#type' => 'select',
262 '#title' => t('Data Source'),
263 '#options' => array(
264 'location' => t('Location.module'),
265 'fields' => t('Choose latitude and longitude fields'),
266 //'geocode' => t('Just-in-time geocoding on field named "address"'),
267 ),
268 '#default_value' => $this->options['datasource'],
269 '#multiple' => FALSE,
270 );
271
272 $form['latfield'] = array(
273 '#title' => t('Latitude field'),
274 '#description' => t('Format must be degrees decimal.'),
275 '#type' => 'select',
276 '#options' => $field_options,
277 '#default_value' => $this->options['latfield'],
278 '#process' => array('views_process_dependency'),
279 '#dependency' => array('edit-style-options-datasource' => array('fields')),
280 );
281
282 $form['lonfield'] = array(
283 '#title' => t('Longitude field'),
284 '#description' => t('Format must be degrees decimal.'),
285 '#type' => 'select',
286 '#options' => $field_options,
287 '#default_value' => $this->options['lonfield'],
288 '#process' => array('views_process_dependency'),
289 '#dependency' => array('edit-style-options-datasource' => array('fields')),
290 );
291
292 $form['markers'] = array(
293 '#type' => 'select',
294 '#title' => t('Marker handling'),
295 // @@@ Detect view type automatically?
296 '#options' => array(
297 'nodetype' => t('By content type (for node views)'),
298 'taxonomy' => t('By term (for node views)'),
299 'userrole' => t('By user role (for user views)'),
300 'field' => t('Use marker field'),
301 'static' => t('Use single marker type'),
302 ),
303 '#default_value' => $this->options['markers'],
304 );
305
306 $form['markerfield'] = array(
307 '#type' => 'select',
308 '#title' => t('Marker field'),
309 '#description' => t('You can use a views field to set the <em>markername</em> property of the markers.'),
310 '#options' => $field_options,
311 '#default_value' => $this->options['markerfield'],
312 '#process' => array('views_process_dependency'),
313 '#dependency' => array('edit-style-options-markers' => array('field')),
314 );
315
316 // Hide the taxonomy handling if gmap_taxonomy.module isn't installed.
317 if (!module_exists('gmap_taxonomy')) {
318 unset($form['markers']['#options']['taxonomy']);
319 }
320
321 $form['markertype'] = array(
322 '#type' => 'gmap_markerchooser',
323 '#title' => t('Marker / fallback marker to use'),
324 '#default_value' => $this->options['markertype'],
325 );
326
327 $form['center_on_nodearg'] = array(
328 '#type' => 'checkbox',
329 '#title' => t('Center on node argument'),
330 '#default_value' => $this->options['center_on_nodearg'],
331 '#description' => ($this->view->base_table == 'node') ? t('Note: The view must contain an argument whose value is a node ID.') : t('Note: The view must contain an argument whose value is a node ID.') . '<br />' . t("The view must contain 'Node: nid' as one of its fields because the view type is not 'Node'."),
332 );
333 $form['center_on_nodearg_arg'] = array(
334 '#title' => t('Argument'),
335 '#description' => empty($argument_options) ? t("The value of the selected argument must be a number that matches a node ID. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("The selected argument must be a number that matches a node ID. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
336 '#type' => 'select',
337 '#options' => $argument_options,
338 '#default_value' => $this->options['center_on_nodearg_arg'],
339 '#process' => array('views_process_dependency'),
340 '#dependency' => array('edit-style-options-center-on-nodearg' => array(TRUE)),
341 );
342
343 $form['highlight_nodearg'] = array(
344 '#type' => 'checkbox',
345 '#title' => t('Highlight marker for node argument'),
346 '#default_value' => $this->options['highlight_nodearg'],
347 '#description' => ($this->view->base_table == 'node') ? t('Note: The view must contain an argument whose value is a node ID.') : t('Note: The view must contain an argument whose value is a node ID.') . '<br />' . t("The view must contain 'Node: nid' as one of its fields because the view type is not 'Node'."),
348 );
349 $form['highlight_nodearg_arg'] = array(
350 '#title' => t('Argument'),
351 '#description' => empty($argument_options) ? t("The value of the selected argument must be a number that matches a node ID. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("The value of the selected argument must be a number that matches a node ID. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
352 '#type' => 'select',
353 '#options' => $argument_options,
354 '#default_value' => $this->options['highlight_nodearg_arg'],
355 '#process' => array('views_process_dependency'),
356 '#dependency' => array('edit-style-options-highlight-nodearg' => array(TRUE)),
357 );
358 $form['highlight_nodearg_color'] = array(
359 '#title' => t('Highlight color'),
360 '#description' => t("A 6 digit hex color value to use for the highlight. Include preceding hash. Example #FF0000"),
361 '#type' => 'textfield',
362 '#size' => 7,
363 '#maxlength' => 7,
364 '#default_value' => $this->options['highlight_nodearg_color'],
365 '#process' => array('views_process_dependency'),
366 '#dependency' => array('edit-style-options-highlight-nodearg' => array(TRUE)),
367 );
368
369 $form['tooltipenabled'] = array(
370 '#type' => 'checkbox',
371 '#title' => t('Display a tooltip when hovering over markers'),
372 '#default_value' => $this->options['tooltipenabled'],
373 );
374 $form['tooltipfield'] = array(
375 '#title' => t('Tooltip field'),
376 '#description' => empty($field_options) ? t("The field's format must be text. You must be using the fields row style and have added fields to the view to use this option.") : t("The field's format must be text."),
377 '#type' => 'select',
378 '#options' => $field_options,
379 '#default_value' => $this->options['tooltipfield'],
380 '#process' => array('views_process_dependency'),
381 '#dependency' => array('edit-style-options-tooltipenabled' => array(TRUE)),
382 );
383 }
384
385 /**
386 * Validate the options form.
387 */
388 function options_validate(&$form, &$form_state) {
389 // Check if highlight color is a valid hex color
390 if (!preg_match('/^#[a-f0-9]{6}$/i', $form_state['values']['style_options']['highlight_nodearg_color'])) {
391 form_error($form['highlight_nodearg_color'], t('Highlight colour must be a valid hex code in the form #FF0000.'));
392 }
393 }
394 }