This commit is contained in:
Javier Casares 2026-10-12 13:25:09 +00:00
commit e1f5a47c96
22 changed files with 3155 additions and 0 deletions

View file

@ -0,0 +1,122 @@
<?php
/**
* Admin settings page: Settings → Font del Bosc Combustible.
*
* @package FontdelboscCombustible
*/
declare( strict_types=1 );
defined( 'ABSPATH' ) || exit;
/**
* Registers the settings screen and the cache-flush action.
*/
final class Fontdelbosc_Combustible_Admin {
/**
* Wires the admin hooks.
*/
public static function init(): void {
add_action( 'admin_menu', array( self::class, 'add_menu' ) );
add_action( 'admin_init', array( self::class, 'register_settings' ) );
add_action( 'admin_post_fontdelbosc_combustible_flush_cache', array( self::class, 'handle_flush' ) );
}
/**
* Adds the options page under Settings.
*/
public static function add_menu(): void {
add_options_page(
__( 'Font del Bosc Combustible', 'fontdelbosc-combustible' ),
__( 'Font del Bosc Combustible', 'fontdelbosc-combustible' ),
'manage_options',
'fontdelbosc-combustible',
array( self::class, 'render_page' )
);
}
/**
* Registers the single options row.
*/
public static function register_settings(): void {
register_setting(
'fontdelbosc_combustible',
Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION,
array(
'type' => 'array',
'sanitize_callback' => array( self::class, 'sanitize' ),
'default' => Fontdelbosc_Combustible_Data_Manager::defaults(),
'show_in_rest' => false,
)
);
}
/**
* Sanitizes the settings form.
*
* @param mixed $input Raw input.
* @return array<string,mixed>
*/
public static function sanitize( mixed $input ): array {
$input = is_array( $input ) ? $input : array();
$fuels = array();
foreach ( Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $input, 'fuel_types' ) ) as $id ) {
$id = Fontdelbosc_Combustible_Payload::int( $id );
if ( null !== $id && $id > 0 ) {
$fuels[] = $id;
}
}
return array(
'search_radius_km' => max( 5, min( 50, Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $input, 'search_radius_km' ) ) ?? 15 ) ),
'max_stations' => max( 5, min( 20, Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $input, 'max_stations' ) ) ?? 10 ) ),
'fuel_types' => array() === $fuels ? Fontdelbosc_Combustible_Fuels::DEFAULT_IDS : array_values( array_unique( $fuels ) ),
'show_only_public' => ! empty( Fontdelbosc_Combustible_Payload::get( $input, 'show_only_public' ) ),
'cache_ttl' => max( 600, min( 86400, Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $input, 'cache_ttl' ) ) ?? 3600 ) ),
'show_distance' => ! empty( Fontdelbosc_Combustible_Payload::get( $input, 'show_distance' ) ),
'show_schedule' => ! empty( Fontdelbosc_Combustible_Payload::get( $input, 'show_schedule' ) ),
'sort_by' => ( Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $input, 'sort_by' ) ) === 'price_asc' ) ? 'price_asc' : 'distance',
'delete_data_on_uninstall' => ! empty( Fontdelbosc_Combustible_Payload::get( $input, 'delete_data_on_uninstall' ) ),
);
}
/**
* Renders the settings page.
*/
public static function render_page(): void {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
require FONTDELBOSC_COMBUSTIBLE_DIR . 'admin/views/settings-page.php';
}
/**
* Handles the flush-cache button.
*/
public static function handle_flush(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Insufficient permissions.', 'fontdelbosc-combustible' ) );
}
check_admin_referer( 'fontdelbosc_combustible_flush_cache' );
Fontdelbosc_Combustible_Data_Manager::flush_cache();
Fontdelbosc_Combustible_Data_Manager::get_payload( true );
wp_safe_redirect(
add_query_arg(
array(
'page' => 'fontdelbosc-combustible',
'fontdelbosc_flushed' => 1,
),
admin_url( 'options-general.php' )
)
);
exit;
}
}

View file

