489 lines
18 KiB
PHP
489 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Admin settings and analytics UI.
|
|
*
|
|
* @package ModernAudioPlayer
|
|
*/
|
|
|
|
namespace ModernAudioPlayer;
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
class Admin {
|
|
/**
|
|
* Register admin hooks.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register() {
|
|
add_action( 'admin_menu', array( $this, 'register_menus' ) );
|
|
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
|
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
|
add_action( 'admin_post_map_reset_design_settings', array( $this, 'handle_reset_design_settings' ) );
|
|
}
|
|
|
|
/**
|
|
* Register admin menu pages.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_menus() {
|
|
add_menu_page(
|
|
__( 'Velora Player', 'modern-audio-player' ),
|
|
__( 'Velora Player', 'modern-audio-player' ),
|
|
'manage_options',
|
|
'map-settings',
|
|
array( $this, 'render_settings_page' ),
|
|
'dashicons-format-audio',
|
|
56
|
|
);
|
|
|
|
add_submenu_page(
|
|
'map-settings',
|
|
__( 'Design Settings', 'modern-audio-player' ),
|
|
__( 'Design Settings', 'modern-audio-player' ),
|
|
'manage_options',
|
|
'map-settings',
|
|
array( $this, 'render_settings_page' )
|
|
);
|
|
|
|
add_submenu_page(
|
|
'map-settings',
|
|
__( 'Analytics', 'modern-audio-player' ),
|
|
__( 'Analytics', 'modern-audio-player' ),
|
|
'manage_options',
|
|
'map-analytics',
|
|
array( $this, 'render_analytics_page' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Register settings.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_settings() {
|
|
register_setting(
|
|
'map_settings_group',
|
|
MAP_OPTION_KEY,
|
|
array(
|
|
'type' => 'array',
|
|
'sanitize_callback' => array( '\ModernAudioPlayer\Settings', 'sanitize' ),
|
|
'default' => Settings::defaults(),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Enqueue admin design assets.
|
|
*
|
|
* @param string $hook_suffix Current screen hook.
|
|
* @return void
|
|
*/
|
|
public function enqueue_assets( $hook_suffix ) {
|
|
if ( 'toplevel_page_map-settings' !== $hook_suffix ) {
|
|
return;
|
|
}
|
|
|
|
wp_enqueue_style( 'wp-color-picker' );
|
|
wp_enqueue_style(
|
|
'map-admin-settings',
|
|
MAP_PLUGIN_URL . 'assets/css/admin-settings.css',
|
|
array( 'wp-color-picker' ),
|
|
MAP_VERSION
|
|
);
|
|
wp_enqueue_style(
|
|
'map-player-preview',
|
|
MAP_PLUGIN_URL . 'assets/css/player.css',
|
|
array(),
|
|
MAP_VERSION
|
|
);
|
|
|
|
wp_enqueue_script( 'wp-color-picker' );
|
|
wp_enqueue_script(
|
|
'map-admin-settings',
|
|
MAP_PLUGIN_URL . 'assets/js/admin-settings.js',
|
|
array( 'jquery', 'wp-color-picker' ),
|
|
MAP_VERSION,
|
|
true
|
|
);
|
|
|
|
$settings = Settings::get();
|
|
$resolved_design = Settings::get_resolved_design_settings( $settings['default_theme'], $settings );
|
|
$presets = Settings::get_design_presets();
|
|
$preset_tokens = array();
|
|
|
|
foreach ( $presets as $slug => $preset ) {
|
|
$preset_tokens[ $slug ] = $preset['tokens'];
|
|
}
|
|
|
|
wp_add_inline_script(
|
|
'map-admin-settings',
|
|
'window.mapAdminSettings = ' . wp_json_encode(
|
|
array(
|
|
'optionKey' => MAP_OPTION_KEY,
|
|
'presets' => $preset_tokens,
|
|
'resolvedDesign' => $resolved_design,
|
|
'i18n' => array(
|
|
'rangeValueLabel' => __( '%1$s %2$s', 'modern-audio-player' ),
|
|
'resetConfirm' => __( 'Reset all design settings back to the default preset values?', 'modern-audio-player' ),
|
|
),
|
|
)
|
|
) . ';',
|
|
'before'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handle reset action.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function handle_reset_design_settings() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_die( esc_html__( 'You are not allowed to perform this action.', 'modern-audio-player' ) );
|
|
}
|
|
|
|
check_admin_referer( 'map_reset_design_settings' );
|
|
|
|
update_option( MAP_OPTION_KEY, Settings::defaults() );
|
|
|
|
$redirect = add_query_arg(
|
|
array(
|
|
'page' => 'map-settings',
|
|
'map-reset' => 1,
|
|
),
|
|
admin_url( 'admin.php' )
|
|
);
|
|
|
|
wp_safe_redirect( $redirect );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Render settings page.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function render_settings_page() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
$settings = Settings::get();
|
|
$schema = Settings::get_design_schema();
|
|
$resolved_design = Settings::get_resolved_design_settings( $settings['default_theme'], $settings );
|
|
$preview_style = Settings::build_design_css_variables( $resolved_design );
|
|
$presets = Settings::get_design_presets();
|
|
$preview_preset = isset( $presets[ $settings['default_theme'] ] ) ? $presets[ $settings['default_theme'] ]['label'] : $presets[ Settings::get_default_preset_slug() ]['label'];
|
|
?>
|
|
<div class="wrap map-admin-page">
|
|
<h1><?php esc_html_e( 'Velora Player Design System', 'modern-audio-player' ); ?></h1>
|
|
<p class="map-admin-page__intro">
|
|
<?php esc_html_e( 'Manage the global player design tokens used by both the block renderer and shortcode output.', 'modern-audio-player' ); ?>
|
|
</p>
|
|
|
|
<?php if ( isset( $_GET['settings-updated'] ) ) : ?>
|
|
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Design settings saved.', 'modern-audio-player' ); ?></p></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( isset( $_GET['map-reset'] ) ) : ?>
|
|
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Design settings reset to the plugin defaults.', 'modern-audio-player' ); ?></p></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="map-admin-layout">
|
|
<form class="map-design-form" action="options.php" method="post">
|
|
<?php settings_fields( 'map_settings_group' ); ?>
|
|
|
|
<div class="map-settings-shell">
|
|
<div class="map-settings-shell__header">
|
|
<div class="map-settings-shell__meta">
|
|
<span class="map-settings-shell__eyebrow"><?php esc_html_e( 'Global Design Controls', 'modern-audio-player' ); ?></span>
|
|
<h2><?php esc_html_e( 'Design Backend', 'modern-audio-player' ); ?></h2>
|
|
<p><?php esc_html_e( 'Switch between sections, edit tokens quickly, and keep the preview visible while you work.', 'modern-audio-player' ); ?></p>
|
|
</div>
|
|
|
|
<div class="map-settings-actions map-settings-actions--header">
|
|
<?php submit_button( __( 'Save Design Settings', 'modern-audio-player' ), 'primary', 'submit', false ); ?>
|
|
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=map_reset_design_settings' ), 'map_reset_design_settings' ) ); ?>" class="button button-secondary map-reset-button">
|
|
<?php esc_html_e( 'Reset to Defaults', 'modern-audio-player' ); ?>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<h2 class="nav-tab-wrapper wp-clearfix">
|
|
<a href="#map-section-presets" class="nav-tab nav-tab-active" data-map-tab="presets"><?php esc_html_e( 'Presets', 'modern-audio-player' ); ?></a>
|
|
<a href="#map-section-colors" class="nav-tab" data-map-tab="colors"><?php esc_html_e( 'Colors', 'modern-audio-player' ); ?></a>
|
|
<a href="#map-section-text" class="nav-tab" data-map-tab="text"><?php esc_html_e( 'Text', 'modern-audio-player' ); ?></a>
|
|
<a href="#map-section-layout" class="nav-tab" data-map-tab="layout"><?php esc_html_e( 'Layout', 'modern-audio-player' ); ?></a>
|
|
<a href="#map-section-controls" class="nav-tab" data-map-tab="controls"><?php esc_html_e( 'Controls', 'modern-audio-player' ); ?></a>
|
|
</h2>
|
|
|
|
<div class="map-settings-panels">
|
|
<?php foreach ( $schema as $section_key => $section ) : ?>
|
|
<section id="map-section-<?php echo esc_attr( $section_key ); ?>" class="map-settings-card map-settings-panel<?php echo 'presets' === $section_key ? ' is-active' : ''; ?>" data-map-panel="<?php echo esc_attr( $section_key ); ?>">
|
|
<div class="map-settings-card__header">
|
|
<h2><?php echo esc_html( $section['title'] ); ?></h2>
|
|
<p><?php echo esc_html( $section['description'] ); ?></p>
|
|
</div>
|
|
|
|
<div class="map-field-grid">
|
|
<?php foreach ( $section['fields'] as $field_key => $field ) : ?>
|
|
<div class="map-field-card map-field-card--<?php echo esc_attr( $field['type'] ); ?>">
|
|
<div class="map-field-card__top">
|
|
<label class="map-field-card__label" for="<?php echo esc_attr( $field_key ); ?>"><?php echo esc_html( $field['label'] ); ?></label>
|
|
<?php $this->render_field( $field_key, $field, $settings, $resolved_design ); ?>
|
|
</div>
|
|
<?php if ( ! empty( $field['description'] ) ) : ?>
|
|
<p class="description"><?php echo esc_html( $field['description'] ); ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</section>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="map-settings-actions map-settings-actions--footer">
|
|
<?php submit_button( __( 'Save Design Settings', 'modern-audio-player' ), 'primary', 'submit', false ); ?>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<aside class="map-preview-panel">
|
|
<div class="map-settings-card map-settings-card--sticky">
|
|
<div class="map-settings-card__header">
|
|
<h2><?php esc_html_e( 'Live Preview', 'modern-audio-player' ); ?></h2>
|
|
<p><?php esc_html_e( 'This static preview reflects the current saved values and updates as you edit the form.', 'modern-audio-player' ); ?></p>
|
|
</div>
|
|
|
|
<div class="map-admin-preview-shell">
|
|
<div class="map-player map-theme-<?php echo esc_attr( $settings['default_theme'] ); ?><?php echo empty( $resolved_design['player_show_cover_image'] ) ? ' map-player--no-cover' : ''; ?>" data-map-admin-preview data-map-has-cover="<?php echo empty( $resolved_design['player_show_cover_image'] ) ? 'false' : 'true'; ?>" style="<?php echo esc_attr( $preview_style ); ?>">
|
|
<div class="map-player__cover-wrap"<?php echo empty( $resolved_design['player_show_cover_image'] ) ? ' style="display:none;"' : ''; ?>>
|
|
<div class="map-admin-preview-cover"></div>
|
|
</div>
|
|
<div class="map-player__content">
|
|
<div class="map-player__header">
|
|
<div class="map-player__eyebrow" data-map-preview-theme<?php echo empty( $resolved_design['player_show_theme_label'] ) ? ' style="display:none;"' : ''; ?>><?php echo esc_html( $preview_preset ); ?></div>
|
|
<div class="map-player__title"><?php esc_html_e( 'Velora Weekly Mix', 'modern-audio-player' ); ?></div>
|
|
</div>
|
|
<div class="map-player__controls">
|
|
<div class="map-player__actions">
|
|
<button type="button" class="map-player__toggle" aria-pressed="false">
|
|
<span class="map-player__toggle-icon map-player__toggle-icon--play" aria-hidden="true"></span>
|
|
<span class="map-player__toggle-text"><?php esc_html_e( 'Play', 'modern-audio-player' ); ?></span>
|
|
</button>
|
|
<div class="map-player__transport">
|
|
<button type="button" class="map-player__icon-button" disabled>
|
|
<span class="map-player__icon map-player__icon--previous" aria-hidden="true"></span>
|
|
</button>
|
|
<button type="button" class="map-player__icon-button">
|
|
<span class="map-player__icon map-player__icon--next" aria-hidden="true"></span>
|
|
</button>
|
|
<button type="button" class="map-player__icon-button">
|
|
<span class="map-player__icon map-player__icon--repeat" aria-hidden="true"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="map-player__timeline">
|
|
<input type="range" class="map-player__progress" min="0" max="100" value="38" step="0.1" />
|
|
<div class="map-player__time">
|
|
<span><?php esc_html_e( '1:12', 'modern-audio-player' ); ?></span>
|
|
<span><?php esc_html_e( '3:08', 'modern-audio-player' ); ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render a field based on schema metadata.
|
|
*
|
|
* @param string $field_key Field key.
|
|
* @param array<string, mixed> $field Field definition.
|
|
* @param array<string, mixed> $settings Saved settings.
|
|
* @param array<string, mixed> $resolved_design Resolved design values.
|
|
* @return void
|
|
*/
|
|
private function render_field( $field_key, $field, $settings, $resolved_design ) {
|
|
$option_name = MAP_OPTION_KEY . '[' . $field_key . ']';
|
|
$value = isset( $settings[ $field_key ] ) && '' !== $settings[ $field_key ] ? $settings[ $field_key ] : $resolved_design[ $field_key ];
|
|
$override = isset( $settings[ $field_key ] ) && '' !== $settings[ $field_key ];
|
|
|
|
switch ( $field['type'] ) {
|
|
case 'color':
|
|
?>
|
|
<input
|
|
type="text"
|
|
id="<?php echo esc_attr( $field_key ); ?>"
|
|
class="map-color-field"
|
|
name="<?php echo esc_attr( $option_name ); ?>"
|
|
value="<?php echo esc_attr( (string) $value ); ?>"
|
|
data-map-field="<?php echo esc_attr( $field_key ); ?>"
|
|
data-default-color="<?php echo esc_attr( (string) $resolved_design[ $field_key ] ); ?>"
|
|
/>
|
|
<?php
|
|
break;
|
|
|
|
case 'range':
|
|
?>
|
|
<div class="map-range-control">
|
|
<input
|
|
type="range"
|
|
id="<?php echo esc_attr( $field_key ); ?>"
|
|
name="<?php echo esc_attr( $option_name ); ?>"
|
|
value="<?php echo esc_attr( (string) $value ); ?>"
|
|
min="<?php echo esc_attr( (string) $field['min'] ); ?>"
|
|
max="<?php echo esc_attr( (string) $field['max'] ); ?>"
|
|
step="<?php echo esc_attr( (string) $field['step'] ); ?>"
|
|
data-map-field="<?php echo esc_attr( $field_key ); ?>"
|
|
data-unit="<?php echo esc_attr( (string) $field['unit'] ); ?>"
|
|
/>
|
|
<span class="map-range-control__value" data-map-range-value="<?php echo esc_attr( $field_key ); ?>">
|
|
<?php echo esc_html( (string) $value . (string) $field['unit'] ); ?>
|
|
</span>
|
|
</div>
|
|
<?php
|
|
break;
|
|
|
|
case 'checkbox':
|
|
?>
|
|
<label class="map-switch" for="<?php echo esc_attr( $field_key ); ?>">
|
|
<input
|
|
type="checkbox"
|
|
id="<?php echo esc_attr( $field_key ); ?>"
|
|
name="<?php echo esc_attr( $option_name ); ?>"
|
|
value="1"
|
|
data-map-field="<?php echo esc_attr( $field_key ); ?>"
|
|
<?php checked( ! empty( $value ) ); ?>
|
|
/>
|
|
<span class="map-switch__track" aria-hidden="true"></span>
|
|
<span class="map-switch__label"><?php esc_html_e( 'Enabled', 'modern-audio-player' ); ?></span>
|
|
</label>
|
|
<?php
|
|
break;
|
|
|
|
case 'checkbox_group':
|
|
?>
|
|
<fieldset class="map-checkbox-group">
|
|
<?php foreach ( $field['options'] as $option_value => $option_label ) : ?>
|
|
<label>
|
|
<input
|
|
type="checkbox"
|
|
name="<?php echo esc_attr( $option_name ); ?>[]"
|
|
value="<?php echo esc_attr( $option_value ); ?>"
|
|
<?php checked( in_array( $option_value, (array) $settings[ $field_key ], true ) ); ?>
|
|
/>
|
|
<span><?php echo esc_html( $option_label ); ?></span>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</fieldset>
|
|
<?php
|
|
break;
|
|
|
|
case 'select':
|
|
?>
|
|
<select
|
|
id="<?php echo esc_attr( $field_key ); ?>"
|
|
name="<?php echo esc_attr( $option_name ); ?>"
|
|
class="map-select-control"
|
|
data-map-preset-select="true"
|
|
>
|
|
<?php foreach ( $field['options'] as $option_value => $option_label ) : ?>
|
|
<option value="<?php echo esc_attr( $option_value ); ?>" <?php selected( $settings[ $field_key ], $option_value ); ?>>
|
|
<?php echo esc_html( $option_label ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<p class="description"><?php esc_html_e( 'Selecting a preset updates the visible field values immediately. Saved customizations remain layered over the chosen preset.', 'modern-audio-player' ); ?></p>
|
|
<?php
|
|
break;
|
|
}
|
|
|
|
if ( 0 === strpos( $field_key, 'player_' ) ) {
|
|
?>
|
|
<p class="map-field-state">
|
|
<?php
|
|
echo $override
|
|
? esc_html__( 'Custom override saved for this token.', 'modern-audio-player' )
|
|
: esc_html__( 'Currently inherited from the selected preset.', 'modern-audio-player' );
|
|
?>
|
|
</p>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render analytics page.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function render_analytics_page() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
$summary = Analytics::get_summary();
|
|
$tracks = Analytics::get_top_tracks( 50 );
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php esc_html_e( 'Velora Player Analytics', 'modern-audio-player' ); ?></h1>
|
|
<div style="display:flex;gap:16px;flex-wrap:wrap;margin:20px 0;">
|
|
<div class="postbox" style="padding:16px;min-width:180px;">
|
|
<strong><?php esc_html_e( 'Tracked Sources', 'modern-audio-player' ); ?></strong>
|
|
<div style="font-size:24px;"><?php echo esc_html( number_format_i18n( $summary['track_count'] ) ); ?></div>
|
|
</div>
|
|
<div class="postbox" style="padding:16px;min-width:180px;">
|
|
<strong><?php esc_html_e( 'Total Plays', 'modern-audio-player' ); ?></strong>
|
|
<div style="font-size:24px;"><?php echo esc_html( number_format_i18n( $summary['total_plays'] ) ); ?></div>
|
|
</div>
|
|
<div class="postbox" style="padding:16px;min-width:220px;">
|
|
<strong><?php esc_html_e( 'Last Played', 'modern-audio-player' ); ?></strong>
|
|
<div style="font-size:16px;">
|
|
<?php echo $summary['last_played'] ? esc_html( get_date_from_gmt( $summary['last_played'], get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ) : esc_html__( 'No data yet', 'modern-audio-player' ); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Track', 'modern-audio-player' ); ?></th>
|
|
<th><?php esc_html_e( 'Audio Source', 'modern-audio-player' ); ?></th>
|
|
<th><?php esc_html_e( 'Plays', 'modern-audio-player' ); ?></th>
|
|
<th><?php esc_html_e( 'Last Played', 'modern-audio-player' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if ( empty( $tracks ) ) : ?>
|
|
<tr>
|
|
<td colspan="4"><?php esc_html_e( 'No analytics recorded yet.', 'modern-audio-player' ); ?></td>
|
|
</tr>
|
|
<?php else : ?>
|
|
<?php foreach ( $tracks as $track ) : ?>
|
|
<tr>
|
|
<td><?php echo esc_html( $track->track_title ? $track->track_title : __( 'Untitled Track', 'modern-audio-player' ) ); ?></td>
|
|
<td>
|
|
<a href="<?php echo esc_url( $track->audio_src ); ?>" target="_blank" rel="noopener noreferrer">
|
|
<?php echo esc_html( $track->audio_src ); ?>
|
|
</a>
|
|
</td>
|
|
<td><?php echo esc_html( number_format_i18n( (int) $track->play_count ) ); ?></td>
|
|
<td><?php echo $track->last_played ? esc_html( get_date_from_gmt( $track->last_played, get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ) : esc_html__( 'Never', 'modern-audio-player' ); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|