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; } }