@ -0,0 +1,246 @@
<?php
/**
* Settings page view: Settings → Font del Bosc Combustible.
*
* @package FontdelboscCombustible
*/
declare( strict_types=1 );
defined( 'ABSPATH' ) || exit;
/**
* Settings screen data.
*
* @var array<string,mixed> $fontdelbosc_options Merged settings.
*/
$fontdelbosc_options = Fontdelbosc_Combustible_Data_Manager::options();
$fontdelbosc_products = Fontdelbosc_Combustible_Data_Manager::get_products();
$fontdelbosc_status = get_option( Fontdelbosc_Combustible_Data_Manager::STATUS_OPTION, array() );
$fontdelbosc_status = is_array( $fontdelbosc_status ) ? $fontdelbosc_status : array();
?>
<div class="wrap">
<h1><?php esc_html_e( 'Font del Bosc — Combustible', 'fontdelbosc-combustible' ); ?></h1>
<?php if ( isset( $_GET['fontdelbosc_flushed'] ) ) : ?>
<div class="notice notice-success is-dismissible">
<p><?php esc_html_e( 'Cache flushed and data refreshed.', 'fontdelbosc-combustible' ); ?></p>
</div>
<?php endif; ?>
<form method="post" action="options.php">
<?php settings_fields( 'fontdelbosc_combustible' ); ?>
<h2><?php esc_html_e( 'Search Settings', 'fontdelbosc-combustible' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><label for="fontdelbosc-radius"><?php esc_html_e( 'Search radius (km)', 'fontdelbosc-combustible' ); ?></label></th>
<td>
<input id="fontdelbosc-radius" type="range" min="5" max="50" step="1"
name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[search_radius_km]"
value="<?php echo esc_attr( (string) $fontdelbosc_options['search_radius_km'] ); ?>" />
<output id="fontdelbosc-radius-out" for="fontdelbosc-radius"><strong><?php echo esc_html( (string) $fontdelbosc_options['search_radius_km'] ); ?></strong> km</output>
</td>
</tr>
<tr>
<th scope="row"><label for="fontdelbosc-max"><?php esc_html_e( 'Max stations', 'fontdelbosc-combustible' ); ?></label></th>
<td>
<input id="fontdelbosc-max" type="number" min="5" max="20" step="1" class="small-text"
name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[max_stations]"
value="<?php echo esc_attr( (string) $fontdelbosc_options['max_stations'] ); ?>" />
</td>
</tr>
<tr>
<th scope="row"><label for="fontdelbosc-sort"><?php esc_html_e( 'Sort order', 'fontdelbosc-combustible' ); ?></label></th>
<td>
<select id="fontdelbosc-sort" name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[sort_by]">
<option value="distance" <?php selected( 'distance', (string) $fontdelbosc_options['sort_by'] ); ?>><?php esc_html_e( 'Nearest first', 'fontdelbosc-combustible' ); ?></option>
<option value="price_asc" <?php selected( 'price_asc', (string) $fontdelbosc_options['sort_by'] ); ?>><?php esc_html_e( 'Cheapest first', 'fontdelbosc-combustible' ); ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Public stations only', 'fontdelbosc-combustible' ); ?></th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[show_only_public]" <?php checked( ! empty( $fontdelbosc_options['show_only_public'] ) ); ?> />
<?php esc_html_e( 'Hide stations that do not sell to the general public.', 'fontdelbosc-combustible' ); ?>
</label>
</td>
</tr>
</table>
<h2><?php esc_html_e( 'Fuel Types', 'fontdelbosc-combustible' ); ?></h2>
<?php if ( array() === $fontdelbosc_products ) : ?>
<p><?php esc_html_e( 'The product catalog is temporarily unavailable; default fuels are used.', 'fontdelbosc-combustible' ); ?></p>
<?php endif; ?>
<fieldset>
<?php foreach ( $fontdelbosc_products as $fontdelbosc_product ) : ?>
<label style="display:inline-block;margin:0 12px 6px 0;">
<input type="checkbox"
name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[fuel_types][]"
value="<?php echo esc_attr( (string) $fontdelbosc_product['id'] ); ?>"
<?php checked( in_array( (int) $fontdelbosc_product['id'], array_map( 'intval', (array) $fontdelbosc_options['fuel_types'] ), true ) ); ?> />
<?php echo esc_html( Fontdelbosc_Combustible_Fuels::label( $fontdelbosc_product['name'] ) ); ?>
</label>
<?php endforeach; ?>
</fieldset>
<p class="description"><?php esc_html_e( 'Diesel B is agricultural diesel and is not available to the general public.', 'fontdelbosc-combustible' ); ?></p>
<h2><?php esc_html_e( 'Display Options', 'fontdelbosc-combustible' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><?php esc_html_e( 'Show distance', 'fontdelbosc-combustible' ); ?></th>
<td><label><input type="checkbox" name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[show_distance]" <?php checked( ! empty( $fontdelbosc_options['show_distance'] ) ); ?> /> <?php esc_html_e( 'Distance from Font del Bosc in each card.', 'fontdelbosc-combustible' ); ?></label></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Show schedule', 'fontdelbosc-combustible' ); ?></th>
<td><label><input type="checkbox" name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[show_schedule]" <?php checked( ! empty( $fontdelbosc_options['show_schedule'] ) ); ?> /> <?php esc_html_e( 'Station opening hours in each card.', 'fontdelbosc-combustible' ); ?></label></td>
</tr>
</table>
<p class="description"><?php esc_html_e( 'The data source attribution is mandatory and cannot be disabled; on the site it is shown collapsed.', 'fontdelbosc-combustible' ); ?></p>
<h2><?php esc_html_e( 'Cache', 'fontdelbosc-combustible' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><label for="fontdelbosc-ttl"><?php esc_html_e( 'Cache lifetime (seconds)', 'fontdelbosc-combustible' ); ?></label></th>
<td>
<input id="fontdelbosc-ttl" type="number" min="600" max="86400" step="60" class="regular-text"
name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[cache_ttl]"
value="<?php echo esc_attr( (string) $fontdelbosc_options['cache_ttl'] ); ?>" />
<p class="description"><?php esc_html_e( 'The source data changes at most once a day; one hour is a sensible value.', 'fontdelbosc-combustible' ); ?></p>
</td>
</tr>
</table>
<p>
<a class="button button-secondary" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=fontdelbosc_combustible_flush_cache' ), 'fontdelbosc_combustible_flush_cache' ) ); ?>">
<?php esc_html_e( 'Flush cache', 'fontdelbosc-combustible' ); ?>
</a>
</p>
<h2><?php esc_html_e( 'Data', 'fontdelbosc-combustible' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><?php esc_html_e( 'Last successful fetch', 'fontdelbosc-combustible' ); ?></th>
<td><?php echo esc_html( empty( $fontdelbosc_status['last_success'] ) ? __( 'Never.', 'fontdelbosc-combustible' ) : wp_date( 'Y-m-d H:i', (int) $fontdelbosc_status['last_success'] ) ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Last fetch duration', 'fontdelbosc-combustible' ); ?></th>
<td><?php echo esc_html( empty( $fontdelbosc_status['last_duration'] ) ? '—' : sprintf( '%s ms', number_format_i18n( (float) $fontdelbosc_status['last_duration'] * 1000 ) ) ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Current data date (from API)', 'fontdelbosc-combustible' ); ?></th>
<td><?php echo esc_html( empty( $fontdelbosc_status['last_data_date'] ) ? '—' : (string) $fontdelbosc_status['last_data_date'] ); ?></td>
</tr>
</table>
<h2><?php esc_html_e( 'Advanced', 'fontdelbosc-combustible' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><?php esc_html_e( 'Uninstall', 'fontdelbosc-combustible' ); ?></th>
<td><label><input type="checkbox" name="<?php echo esc_attr( Fontdelbosc_Combustible_Data_Manager::OPTIONS_OPTION ); ?>[delete_data_on_uninstall]" <?php checked( ! empty( $fontdelbosc_options['delete_data_on_uninstall'] ) ); ?> /> <?php esc_html_e( 'Delete all plugin data on uninstall.', 'fontdelbosc-combustible' ); ?></label></td>
</tr>
</table>
<?php submit_button(); ?>
</form>
<hr />
<h2><?php esc_html_e( 'Documentation', 'fontdelbosc-combustible' ); ?></h2>
<h3><?php esc_html_e( 'What it does', 'fontdelbosc-combustible' ); ?></h3>
<p><?php esc_html_e( 'Shows current fuel prices at the petrol stations nearest to Font del Bosc (Mediona, Alt Penedès), sorted by distance or by price, using the official Spanish Ministry open data API. No API key is needed.', 'fontdelbosc-combustible' ); ?></p>
<h3><?php esc_html_e( 'Shortcode', 'fontdelbosc-combustible' ); ?></h3>
<p><code>[fontdelbosc_combustible]</code> <?php esc_html_e( 'renders the station list using the settings defaults.', 'fontdelbosc-combustible' ); ?></p>
<table class="widefat striped" role="presentation">
<thead>
<tr>
<th><?php esc_html_e( 'Attribute', 'fontdelbosc-combustible' ); ?></th>
<th><?php esc_html_e( 'Default', 'fontdelbosc-combustible' ); ?></th>
<th><?php esc_html_e( 'Description', 'fontdelbosc-combustible' ); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>radius</code></td>
<td><?php esc_html_e( 'From settings', 'fontdelbosc-combustible' ); ?></td>
<td><?php esc_html_e( 'Search radius in kilometers (5-50).', 'fontdelbosc-combustible' ); ?></td>
</tr>
<tr>
<td><code>max</code></td>
<td><?php esc_html_e( 'From settings', 'fontdelbosc-combustible' ); ?></td>
<td><?php esc_html_e( 'Maximum number of stations to show (1-20).', 'fontdelbosc-combustible' ); ?></td>
</tr>
<tr>
<td><code>fuel</code></td>
<td><?php esc_html_e( 'From settings', 'fontdelbosc-combustible' ); ?></td>
<td><?php esc_html_e( 'Comma-separated product IDs (see the catalog below).', 'fontdelbosc-combustible' ); ?></td>
</tr>
<tr>
<td><code>sort</code></td>
<td><?php esc_html_e( 'From settings', 'fontdelbosc-combustible' ); ?></td>
<td><code>distance</code> / <code>price_asc</code> — <?php esc_html_e( 'with price_asc the cheapest price among the selected fuels wins.', 'fontdelbosc-combustible' ); ?></td>
</tr>
</tbody>
</table>
<p><?php esc_html_e( 'Examples:', 'fontdelbosc-combustible' ); ?></p>
<ul>
<li><code>[fontdelbosc_combustible radius="10" fuel="1,4" max="5"]</code> — <?php esc_html_e( 'five stations within 10 km, gasoline 95 E5 and diesel A only.', 'fontdelbosc-combustible' ); ?></li>
<li><code>[fontdelbosc_combustible sort="price_asc" fuel="1"]</code> — <?php esc_html_e( 'cheapest gasoline 95 E5 first.', 'fontdelbosc-combustible' ); ?></li>
</ul>
<h3><?php esc_html_e( 'Fuel catalog', 'fontdelbosc-combustible' ); ?></h3>
<p><?php esc_html_e( 'Product IDs come from the Ministry catalog and may change; the plugin refreshes it weekly. Use these IDs for the fuel attribute and the widget:', 'fontdelbosc-combustible' ); ?></p>
<?php if ( array() === $fontdelbosc_products ) : ?>
<p><em><?php esc_html_e( 'The product catalog is temporarily unavailable; default fuels are used.', 'fontdelbosc-combustible' ); ?></em></p>
<?php else : ?>
<table class="widefat striped" role="presentation">
<thead>
<tr>
<th><?php esc_html_e( 'ID', 'fontdelbosc-combustible' ); ?></th>
<th><?php esc_html_e( 'Product', 'fontdelbosc-combustible' ); ?></th>
<th><?php esc_html_e( 'Abbreviation', 'fontdelbosc-combustible' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $fontdelbosc_products as $fontdelbosc_item ) : ?>
<tr>
<td><?php echo esc_html( (string) $fontdelbosc_item['id'] ); ?></td>
<td><?php echo esc_html( Fontdelbosc_Combustible_Fuels::label( $fontdelbosc_item['name'] ) ); ?></td>
<td><?php echo esc_html( $fontdelbosc_item['abbr'] ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h3><?php esc_html_e( 'Widget', 'fontdelbosc-combustible' ); ?></h3>
<p><?php esc_html_e( 'The "Font del Bosc — Fuel Prices" widget shows the three nearest stations with the price of one fuel type and the distance. Pick the title and the fuel type in the widget settings.', 'fontdelbosc-combustible' ); ?></p>
<h3><?php esc_html_e( 'Data source and freshness', 'fontdelbosc-combustible' ); ?></h3>
<p><?php esc_html_e( 'Prices come from the MITECO / MINETUR open data API (ServiciosRestCarburantes), which petrol stations must report daily under Orden ITC/2308/2007. The timestamp shown in the list footer is the data date reported by the API itself, not the request time.', 'fontdelbosc-combustible' ); ?></p>
<p><?php esc_html_e( 'If the API is unreachable, the plugin keeps serving the last data for up to 25 hours with a "showing cached data" notice.', 'fontdelbosc-combustible' ); ?></p>
<p>
<a href="https://datos.gob.es/es/catalogo/e05068001-precio-de-carburantes-en-las-gasolineras-espanolas" rel="nofollow noopener" target="_blank"><?php esc_html_e( 'Open data catalog (datos.gob.es)', 'fontdelbosc-combustible' ); ?></a>
</p>
</div>
<script>
( function () {
'use strict';
var slider = document.getElementById( 'fontdelbosc-radius' );
var output = document.getElementById( 'fontdelbosc-radius-out' );
if ( ! slider || ! output ) {
return;
}
var sync = function () {
output.textContent = slider.value + ' km';
};
slider.addEventListener( 'input', sync );
sync();
} )();
</script>