FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,21 @@
<?php
/**
* Argument handler to accept a country code or name.
*/
class location_handler_argument_location_country extends views_handler_argument {
/**
* Override the behavior of title(). Get the country name.
*/
function title() {
return location_country_name($this->argument);
}
function summary_name($data) {
$value = $data->{$this->name_alias};
if (empty($value)) {
return t('Unknown');
}
return location_country_name($value);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Argument handler to accept a province code or name.
*/
class location_handler_argument_location_province extends views_handler_argument {
/**
* Override the behavior of title(). Get the province name.
*/
function title() {
if (isset($this->view->argument['country'])) {
$country = $this->view->argument['country']->argument;
}
else {
$country = variable_get('location_default_country', 'us');
}
return location_province_name($country, $this->argument);
}
function summary_name($data) {
$value = $data->{$this->name_alias};
if (empty($value)) {
return t('Unknown');
}
if (isset($this->view->argument['country'])) {
$country = $this->view->argument['country']->argument;
}
else {
$country = variable_get('location_default_country', 'us');
}
return location_province_name($country, $value);
}
}

View File

@@ -0,0 +1,187 @@
<?php
/**
* @file
* Location proximity argument handler.
*/
/**
* Argument handler to accept proximity
*/
class location_handler_argument_location_proximity extends views_handler_argument {
function option_definition() {
$options = parent::option_definition();
// As only us and uk use miles, make km the default otherwise.
$country = variable_get('location_default_country', 'us');
$options['search_units'] = array('default' => ($country == 'us' || $country == 'uk' ? 'mile' : 'km'));
$options['search_method'] = array('default' => 'mbr');
$options['type'] = array('default' => 'postal');
return $options;
}
/**
* Add a form elements to select options for this argument.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['type'] = array(
'#title' => t('Coordinate Type'),
'#type' => 'select',
'#options' => array(
'postal' => t('Postal Code (Zipcode)'),
'latlon' => t('Decimal Latitude and Longitude coordinates, comma delimited'),
),
'#default_value' => $this->options['type'],
'#description' => t('Type of center point.') . '<br />' . t('Postal code argument format: country_postcode_distance or postcode_distance') . '<br />' . t('Lat/Lon argument format: lat,lon_distance') . '<br />' . t('where distance is either a number or a comma delimited pair of decimal degrees'),
);
// Units used.
$form['search_units'] = array(
'#type' => 'select',
'#title' => t('Distance unit'),
'#options' => array(
'km' => t('Kilometers'),
'm' => t('Meters'),
'mile' => t('Miles'),
'dd' => t('Decimal degrees'),
),
'#default_value' => $this->options['search_units'],
'#description' => t('Select the unit of distance. Decimal degrees should be comma delimited.'),
);
$form['search_method'] = array(
'#title' => t('Method'),
'#type' => 'select',
'#options' => array(
'dist' => t('Circular Proximity'),
'mbr' => t('Rectangular Proximity'),
),
'#default_value' => $this->options['search_method'],
'#description' => t('Method of determining proximity. Please note that Circular Proximity does not work with Decimal degrees.'),
);
}
function calculate_coords() {
if (!empty($this->value['latitude']) && !empty($this->value['longitude'])) {
// If there are already coordinates, there's no work for us.
return TRUE;
}
// @@@ Switch to mock location object and rely on location more?
if ($this->options['type'] == 'postal') {
if (!isset($this->value['country'])) {
$this->value['country'] = variable_get('location_default_country', 'us');
}
// Zip code lookup.
if (!empty($this->value['postal_code']) && !empty($this->value['country'])) {
location_load_country($this->value['country']);
$coord = location_get_postalcode_data($this->value);
if ($coord) {
$this->value['latitude'] = $coord['lat'];
$this->value['longitude'] = $coord['lon'];
}
else {
$coord = location_latlon_rough($this->value);
if ($coord) {
$this->value['latitude'] = $coord['lat'];
$this->value['longitude'] = $coord['lon'];
}
else {
return FALSE;
}
}
}
else {
return FALSE;
}
}
return TRUE;
}
/**
* Set up the query for this argument.
*
* The argument sent may be found at $this->argument.
*/
function query($group_by = FALSE) {
// Get and process argument.
if ($this->options['type'] == 'postal') {
foreach ($this->view->argument as $argument) {
if ($argument->field == 'distance') {
$arg_parts = explode('_', $this->view->args[$argument->position]);
if (count($arg_parts) == 3) {
$this->value['country'] = drupal_strtolower($arg_parts[0]);
$this->value['postal_code'] = $arg_parts[1];
$this->value['search_distance'] = $arg_parts[2];
}
else {
$this->value['postal_code'] = $arg_parts[0];
$this->value['search_distance'] = $arg_parts[1];
}
break;
}
}
}
else if ($this->options['type'] == 'latlon') {
foreach ($this->view->argument as $argument) {
if ($argument->field == 'distance') {
list($coords, $this->value['search_distance']) = explode('_', $this->view->args[$argument->position]);
list($this->value['latitude'], $this->value['longitude']) = explode(',', $coords);
break;
}
}
}
// Coordinates available?
if (!$this->calculate_coords()) {
// Distance set?
if (!empty($this->value['search_distance'])) {
// Hmm, distance set but unable to resolve coordinates.
// Force nothing to match.
$this->query->add_where(0, "1 = 0");
}
return;
}
$this->ensure_my_table();
$lat = $this->value['latitude'];
$lon = $this->value['longitude'];
// search_distance
if ($this->options['search_units'] == 'dd') {
list($lat_distance, $lon_distance) = explode(',', $this->value['search_distance']);
$latrange[0] = $lat - $lat_distance;
$latrange[1] = $lat + $lat_distance;
$lonrange[0] = $lon - $lon_distance;
$lonrange[1] = $lon + $lon_distance;
}
else {
$distance = $this->value['search_distance'];
if ($this->options['search_units'] == 'm') {
$distance_meters = $distance;
}
else {
$distance_meters = _location_convert_distance_to_meters($distance, $this->options['search_units']);
}
$latrange = earth_latitude_range($lon, $lat, $distance_meters);
$lonrange = earth_longitude_range($lon, $lat, $distance_meters);
}
// Add MBR check (always).
// In case we go past the 180/-180 mark for longitude.
if ($lonrange[0] > $lonrange[1]) {
$where = "$this->table_alias.latitude > :minlat AND $this->table_alias.latitude < :maxlat AND (($this->table_alias.longitude < 180 AND $this->table_alias.longitude > :minlon) OR ($this->table_alias.longitude < :maxlon AND $this->table_alias.longitude > -180))";
}
else {
$where = "$this->table_alias.latitude > :minlat AND $this->table_alias.latitude < :maxlat AND $this->table_alias.longitude > :minlon AND $this->table_alias.longitude < :maxlon";
}
$this->query->add_where_expression(0, $where, array(':minlat' => $latrange[0], ':maxlat' => $latrange[1], ':minlon' => $lonrange[0], ':maxlon' => $lonrange[1]));
if ($this->options['search_method'] == 'dist') {
// Add radius check.
$this->query->add_where_expression(0, earth_distance_sql($lon, $lat, $this->table_alias) . ' < :distance', array(':distance' => $distance_meters));
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* @file
* Field handler to display a complete address.
*/
class location_handler_field_location_address extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['hide'] = array('default' => array());
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['hide'] = array(
'#type' => 'checkboxes',
'#title' => t('Hide fields from display'),
'#options' => location_field_names(TRUE),
'#default_value' => $this->options['hide'],
);
}
function render($values) {
if ($values->{$this->field_alias}) {
$location = location_load_location($values->{$this->field_alias});
if ($location['lid']) {
return theme('location', array('location' => $location, 'hide' => $this->options['hide']));
}
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* @file
* Country field handler.
*/
class location_handler_field_location_country extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['style'] = array('default' => 'name');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['style'] = array(
'#title' => t('Display style'),
'#type' => 'select',
'#options' => array('name' => t('Country name'), 'code' => t('Country code')),
'#default_value' => $this->options['style'],
);
}
function render($values) {
if ($this->options['style'] == 'name') {
return check_plain(location_country_name($values->{$this->field_alias}));
}
else {
return check_plain(strtoupper($values->{$this->field_alias}));
}
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* @file
* Coordinates field handler.
*/
class location_handler_field_location_distance extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['origin'] = array('default' => 'user');
$options['units'] = array('default' => 'km');
$options['latitude'] = array('default' => '');
$options['longitude'] = array('default' => '');
$options['postal_code'] = array('default' => '');
$options['country'] = array('default' => '');
$options['php_code'] = array('default' => '');
$options['nid_arg'] = array('default' => '');
$options['nid_loc_field'] = array('default' => 'node');
$options['uid_arg'] = array('default' => '');
return $options;
}
function has_extra_options() {
return TRUE;
}
function extra_options_form(&$form, &$form_state) {
$form['units'] = array(
'#type' => 'radios',
'#title' => t('Units'),
'#options' => array(
'km' => t('Kilometers'),
'mi' => t('Miles'),
),
'#default_value' => $this->options['units'],
);
$form['origin'] = array(
'#type' => 'select',
'#title' => t('Origin'),
'#options' => array(
'user' => t("User's Latitude / Longitude (blank if unset)"),
'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
'static' => t('Static Latitude / Longitude'),
'tied' => t("Use Distance / Proximity filter"),
'postal' => t('Postal Code / Country'),
'postal_default' => t('Postal Code (assume default country)'),
'php' => t('Use PHP code to determine latitude/longitude'),
'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
'distance_arg' => t("Latitude / Longitude from views argument"),
),
'#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
'#default_value' => $this->options['origin'],
);
$form['latitude'] = array(
'#type' => 'textfield',
'#title' => t('Latitude'),
'#default_value' => $this->options['latitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
);
$form['longitude'] = array(
'#type' => 'textfield',
'#title' => t('Longitude'),
'#default_value' => $this->options['longitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
);
$form['postal_code'] = array(
'#type' => 'textfield',
'#title' => t('Postal code'),
'#default_value' => $this->options['postal_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
'#maxlength' => 16
);
$form['country'] = array(
'#type' => 'select',
'#title' => t('Country'),
'#options' => array('' => '') + location_get_iso3166_list(),
'#default_value' => $this->options['country'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal')),
);
$form['php_code'] = array(
'#type' => 'textarea',
'#title' => t('PHP code for latitude, longitude'),
'#default_value' => $this->options['php_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('php')),
'#description' => t("Enter PHP code that returns a latitude/longitude. Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
);
list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
$nid_loc_field_options = location_views_proximity_get_location_field_options();
$form['nid_arg'] = array(
'#type' => 'select',
'#title' => t('Node ID argument to use'),
'#options' => $nid_argument_options,
'#default_value' => $this->options['nid_arg'],
'#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. 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("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
);
$form['nid_loc_field'] = array(
'#type' => 'select',
'#title' => t('Location to use'),
'#options' => $nid_loc_field_options,
'#default_value' => $this->options['nid_loc_field'],
'#description' => t("Select which of the node's locations to use as the origin. Either the node locations or a CCK location field. If the location supports multiple entries the first one will be used."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
);
$form['uid_arg'] = array(
'#type' => 'select',
'#title' => t('User ID argument to use'),
'#options' => $uid_argument_options,
'#default_value' => $this->options['uid_arg'],
'#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('uid_arg')),
);
}
function click_sort($order) {
$location = location_views_proximity_get_reference_location($this->view, $this->options);
if ($location) {
$this->query->add_orderby(NULL, earth_distance_sql($location['longitude'], $location['latitude'], $this->table_alias), $order, $this->field_alias);
}
}
function render($values) {
if (empty($values->{$this->field_alias}) || $values->{$this->field_alias} == 'Unknown') {
// Unset location, empty display.
return;
}
$dist = (float)$values->{$this->field_alias};
if ($this->options['units'] == 'km') {
$dist = $dist / 1000.0;
return theme('location_distance', array('distance' => $dist, 'units' => 'km'));
}
else {
$dist = $dist / 1609.347;
return theme('location_distance', array('distance' => $dist, 'units' => 'mi'));
}
}
function query() {
$this->ensure_my_table();
$coordinates = location_views_proximity_get_reference_location($this->view, $this->options);
if (!empty($coordinates)) {
$this->field_alias = $this->query->add_field(NULL, earth_distance_sql($coordinates['longitude'], $coordinates['latitude'], $this->table_alias), $this->table_alias .'_'. $this->field);
}
else {
$this->field_alias = $this->query->add_field(NULL, "'Unknown'", $this->table_alias .'_'. $this->field);
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @file
* Province field handler.
*/
class location_handler_field_location_province extends views_handler_field {
function construct() {
parent::construct();
$this->additional_fields = array(
'country' => 'country',
);
}
function option_definition() {
$options = parent::option_definition();
$options['style'] = array('default' => 'name');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['style'] = array(
'#title' => t('Display style'),
'#type' => 'select',
'#options' => array('name' => t('Province name'), 'code' => t('Province code')),
'#default_value' => $this->options['style'],
);
}
function render($values) {
if ($this->options['style'] == 'name') {
return check_plain(location_province_name($values->{$this->aliases['country']}, $values->{$this->field_alias}));
}
else {
return check_plain(strtoupper($values->{$this->field_alias}));
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* @file
* Province field handler.
*/
class location_handler_field_location_street extends views_handler_field {
function construct() {
parent::construct();
$this->additional_fields = array(
'additional' => 'additional',
);
}
function option_definition() {
$options = parent::option_definition();
$options['style'] = array('default' => 'both');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['style'] = array(
'#title' => t('Display style'),
'#type' => 'select',
'#options' => array(
'both' => t('Both street and additional'),
'street' => t('Street only'),
'additional' => t('Additional only'),
),
'#default_value' => $this->options['style'],
);
}
function render($values) {
$parts = array();
if ($this->options['style'] != 'additional') {
$parts[] = check_plain($values->{$this->field_alias});
}
if ($this->options['style'] != 'street') {
$additional = trim($values->{$this->aliases['additional']});
if (!empty($additional)) {
$parts[] = check_plain($values->{$this->aliases['additional']});
}
}
// @@@ Better theming?
return implode('<br />', $parts);
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* Filter on country.
*/
class location_handler_filter_location_country extends views_handler_filter_in_operator {
function option_definition() {
$options = parent::option_definition();
$options['operator'] = array('default' => 'in');
return $options;
}
function admin_summary() {
return '';
// $options = $this->operator_options('short');
// return (!empty($this->options['exposed']) ? t('exposed') : $options[$this->operator]);
}
function get_value_options() {
$this->value_options = location_get_iso3166_list();
}
/**
* Provide widgets for filtering by country.
*/
function value_form(&$form, &$form_state) {
$this->get_value_options();
$options = $this->value_options;
$default_value = (array) $this->value;
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator'])) {
// exposed and locked.
$which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
}
else {
$source = 'edit-' . drupal_clean_css_identifier($this->options['expose']['operator']);
}
if (!empty($this->options['expose']['reduce'])) {
$options = $this->reduce_value_options();
if (empty($this->options['expose']['single']) && !empty($this->options['expose']['optional'])) {
$default_value = array();
}
}
if (!empty($this->options['expose']['single'])) {
if (!empty($this->options['expose']['optional']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
$default_value = 'All';
}
else if (empty($default_value)) {
$keys = array_keys($options);
$default_value = array_shift($keys);
}
else {
$copy = $default_value;
$default_value = array_shift($copy);
}
}
}
$form['value'] = array(
'#type' => 'select',
'#title' => t('Country'),
'#default_value' => $default_value,
'#options' => $options,
// Used by province autocompletion js.
'#attributes' => array('class' => array('location_auto_country')),
'#multiple' => TRUE, // views will change this as necessary when exposed.
);
// Let location_autocomplete.js find the correct fields to attach.
$form['value']['#attributes']['class'][] = 'location_auto_join_' . $this->options['expose']['identifier'];
}
function reduce_value_options($input = NULL) {
if (empty($this->options)) {
$this->get_value_options();
}
if (!empty($this->options['expose']['reduce']) && !empty($this->options['value'])) {
$reduced_options = array();
foreach ($this->options['value'] as $value) {
$reduced_options[$value] = $this->value_options[$value];
}
return $reduced_options;
}
return $this->get_value_options();
}
function accept_exposed_input($input) {
if (empty($this->options['exposed'])) {
return TRUE;
}
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
$this->operator = $input[$this->options['expose']['operator_id']];
}
if (!empty($this->options['expose']['identifier'])) {
$value = $input[$this->options['expose']['identifier']];
if (empty($this->options['expose']['required'])) {
if ($value == 'All' || $value === array()) {
if (empty($this->options['value']) || (!empty($this->options['value']) && empty($this->options['expose']['reduce']))) {
return FALSE;
}
else {
$value = $this->options['value'];
}
}
if (!empty($this->always_multiple) && $value === '') {
return FALSE;
}
}
if (isset($value)) {
$this->value = $value;
if (empty($this->always_multiple) && empty($this->options['expose']['multiple'])) {
$this->value = array($value);
}
}
else {
return FALSE;
}
}
return TRUE;
}
function operator_options($which = 'title') {
if (empty($this->options['expose']['multiple'])) {
return array(
'in' => t('Is'),
'not in' => t('Is not'),
);
}
else {
return array(
'in' => t('Is one of'),
'not in' => t('Is not one of'),
);
}
}
function query() {
if (empty($this->value)) {
return;
}
$this->ensure_my_table();
$field = "$this->table_alias.$this->real_field";
// Normalize values.
$value = $this->value;
if (is_array($value)) {
if (count($value) == 1) {
// If multiple is allowed but only one was chosen, use a string instead.
$value = reset($value);
}
}
if (is_array($value)) {
// Multiple values
$operator = ($this->operator == 'in') ? 'IN' : 'NOT IN';
$this->query->add_where($this->options['group'], $field, $value, $operator);
}
else {
// Single value
$operator = ($this->operator == 'in') ? '=' : '!=';
$this->query->add_where($this->options['group'], $field, $value, $operator);
}
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Filter on province.
*/
class location_handler_filter_location_province extends views_handler_filter {
var $location_country = FALSE;
var $location_country_identifier = FALSE;
function option_definition() {
$options = parent::option_definition();
$options['operator'] = array('default' => 'is');
return $options;
}
function admin_summary() {
return '';
// $options = $this->operator_options('short');
// return (!empty($this->options['exposed']) ? t('exposed') : $options[$this->operator]);
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
$country = $this->grovel_country();
drupal_add_js(drupal_get_path('module', 'location') .'/location_autocomplete.js');
$ac = $country;
if (is_array($ac)) {
$ac = implode(',', $ac);
}
$form['value'] = array(
'#type' => 'textfield',
'#title' => t('State/Province'),
'#autocomplete_path' => 'location/autocomplete/'. $ac,
'#default_value' => $this->value,
'#size' => 64,
'#maxlength' => 64,
// Used by province autocompletion js.
'#attributes' => array('class' => array('location_auto_province')),
'#multiple' => TRUE, //$this->options['multiple'],
);
// Let location_autocomplete.js find the correct fields to attach.
if ($this->location_country_identifier) {
$form['value']['#attributes']['class'][] = 'location_auto_join_' . $this->location_country_identifier;
}
}
function operator_options() {
if ($this->options['expose']['single']) {
return array(
'is' => t('Is'),
'is not' => t('Is not'),
);
}
else {
return array(
'is' => t('Is one of'),
'is not' => t('Is not one of'),
);
}
}
function grovel_country() {
$country = variable_get('location_default_country', 'us');
if (!empty($this->view->filter))
foreach ($this->view->filter as $k => $v) {
if ($v->table == 'location' && $v->field == 'country' && $v->options['relationship'] == $this->options['relationship']) {
$country = $v->value;
if (!empty($v->options['expose']['identifier'])) {
if (isset($this->view->exposed_input[$v->options['expose']['identifier']])) {
$country = $this->view->exposed_input[$v->options['expose']['identifier']];
}
$this->location_country_identifier = $v->options['expose']['identifier'];
}
}
}
if ($country == '' || $country == 'All' || $country == ' ' || $country == 'xx') {
// It's set to something nonsensical, reset to the default to prevent malfunctions.
$country = variable_get('location_default_country', 'us');
}
$this->location_country = $country;
return $country;
}
function query() {
// Normalize values.
$value = $this->value;
if (is_array($value)) {
// At one point during development, provinces was a select box.
// Right now it's an autocomplete textfield.
// @@@ Investigate correct fix sometime.
//$value = array_keys($value);
if (count($value) == 1) {
// If multiple is allowed but only one was chosen, use a string instead.
$value = reset($value);
}
}
if (empty($value)) {
return;
}
$country = $this->grovel_country();
$this->ensure_my_table();
$field = "$this->table_alias.$this->real_field";
if (is_array($value)) {
// Multiple values
foreach ($value as $k => $v) {
// Convert to province codes.
$value[$k] = location_province_code($country, $v);
}
$operator = ($this->operator == 'is') ? 'IN' : 'NOT IN';
$this->query->add_where($this->options['group'], $field, $value, $operator);
}
else {
// Single value
// Convert to province code.
$value = location_province_code($country, $value);
$operator = ($this->operator == 'is') ? '=' : '!=';
$this->query->add_where($this->options['group'], $field, $value, $operator);
}
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* @file
* Coordinates sort handler.
*/
class location_handler_sort_location_distance extends views_handler_sort {
function option_definition() {
$options = parent::option_definition();
$options['origin'] = array('default' => 'user');
$options['latitude'] = array('default' => '');
$options['longitude'] = array('default' => '');
$options['postal_code'] = array('default' => '');
$options['country'] = array('default' => '');
$options['php_code'] = array('default' => '');
$options['nid_arg'] = array('default' => '');
$options['nid_loc_field'] = array('default' => 'node');
$options['uid_arg'] = array('default' => '');
return $options;
}
function has_extra_options() {
return TRUE;
}
function extra_options_form(&$form, &$form_state) {
$form['origin'] = array(
'#type' => 'select',
'#title' => t('Origin'),
'#options' => array(
'user' => t("User's Latitude / Longitude (blank if unset)"),
'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
'static' => t('Static Latitude / Longitude'),
'tied' => t("Use Distance / Proximity filter"),
'postal' => t('Postal Code / Country'),
'postal_default' => t('Postal Code (assume default country)'),
'php' => t('Use PHP code to determine latitude/longitude'),
'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
),
'#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
'#default_value' => $this->options['origin'],
);
$form['latitude'] = array(
'#type' => 'textfield',
'#title' => t('Latitude'),
'#default_value' => $this->options['latitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
);
$form['longitude'] = array(
'#type' => 'textfield',
'#title' => t('Longitude'),
'#default_value' => $this->options['longitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
);
$form['postal_code'] = array(
'#type' => 'textfield',
'#title' => t('Postal code'),
'#default_value' => $this->options['postal_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
'#maxlength' => 16
);
$form['country'] = array(
'#type' => 'select',
'#title' => t('Country'),
'#options' => array('' => '') + location_get_iso3166_list(),
'#default_value' => $this->options['country'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal')),
);
$form['php_code'] = array(
'#type' => 'textarea',
'#title' => t('PHP code for latitude, longitude'),
'#default_value' => $this->options['php_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('php')),
'#description' => t("Enter PHP code that returns a latitude/longitude. Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
);
list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
$nid_loc_field_options = location_views_proximity_get_location_field_options();
$form['nid_arg'] = array(
'#type' => 'select',
'#title' => t('Node ID argument to use'),
'#options' => $nid_argument_options,
'#default_value' => $this->options['nid_arg'],
'#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. 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("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
);
$form['nid_loc_field'] = array(
'#type' => 'select',
'#title' => t('Location to use'),
'#options' => $nid_loc_field_options,
'#default_value' => $this->options['nid_loc_field'],
'#description' => t("Select which of the node's locations to use as the origin. Either the node locations or a CCK location field. If the location supports multiple entries the first one will be used."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
);
$form['uid_arg'] = array(
'#type' => 'select',
'#title' => t('User ID argument to use'),
'#options' => $uid_argument_options,
'#default_value' => $this->options['uid_arg'],
'#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('uid_arg')),
);
}
function query() {
$coordinates = location_views_proximity_get_reference_location($this->view, $this->options);
$this->ensure_my_table();
// OK, so this part will need a little explanation.
// Since the distance calculation is so icky, we try quite hard
// to save some work for the database.
// If someone has added a field that matches the sort, we just sort on that column!
$alias = $this->table_alias . '_' . $this->field . '_sort';
foreach ($this->view->field as $filter) {
if ($filter->table == 'location' && $filter->field == 'distance' && $filter->options['relationship'] == $this->options['relationship']) {
if ($filter->options['origin'] == $this->options['origin']
&& $filter->options['latitude'] == $this->options['latitude']
&& $filter->options['longitude'] == $this->options['longitude']) {
// We have a match! Sync aliases to make it easier on the database.
$alias = $filter->field_alias;
}
}
}
if (empty($coordinates)) {
// We don't know the distance.
// Therefore, we don't need to sort on it.
}
else {
// This is done exactly the same as the field version.
// Views is ok with us redefining the formula for a field.
// If ANYTHING differs in the configuration, we will use a new alias.
$this->query->add_orderby(NULL, earth_distance_sql($coordinates['longitude'], $coordinates['latitude'], $this->table_alias), $this->options['order'], $alias);
}
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @file
* Coordinates field handler.
*/
class location_views_handler_field_coordinates extends location_views_handler_field_latitude {
function construct() {
parent::construct();
$this->additional_fields['longitude'] = 'longitude';
}
function render($values) {
if ($this->options['style'] == 'dms') {
return theme('location_latitude_dms', array('latitude' => $values->{$this->field_alias})) . ', ' . theme('location_longitude_dms', array('longitude' => $values->{$this->aliases['longitude']}));
}
else {
return check_plain($values->{$this->field_alias}) . ', ' . check_plain($values->{$this->aliases['longitude']});
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* @file
* Latitude field handler.
*/
class location_views_handler_field_latitude extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['style'] = array('default' => 'dms');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['style'] = array(
'#title' => t('Display style'),
'#type' => 'select',
'#options' => array(
'dd' => t('Decimal degrees'),
'dms' => t('Degrees, minutes, seconds'),
),
'#default_value' => $this->options['style'],
);
}
function render($values) {
if ($this->options['style'] == 'dd') {
return check_plain($values->{$this->field_alias});
}
else {
return theme('location_latitude_dms', array('latitude' => $values->{$this->field_alias}));
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* @file
* Longitude field handler.
*/
class location_views_handler_field_longitude extends location_views_handler_field_latitude {
function render($values) {
if ($this->options['style'] == 'dms') {
return theme('location_longitude_dms', array('longitude' => $values->{$this->field_alias}));
}
return parent::render($values);
}
}

View File

@@ -0,0 +1,334 @@
<?php
/**
* General proximity filter for location latitude/longitude.
*/
class location_views_handler_filter_proximity extends views_handler_filter {
// This is always multiple, because we can have distance, units etc.
var $always_multiple = TRUE;
function option_definition() {
$options = parent::option_definition();
$options['operator'] = array('default' => 'mbr');
$options['identifier'] = array('default' => 'dist');
$options['origin'] = array('default' => 'user');
$options['value'] = array(
'default' => array(
'latitude' => '',
'longitude' => '',
'postal_code' => '',
'country' => '',
'php_code' => '',
'nid_arg' => '',
'nid_loc_field' => 'node',
'uid_arg' => '',
'search_distance' => 100,
'search_units' => 'mile',
),
);
$options['expose']['contains']['gmap_macro'] = array('default' => '[gmap ]');
$options['expose']['contains']['user_location_choose'] = array('default' => FALSE);
return $options;
}
function admin_summary() {
return '';
}
function operator_options() {
return array(
'mbr' => t('Proximity (Rectangular)'),
'dist' => t('Proximity (Circular)'),
);
}
function expose_options() {
parent::expose_options();
$this->options['expose']['gmap_macro'] = array('default' => '[gmap ]');
$this->options['expose']['user_location_choose'] = array('default' => FALSE);
}
function expose_form_left(&$form, &$form_state) {
parent::expose_form_left($form, $form_state);
// @@@ Todo: autohide this.
$form['expose']['gmap_macro'] = array(
'#parents' => array('options', 'gmap_macro'),
'#type' => 'textfield',
'#title' => t('GMap macro'),
'#description' => t('The macro to use for the Latitude / Longitude map, if applicable.'),
'#default_value' => $this->options['expose']['gmap_macro'],
);
$form['expose']['user_location_choose'] = array(
'#type' => 'checkbox',
'#title' => t("Allow choice of user location"),
'#default_value' => $this->options['expose']['user_location_choose'],
'#description' => t("If checked and using a user location origin, the user will be able to choose which of their locations to use. Otherwise their first location will be used."),
);
}
function value_form(&$form, &$form_state) {
$form['origin'] = array(
'#type' => 'select',
'#title' => t('Origin'),
'#options' => array(
'user' => t("User's Latitude / Longitude (blank if unset)"),
'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
'static' => t('Static Latitude / Longitude'),
'postal' => t('Postal Code / Country'),
'postal_default' => t('Postal Code (assume default country)'),
'php' => t('Use PHP code to determine latitude/longitude'),
'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
),
'#description' => t('This will be the way the latitude/longitude of origin is determined. If this filter is exposed, this will determine the default values. NOTE: The PHP code, nid argument and uid argument options will not be available when the filter is exposed and the use of map is only available when the filter is exposed.'),
'#default_value' => $this->options['origin'] ? $this->options['origin'] : 'user',
);
if (module_exists('gmap')) {
$form['origin']['#options']['latlon_gmap'] = t('Latitude / Longitude input (use map)');
}
// [11:44] <merlinofchaos> If you load the page from scratch, $input for your identifier will be empty.
// [11:44] <merlinofchaos> So what's going on here is that for $_GET forms, there's no form id, no op button or anything, so they always appear to submit.
// [11:45] <merlinofchaos> FAPI doesn't quite get along with that; sometimes it handles the input being empty right and sometimes it doesn't.
// [11:45] <Bdragon> But if I set #default_value to a static string, it doesn't work either
// [11:45] <merlinofchaos> Right, fapi thinks the empty input is actually input, thus it overrides the default value.
// [11:45] <Bdragon> Ahh, hmm...
// [11:46] <Bdragon> So where would I go to clean it up?
// [11:55] <merlinofchaos> Bdragon: See views_handler_filter_string.inc line 174
// [11:55] <merlinofchaos> Bdragon: That's how i address this problem.
// [11:58] <Bdragon> Hmm, OK
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (!isset($form_state['input'][$identifier])) {
// We need to pretend the user already inputted the defaults, because
// fapi will malfunction otherwise.
$form_state['input'][$identifier] = $this->value;
}
}
$form['value'] = array(
'#tree' => TRUE,
);
$form['value']['latitude'] = array(
'#type' => 'textfield',
'#title' => t('Latitude'),
'#default_value' => $this->value['latitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
'#weight' => 1,
);
$form['value']['longitude'] = array(
'#type' => 'textfield',
'#title' => t('Longitude'),
'#default_value' => $this->value['longitude'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
'#weight' => 2,
);
$form['value']['postal_code'] = array(
'#type' => 'textfield',
'#title' => t('Postal code'),
'#default_value' => $this->value['postal_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
'#weight' => 3,
'#maxlength' => 16
);
$form['value']['country'] = array(
'#type' => 'select',
'#title' => t('Country'),
'#options' => array('' => '') + location_get_iso3166_list(),
'#default_value' => $this->value['country'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('postal')),
'#weight' => 4,
);
$form['value']['php_code'] = array(
'#type' => 'textarea',
'#title' => t('PHP code for latitude, longitude'),
'#default_value' => $this->value['php_code'],
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('php')),
'#description' => t("Enter PHP code that returns a latitude/longitude. Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
'#weight' => 5,
);
list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
$nid_loc_field_options = location_views_proximity_get_location_field_options();
$form['value']['nid_arg'] = array(
'#type' => 'select',
'#title' => t('Node ID argument to use'),
'#options' => $nid_argument_options,
'#default_value' => $this->value['nid_arg'],
'#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. 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("Select which of the view's arguments to use as the node ID. The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
'#weight' => 6,
);
$form['value']['nid_loc_field'] = array(
'#type' => 'select',
'#title' => t('Location to use'),
'#options' => $nid_loc_field_options,
'#default_value' => $this->value['nid_loc_field'],
'#description' => t("Select which of the node's locations to use as the origin. Either the node locations or a CCK location field. If the location supports multiple entries the first one will be used."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('nid_arg')),
'#weight' => 7,
);
$form['value']['uid_arg'] = array(
'#type' => 'select',
'#title' => t('User ID argument to use'),
'#options' => $uid_argument_options,
'#default_value' => $this->value['uid_arg'],
'#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID. The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
'#process' => array('ctools_dependent_process'),
'#dependency' => array('edit-options-origin' => array('uid_arg')),
'#weight' => 8,
);
$form['value']['search_distance'] = array(
'#type' => 'textfield',
'#title' => t('Distance'),
'#default_value' => $this->value['search_distance'],
'#weight' => 9,
);
$form['value']['search_units'] = array(
'#type' => 'select',
'#options' => array(
'mile' => t('Miles'),
'km' => t('Kilometers'),
),
'#default_value' => $this->value['search_units'],
'#weight' => 10,
);
}
function exposed_form(&$form, &$form_state) {
parent::exposed_form($form, $form_state);
$key = $this->options['expose']['identifier'];
$origin = $this->options['origin'];
// Strip dependencies off on exposed form.
foreach (element_children($form[$key]) as $el) {
if (!empty($form[$key][$el]['#dependency'])) {
$form[$key][$el]['#dependency'] = array();
}
}
if ($origin == 'latlon_gmap' && module_exists('gmap')) {
// Bad things happen if we try to show a gmap in the views live preview.
if (!empty($form_state['view']->live_preview)) {
$form[$key]['proximity_map'] = array(
'#markup' => t('Gmap location selection is not available during live preview.'),
'#weight' => 0,
);
}
else {
$form[$key]['proximity_map'] = array(
'#markup' => gmap_set_location($this->options['expose']['gmap_macro'], $form[$key], array('latitude' => 'latitude', 'longitude' => 'longitude')),
'#weight' => 0,
);
}
}
else if (($origin == 'user' || $origin == 'hybrid') && $this->options['expose']['user_location_choose']) {
global $user;
$user_locations = isset($user->locations) ? $user->locations : location_load_locations($user->uid, 'uid');
$location_options = array();
if (!empty($user_locations)) {
foreach ($user_locations as $i => $location) {
if (isset($location['latitude']) && isset($location['longitude'])) {
if (!empty($location['name'])) {
$location_options[$i] = t(check_plain($location['name']));
}
else {
$location_options[$i] = t('Location #@num', array('@num' => $i + 1));
}
}
}
}
$form[$key]['user_location_delta'] = array(
'#type' => 'select',
'#title' => t('Location'),
'#options' => $location_options,
'#description' => t('Select which of your locations to use.'),
'#weight' => 0,
);
}
// Remove unneeded fields when exposing the form.
// It's shorter than redefining value_form.
if ($origin != 'static' && $origin != 'latlon_gmap') {
unset($form[$key]['latitude']);
unset($form[$key]['longitude']);
}
if ($origin != 'postal' && $origin != 'postal_default') {
unset($form[$key]['postal_code']);
}
if ($origin != 'postal') {
unset($form[$key]['country']);
}
// And we definitely do not want to expose the php code option when exposing the filter
unset($form[$key]['php_code']);
// The nid/uid arg options are not useful on an exposed form.
unset($form[$key]['nid_arg']);
unset($form[$key]['nid_loc_field']);
unset($form[$key]['uid_arg']);
unset($form['origin']);
}
function query() {
if (empty($this->value)) {
return;
}
// We need to merge with $this->options['value'] for filter values
// and $this->value for exposed filter values.
$options = array_merge($this->options, $this->options['value'], $this->value);
$coordinates = location_views_proximity_get_reference_location($this->view, $options);
// If we don't have any coordinates or distance, there's nothing to filter
// by, so don't modify the query at all.
if (empty($coordinates) || empty($this->value['search_distance'])) {
return;
}
$this->ensure_my_table();
$lat = $coordinates['latitude'];
$lon = $coordinates['longitude'];
$distance_meters = _location_convert_distance_to_meters($this->value['search_distance'], $this->value['search_units']);
$latrange = earth_latitude_range($lon, $lat, $distance_meters);
$lonrange = earth_longitude_range($lon, $lat, $distance_meters);
// Add MBR check (always).
// In case we go past the 180/-180 mark for longitude.
if ($lonrange[0] > $lonrange[1]) {
$where = "$this->table_alias.latitude > :minlat AND $this->table_alias.latitude < :maxlat AND (($this->table_alias.longitude < 180 AND $this->table_alias.longitude > :minlon) OR ($this->table_alias.longitude < :maxlon AND $this->table_alias.longitude > -180))";
}
else {
$where = "$this->table_alias.latitude > :minlat AND $this->table_alias.latitude < :maxlat AND $this->table_alias.longitude > :minlon AND $this->table_alias.longitude < :maxlon";
}
$this->query->add_where_expression($this->options['group'], $where, array(':minlat' => $latrange[0], ':maxlat' => $latrange[1], ':minlon' => $lonrange[0], ':maxlon' => $lonrange[1]));
if ($this->operator == 'dist') {
// Add radius check.
$this->query->add_where_expression($this->options['group'], earth_distance_sql($lon, $lat, $this->table_alias) .' < :distance', array(':distance' => $distance_meters));
}
}
}