Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
<?php
/**
* @brief GeoCoder
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 11 Dec 2017
*/
namespace IPS\GeoLocation;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* GeoCoder abstract class
*/
abstract class _GeoCoder
{
/**
* @brief Cached GeoCoder instance
*/
protected static $instance = NULL;
/**
* Return instance of GeoCoder
*
* @return \IPS\GeoCoder
* @throws \BadMethodCallException
*/
public static function i()
{
if( static::$instance === NULL )
{
if ( \IPS\Settings::i()->googlemaps and \IPS\Settings::i()->google_maps_api_key )
{
static::$instance = new \IPS\GeoLocation\GeoCoder\Google();
}
elseif ( \IPS\Settings::i()->mapbox and \IPS\Settings::i()->mapbox_api_key )
{
static::$instance = new \IPS\GeoLocation\GeoCoder\Mapbox();
}
else
{
throw new \BadMethodCallException;
}
}
return static::$instance;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
/**
* @brief Google GeoCoder
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 11 Dec 2017
*/
namespace IPS\GeoLocation\GeoCoder;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Google GeoCoder class
*/
class _Google extends \IPS\GeoLocation\GeoCoder
{
/**
* Get by latitude and longitude
*
* @param float $lat Latitude
* @param float $long Longitude
* @return \IPS\GeoLocation
* @throws \BadFunctionCallException
* @throws \IPS\Http\Request\Exception
*/
public static function decodeLatLong( $lat, $long )
{
if ( \IPS\Settings::i()->googlemaps AND \IPS\Settings::i()->google_maps_api_key )
{
$data = \IPS\Http\Url::external( "https://maps.googleapis.com/maps/api/geocode/json" )->setQueryString( array(
'latlng' => "{$lat},{$long}",
'sensor' => 'false',
'key' => \IPS\Settings::i()->google_maps_api_key
) )->request()->get()->decodeJson();
$obj = new \IPS\GeoLocation;
$obj->lat = $lat;
$obj->long = $long;
$_address = '';
/* Make sure the response from Google is valid */
if( isset( $data['results'] ) AND is_array( $data['results'] ) AND count( $data['results'] ) )
{
foreach( $data['results'][0]['address_components'] as $component )
{
if( $component['types'][0] == 'street_number' )
{
$_address = $component['long_name'];
}
elseif( $component['types'][0] == 'route' )
{
$_address .= " " . $component['long_name'];
}
if( $component['types'][0] == 'postal_code' )
{
$obj->postalCode = $component['long_name'];
}
if( $component['types'][0] == 'country' )
{
$obj->country = $component['short_name'];
}
if( $component['types'][0] == 'administrative_area_level_1' )
{
$obj->region = $component['long_name'];
}
if( $component['types'][0] == 'locality' )
{
$obj->city = $component['long_name'];
}
}
}
if( $_address )
{
$obj->addressLines = array( $_address );
}
return $obj;
}
else
{
throw new \BadFunctionCallException;
}
}
/**
* Get the latitude and longitude for the current object. Address must be set.
*
* @param \IPS\GeoLocation $geoLocation Geolocation object
* @return void
* @throws \BadMethodCallException
*/
public function setLatLong( \IPS\GeoLocation &$geoLocation )
{
if ( \IPS\Settings::i()->googlemaps AND \IPS\Settings::i()->google_maps_api_key AND $geoLocation->toString() )
{
try
{
$data = \IPS\Http\Url::external( "https://maps.googleapis.com/maps/api/geocode/json" )->setQueryString( array(
'address' => $geoLocation->toString(),
'sensor' => 'false',
'key' => \IPS\Settings::i()->google_maps_api_key
) )->request()->get()->decodeJson();
}
catch( \RuntimeException $e )
{
return;
}
if ( !count( $data['results'] ) )
{
return;
}
$_address = NULL;
$geoLocation->lat = $data['results'][0]['geometry']['location']['lat'];
$geoLocation->long = $data['results'][0]['geometry']['location']['lng'];
}
else
{
throw new \BadFunctionCallException;
}
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
/**
* @brief Mapbox GeoCoder
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 11 Dec 2017
*/
namespace IPS\GeoLocation\GeoCoder;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Mapbox GeoCoder class
*/
class _Mapbox extends \IPS\GeoLocation\GeoCoder
{
/**
* Get by latitude and longitude
*
* @param float $lat Latitude
* @param float $long Longitude
* @return \IPS\GeoLocation
* @throws \BadFunctionCallException
* @throws \IPS\Http\Request\Exception
*/
public static function decodeLatLong( $lat, $long )
{
if ( \IPS\Settings::i()->mapbox AND \IPS\Settings::i()->mapbox_api_key )
{
$location = $long . ',' . $lat;
$data = \IPS\Http\Url::external( "https://api.mapbox.com/geocoding/v5/mapbox.places/{$location}.json" )->setQueryString( array(
'access_token' => \IPS\Settings::i()->mapbox_api_key,
) )->request()->get()->decodeJson();
$obj = new \IPS\GeoLocation;
$obj->lat = $lat;
$obj->long = $long;
$_address = '';
/* Make sure the response from Mapbox is valid */
if( isset( $data['results'] ) AND is_array( $data['results'] ) AND count( $data['results'] ) )
{
foreach( $data['results'][0]['address_components'] as $component )
{
if( $component['types'][0] == 'street_number' )
{
$_address = $component['long_name'];
}
elseif( $component['types'][0] == 'route' )
{
$_address .= " " . $component['long_name'];
}
if( $component['types'][0] == 'postal_code' )
{
$obj->postalCode = $component['long_name'];
}
if( $component['types'][0] == 'country' )
{
$obj->country = $component['short_name'];
}
if( $component['types'][0] == 'administrative_area_level_1' )
{
$obj->region = $component['long_name'];
}
if( $component['types'][0] == 'locality' )
{
$obj->city = $component['long_name'];
}
}
}
if( $_address )
{
$obj->addressLines = array( $_address );
}
return $obj;
}
else
{
throw new \BadFunctionCallException;
}
}
/**
* Get the latitude and longitude for the current object. Address must be set.
*
* @param \IPS\GeoLocation $geoLocation Geolocation object
* @return void
* @throws \BadMethodCallException
*/
public function setLatLong( \IPS\GeoLocation &$geoLocation )
{
if ( \IPS\Settings::i()->mapbox AND \IPS\Settings::i()->mapbox_api_key AND $geoLocation->toString() )
{
try
{
$data = \IPS\Http\Url::external( "https://api.mapbox.com/geocoding/v5/mapbox.places/" . urlencode( html_entity_decode( $geoLocation->toString() ) ) . ".json" )->setQueryString( array(
'access_token' => \IPS\Settings::i()->mapbox_api_key,
) )->request()->get()->decodeJson();
}
catch( \RuntimeException $e )
{
return;
}
if ( !isset( $data['features'] ) or !count( $data['features'] ) )
{
return;
}
$_address = NULL;
$geoLocation->long = $data['features'][0]['center'][0];
$geoLocation->lat = $data['features'][0]['center'][1];
}
else
{
throw new \BadFunctionCallException;
}
}
}
+36 -147
View File
@@ -277,7 +277,7 @@ class _GeoLocation
'ZM', // Zambia
'ZW', // Zimbabwe
);
/**
* @brief State List
*/
@@ -291,7 +291,7 @@ class _GeoLocation
'Tasmania',
'Victoria',
'Western Australia',
),
),
'BR' => array(
'Acre',
'Alagoas',
@@ -335,7 +335,7 @@ class _GeoLocation
"Quebec",
"Saskatchewan",
"Yukon",
),
),
'PT' => array(
"Aveiro",
"Azores",
@@ -357,7 +357,7 @@ class _GeoLocation
"Viana do Castelo",
"Vila Real",
"Viseu",
),
),
'US' => array(
"Alabama",
"Alaska",
@@ -421,8 +421,8 @@ class _GeoLocation
"Armed Forces - Americas",
"Armed Forces - Europe",
"Armed Forces - Pacific",
),
);
),
);
/**
* @brief Latitude
@@ -554,67 +554,7 @@ class _GeoLocation
*/
public static function getByLatLong( $lat, $long )
{
if ( \IPS\Settings::i()->google_maps_api_key )
{
$data = \IPS\Http\Url::external( "https://maps.googleapis.com/maps/api/geocode/json" )->setQueryString( array(
'latlng' => "{$lat},{$long}",
'sensor' => 'false',
'key' => \IPS\Settings::i()->google_maps_api_key
) )->request()->get()->decodeJson();
$obj = new static;
$obj->lat = $lat;
$obj->long = $long;
$_address = '';
/* Make sure the response from Google is valid */
if( isset( $data['results'] ) AND is_array( $data['results'] ) AND count( $data['results'] ) )
{
foreach( $data['results'][0]['address_components'] as $component )
{
if( $component['types'][0] == 'street_number' )
{
$_address = $component['long_name'];
}
elseif( $component['types'][0] == 'route' )
{
$_address .= " " . $component['long_name'];
}
if( $component['types'][0] == 'postal_code' )
{
$obj->postalCode = $component['long_name'];
}
if( $component['types'][0] == 'country' )
{
$obj->country = $component['short_name'];
}
if( $component['types'][0] == 'administrative_area_level_1' )
{
$obj->region = $component['long_name'];
}
if( $component['types'][0] == 'locality' )
{
$obj->city = $component['long_name'];
}
}
}
if( $_address )
{
$obj->addressLines = array( $_address );
}
return $obj;
}
else
{
throw new \BadFunctionCallException;
}
return \IPS\GeoLocation\GeoCoder::i()->decodeLatLong( $lat, $long );
}
/**
@@ -625,75 +565,9 @@ class _GeoLocation
*/
public function getLatLong()
{
if ( \IPS\Settings::i()->google_maps_api_key AND $this->toString() )
{
try
{
$data = \IPS\Http\Url::external( "https://maps.googleapis.com/maps/api/geocode/json" )->setQueryString( array(
'address' => $this->toString(),
'sensor' => 'false',
'key' => \IPS\Settings::i()->google_maps_api_key
) )->request()->get()->decodeJson();
}
catch( \RuntimeException $e )
{
return;
}
if ( !count( $data['results'] ) )
{
return;
}
$_address = NULL;
$this->lat = $data['results'][0]['geometry']['location']['lat'];
$this->long = $data['results'][0]['geometry']['location']['lng'];
/* Reset address components in case there were typos */
foreach( $data['results'][0]['address_components'] as $component )
{
if( $component['types'][0] == 'street_number' )
{
$_address = $component['long_name'];
}
elseif( $component['types'][0] == 'route' )
{
$_address .= " " . $component['long_name'];
}
if( $component['types'][0] == 'postal_code' )
{
$this->postalCode = $component['long_name'];
}
if( $component['types'][0] == 'country' )
{
$this->country = $component['short_name'];
}
if( $component['types'][0] == 'administrative_area_level_1' )
{
$this->region = $component['long_name'];
}
if( $component['types'][0] == 'locality' )
{
$this->city = $component['long_name'];
}
}
if( $_address )
{
$this->addressLines = array( $_address );
}
}
else
{
throw new \BadFunctionCallException;
}
\IPS\GeoLocation\GeoCoder::i()->setLatLong( $this );
}
/**
* Build from JSON
*
@@ -723,7 +597,7 @@ class _GeoLocation
{
return $this->toString();
}
/**
* Convert to string
*
@@ -731,7 +605,7 @@ class _GeoLocation
* @param string|null $name Optional name to add to the address
* @return string
* @note While some places like France capitalize the surname, this cannot be done automatically because the surname could be supplied as the first or last
name value, and the name could contain more than one string that constitutes the surname.
name value, and the name could contain more than one string that constitutes the surname.
*/
public function toString( $separator=', ', $name=NULL )
{
@@ -752,13 +626,13 @@ class _GeoLocation
{
if( $v )
{
$output[] = htmlspecialchars( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$output[] = htmlspecialchars( $v, ENT_DISALLOWED, 'UTF-8', FALSE );
}
}
}
else
{
$output[] = htmlspecialchars( $this->$k, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$output[] = htmlspecialchars( $this->$k, ENT_DISALLOWED, 'UTF-8', FALSE );
}
}
}
@@ -766,14 +640,14 @@ class _GeoLocation
{
try
{
$output[] = \IPS\Member::loggedIn()->language()->get( htmlspecialchars( 'country-' . $this->country, \IPS\HTMLENTITIES, 'UTF-8', FALSE ), FALSE, array( 'strtoupper' => TRUE ) );
$output[] = \IPS\Member::loggedIn()->language()->get( htmlspecialchars( 'country-' . $this->country, ENT_DISALLOWED, 'UTF-8', FALSE ), FALSE, array( 'strtoupper' => TRUE ) );
}
catch ( \UnderflowException $e )
{
$output[] = htmlspecialchars( $this->country, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$output[] = htmlspecialchars( $this->country, ENT_DISALLOWED, 'UTF-8', FALSE );
}
}
if ( !empty( $output ) )
{
return implode( $separator, $output );
@@ -782,7 +656,7 @@ class _GeoLocation
{
return "{$this->lat},{$this->long}";
}
return '';
}
@@ -800,12 +674,16 @@ class _GeoLocation
{
$this->map = new \IPS\GeoLocation\Maps\Google( $this );
}
else if ( \IPS\Settings::i()->mapbox and \IPS\Settings::i()->mapbox_api_key)
{
$this->map = new \IPS\GeoLocation\Maps\Mapbox( $this );
}
else
{
throw new \BadMethodCallException;
}
}
return $this->map;
}
@@ -819,7 +697,7 @@ class _GeoLocation
{
$address = json_decode( $data, TRUE );
$mapper = new static;
if ( is_array( $address ) )
{
foreach( $address as $k => $v )
@@ -827,13 +705,14 @@ class _GeoLocation
$mapper->$k = $v;
}
}
return (string) $mapper;
}
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse float lat Latitude
* @apiresponse float long Longitude
@@ -843,8 +722,18 @@ class _GeoLocation
* @apiresponse string country 2-letter country code
* @apiresponse string postalCode ZIP/Postal Code
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
return $this;
}
/**
* GeoLocation is enabled
*
* @return bool
*/
public static function enabled()
{
return ( ( \IPS\Settings::i()->googlemaps and \IPS\Settings::i()->google_maps_api_key ) || ( \IPS\Settings::i()->mapbox and \IPS\Settings::i()->mapbox_api_key ) );
}
}
+6 -3
View File
@@ -44,9 +44,11 @@ class _Google
* @param int $width Width
* @param int $height Height
* @param float|NULL $zoom The zoom amount (a value between 0 being totally zoomed out view of the world, and 1 being as fully zoomed in as possible) or NULL to zoom automatically based on how much data is available
* @param int $scale Google maps scale to use (https://developers.google.com/maps/documentation/static-maps/intro#scale_values)
* @param string $maptype Type of map to use. Valid values are roadmap (default), satellite, terrain, and hybrid (https://developers.google.com/maps/documentation/static-maps/intro#MapTypes)
* @return string
*/
public function render( $width, $height, $zoom=NULL )
public function render( $width, $height, $zoom=NULL, $scale=1, $maptype='roadmap' )
{
if ( $this->geolocation->lat and $this->geolocation->long )
{
@@ -84,11 +86,12 @@ class _Google
'center' => $location,
'zoom' => $zoom === NULL ? NULL : ceil( $zoom * 8 ),
'size' => "{$width}x{$height}",
'sensor' => 'false',
'markers' => $location,
'key' => \IPS\Settings::i()->google_maps_api_key,
'scale' => $scale,
'maptype' => $maptype
) );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->googleMap( $linkUrl, $imageUrl, $this->geolocation->lat, $this->geolocation->long );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->staticMap( $linkUrl, $imageUrl, $this->geolocation->lat, $this->geolocation->long );
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
/**
* @brief Mapbox Maps
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 2 Nov 2017
*/
namespace IPS\GeoLocation\Maps;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Mapbox Maps
*/
class _Mapbox
{
/**
* @brief GeoLocation
*/
public $geoLocation;
/**
* Constructor
*
* @param \IPS\GeoLocation $geoLocation Location
* @return void
*/
public function __construct( \IPS\GeoLocation $geoLocation )
{
$this->geolocation = $geoLocation;
}
/**
* Render
*
* @param int $width Width
* @param int $height Height
* @param float|NULL $zoom The zoom amount (a value between 0 being totally zoomed out view of the world, and 1 being as fully zoomed in as possible) or NULL to zoom automatically based on how much data is available
* @return string
*/
public function render( $width, $height, $zoom=NULL )
{
if( !($this->geolocation->long and $this->geolocation->lat ) )
{
return "";
}
$location = $this->geolocation->long . ',' . $this->geolocation->lat;
$imageUrl = \IPS\Http\Url::external( "https://api.mapbox.com/styles/v1/mapbox/streets-v10/static/pin-l-marker+f00({$location})/{$location},14,0,60/{$width}x{$height}@2x" )->setQueryString( array(
'access_token' => \IPS\Settings::i()->mapbox_api_key,
) );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->staticMap( NULL, $imageUrl, $this->geolocation->lat, $this->geolocation->long );
}
}