OpenStreetMap contributors · Protomaps';
/**
* The attribution the first version stored with its raster default.
*/
const LEGACY_TILE_ATTRIBUTION = '© OpenStreetMap contributors';
/**
* Default global settings.
*
* @return array
*/
public static function defaults() {
return array(
'license_key' => '',
// Placeholder: the production API host is not live yet. Pro features need a reachable API.
'api_url' => 'https://api.reachzone.app',
// Empty = the built-in basemap (see tile_url()). The public OpenStreetMap raster servers are not for
// production plugin traffic, so no OSM URL is the default here; a shop may still enter its own raster URL.
'tile_url' => '',
'tile_attribution' => '',
'msg_in' => '',
'msg_out' => '',
'msg_unverified' => '',
'msg_min_order' => '',
'unverified_policy' => 'block', // block|allow: what to do when the address cannot be placed on the map.
'keep_pickup' => 0,
'demand_map' => 0, // Opt-in: send rounded out-of-area points to the ReachZone service (Business plan).
'default_country' => '',
// Where the visitor's IP comes from for the rate limit: '' (the connection), 'cloudflare' or 'xff'.
'trusted_proxy' => '',
);
}
/**
* Default message templates (translatable, so they are resolved at read time, not stored).
*
* @return array
*/
public static function default_messages() {
return array(
'msg_in' => __( 'We deliver here: {eta}, {fee}', 'reachzone' ),
'msg_out' => __( 'Sorry, this address is outside our delivery area.', 'reachzone' ),
'msg_unverified' => __( 'We could not check your address against our delivery area. Please use the delivery checker on our site (address or postcode) before checking out.', 'reachzone' ),
/* translators: 1: minimum order amount, 2: amount still missing, 3: zone name */
'msg_min_order' => __( 'The minimum order for delivery to {zone} is {min}. Add {missing} more to your cart to continue.', 'reachzone' ),
);
}
/**
* Global settings merged over the defaults.
*
* @return array
*/
public static function get() {
$stored = get_option( self::OPTION_SETTINGS, array() );
$settings = array_merge( self::defaults(), is_array( $stored ) ? $stored : array() );
// Resolve the basemap: empty or the retired first default means the built-in vector basemap.
$url = trim( (string) $settings['tile_url'] );
if ( '' === $url || self::LEGACY_TILE_URL === $url ) {
$url = self::tile_url();
}
$attribution = trim( (string) $settings['tile_attribution'] );
if ( '' === $attribution || self::LEGACY_TILE_ATTRIBUTION === $attribution ) {
$attribution = self::DEFAULT_TILE_ATTRIBUTION;
}
$settings['tile_url'] = $url;
$settings['tile_attribution'] = $attribution;
return $settings;
}
/**
* The built-in basemap URL. The filter `reachzone_tiles_url` lets a site (or the local test setup, with
* http://localhost:8787/planet/{z}/{x}/{y}.mvt) point the default at another tile service.
*
* @return string
*/
public static function tile_url() {
$url = apply_filters( 'reachzone_tiles_url', self::DEFAULT_TILE_URL );
return is_string( $url ) && '' !== $url ? $url : self::DEFAULT_TILE_URL;
}
/**
* Which kind of tiles a URL template serves: 'vector' (Protomaps .mvt/.pbf tiles, drawn by protomaps-leaflet) or
* 'raster' (images such as .png, drawn by Leaflet's tile layer).
*
* @param string $url Tile URL template.
* @return string vector|raster.
*/
public static function tile_mode( $url ) {
$path = (string) wp_parse_url( (string) $url, PHP_URL_PATH );
return preg_match( '/\.(mvt|pbf)$/i', $path ) ? 'vector' : 'raster';
}
/**
* A message template, falling back to the translatable default when the shop did not customise it.
*
* @param string $key msg_in|msg_out|msg_unverified|msg_min_order.
* @return string
*/
public static function message( $key ) {
$settings = self::get();
if ( isset( $settings[ $key ] ) && '' !== trim( (string) $settings[ $key ] ) ) {
return (string) $settings[ $key ];
}
$defaults = self::default_messages();
return isset( $defaults[ $key ] ) ? $defaults[ $key ] : '';
}
/**
* Locations with their zones.
*
* @return array[]
*/
public static function locations() {
$stored = get_option( self::OPTION_LOCATIONS, array() );
return is_array( $stored ) ? array_values( $stored ) : array();
}
/**
* All zones across locations as one flat list, each carrying its location's coordinates.
*
* @param array[]|null $locations Locations, or null to read the option.
* @return array[]
*/
public static function flat_zones( $locations = null ) {
$locations = null === $locations ? self::locations() : $locations;
$flat = array();
foreach ( $locations as $location ) {
foreach ( isset( $location['zones'] ) ? (array) $location['zones'] : array() as $zone ) {
$zone['location_id'] = isset( $location['id'] ) ? $location['id'] : '';
$zone['location_name'] = isset( $location['name'] ) ? $location['name'] : '';
$zone['center_lat'] = isset( $location['lat'] ) ? (float) $location['lat'] : null;
$zone['center_lon'] = isset( $location['lon'] ) ? (float) $location['lon'] : null;
// A drive-time zone is active only with a stored polygon.
$zone['active'] = ! isset( $zone['type'] ) || Zones::TYPE_DRIVE_TIME !== $zone['type'] || ! empty( $zone['geojson'] );
$flat[] = $zone;
}
}
return $flat;
}
/**
* Does the shop have any zone configured?
*
* @return bool
*/
public static function has_zones() {
return count( self::flat_zones() ) > 0;
}
}