<?php
/**
* Admin UI.
*
* @package TonItaliaRegistration
*/
defined( 'ABSPATH' ) || exit;
/**
* Class TON_Reg_Admin
*/
class TON_Reg_Admin {
/**
* Register hooks.
*/
public static function register() {
add_action( 'admin_menu', array( __CLASS__, 'menu' ) );
add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
add_action( 'admin_post_ton_reg_save_registration', array( __CLASS__, 'save_registration' ) );
add_action( 'admin_post_ton_reg_gdpr_export', array( __CLASS__, 'gdpr_export' ) );
add_action( 'admin_post_ton_reg_gdpr_anonymize', array( __CLASS__, 'gdpr_anonymize' ) );
add_action( 'admin_post_ton_reg_gdpr_erase', array( __CLASS__, 'gdpr_erase' ) );
add_action( 'admin_post_ton_reg_upload_document', array( __CLASS__, 'upload_document' ) );
add_action( 'admin_post_ton_reg_delete_document', array( __CLASS__, 'delete_document' ) );
add_action( 'admin_post_ton_reg_mailchimp_test', array( __CLASS__, 'mailchimp_test' ) );
add_action( 'admin_post_ton_reg_mailchimp_retry', array( __CLASS__, 'mailchimp_retry' ) );
add_action( 'admin_post_ton_reg_save_card_settings', array( __CLASS__, 'save_card_settings' ) );
add_action( 'admin_post_ton_reg_upload_card_template', array( __CLASS__, 'upload_card_template' ) );
add_action( 'admin_post_ton_reg_batch_cards', array( __CLASS__, 'batch_cards' ) );
add_action( 'admin_post_ton_reg_generate_card', array( __CLASS__, 'generate_card' ) );
add_action( 'admin_post_ton_reg_export_libro_soci', array( __CLASS__, 'export_libro_soci' ) );
}
/**
* @param string $hook_suffix Current admin page hook.
*/
public static function enqueue_assets( $hook_suffix ) {
$allowed = array(
'toplevel_page_ton-registrations',
'ton-registrations_page_ton-reg-settings',
);
if ( ! in_array( $hook_suffix, $allowed, true ) ) {
return;
}
wp_enqueue_style(
'ton-reg-admin',
TON_REG_PLUGIN_URL . 'assets/css/admin.css',
array(),
TON_REG_VERSION
);
}
/**
* Admin menu.
*/
public static function menu() {
add_menu_page(
__( 'TON Iscrizioni', 'ton-italia-registration' ),
__( 'TON Iscrizioni', 'ton-italia-registration' ),
'manage_options',
'ton-registrations',
array( __CLASS__, 'page_list' ),
'dashicons-groups',
58
);
add_submenu_page(
'ton-registrations',
__( 'Elenco iscrizioni', 'ton-italia-registration' ),
__( 'Elenco', 'ton-italia-registration' ),
'manage_options',
'ton-registrations',
array( __CLASS__, 'page_list' )
);
add_submenu_page(
'ton-registrations',
__( 'Impostazioni', 'ton-italia-registration' ),
__( 'Impostazioni', 'ton-italia-registration' ),
'manage_options',
'ton-reg-settings',
array( __CLASS__, 'page_settings' )
);
}
/**
* Register settings.
*/
public static function register_settings() {
$checkboxes = array(
'ton_reg_captcha_enabled',
'ton_reg_newsletter_required',
'ton_reg_delete_users_on_uninstall',
'ton_reg_mailchimp_enabled',
);
$plain_text = array(
'ton_reg_social_year',
'ton_reg_renewal_reminders_start_year',
'ton_reg_membership_fee',
'ton_reg_beneficiary',
'ton_reg_iban',
'ton_reg_admin_email',
'ton_reg_gdpr_contact',
'ton_reg_email_admin_subject',
'ton_reg_email_user_subject',
'ton_reg_email_renewal_first_subject',
'ton_reg_email_renewal_second_subject',
'ton_reg_email_renewal_admin_subject',
'ton_reg_rate_limit_max',
'ton_reg_rate_limit_window',
'ton_reg_privacy_version',
'ton_reg_mailchimp_tags',
);
$skip_auto = array(
'ton_reg_mailchimp_api_key',
'ton_reg_mailchimp_list_id',
'ton_reg_mailchimp_permission_ids',
'ton_reg_success_page_id',
);
register_setting(
'ton_reg_settings',
'ton_reg_mailchimp_api_key',
array(
'type' => 'string',
'sanitize_callback' => array( 'TON_Reg_Mailchimp', 'sanitize_api_key' ),
'show_in_rest' => false,
'default' => '',
)
);
register_setting(
'ton_reg_settings',
'ton_reg_mailchimp_list_id',
array(
'type' => 'string',
'sanitize_callback' => array( 'TON_Reg_Mailchimp', 'sanitize_list_id' ),
'show_in_rest' => false,
'default' => '',
)
);
register_setting(
'ton_reg_settings',
'ton_reg_success_page_id',
array(
'type' => 'integer',
'sanitize_callback' => array( __CLASS__, 'sanitize_success_page_id' ),
'show_in_rest' => false,
'default' => 0,
)
);
$options = array_keys( TON_Reg_Defaults::options() );
foreach ( $options as $option ) {
if ( in_array( $option, $skip_auto, true ) ) {
continue;
}
if ( in_array( $option, $checkboxes, true ) ) {
$sanitize = array( __CLASS__, 'sanitize_checkbox' );
} elseif ( in_array( $option, $plain_text, true ) ) {
$sanitize = 'sanitize_text_field';
} else {
$sanitize = array( __CLASS__, 'sanitize_setting' );
}
register_setting(
'ton_reg_settings',
$option,
array(
'type' => 'string',
'sanitize_callback' => $sanitize,
)
);
}
}
/**
* @param mixed $value Value.
* @return string
*/
public static function sanitize_setting( $value ) {
if ( is_array( $value ) ) {
return '';
}
return wp_kses_post( wp_unslash( (string) $value ) );
}
/**
* @param string $value Checkbox value.
* @return string
*/
public static function sanitize_checkbox( $value ) {
return '1' === (string) $value ? '1' : '0';
}
/**
* @param mixed $value Page ID.
* @return int
*/
public static function sanitize_success_page_id( $value ) {
$page_id = absint( $value );
if ( ! $page_id ) {
return 0;
}
if ( ! TON_Reg_Registration_Handler::is_publicly_viewable_page( $page_id ) ) {
return 0;
}
return $page_id;
}
/**
* List page.
*/
public static function page_list() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
if ( isset( $_GET['action'], $_GET['id'] ) && 'view' === $_GET['action'] ) {
self::page_detail( (int) $_GET['id'] );
return;
}
$table = new TON_Reg_Registration_List_Table();
$table->prepare_items();
$status = isset( $_GET['status'] ) ? sanitize_text_field( wp_unslash( $_GET['status'] ) ) : '';
$renewal_unpaid = ! empty( $_GET['renewal_unpaid'] );
?>
<div class="wrap ton-reg-admin-list">
<h1><?php esc_html_e( 'Iscrizioni socio', 'ton-italia-registration' ); ?></h1>
<?php if ( isset( $_GET['updated'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Salvato.', 'ton-italia-registration' ); ?></p></div>
<?php endif; ?>
<ul class="subsubsub ton-reg-admin-list__filters">
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=ton-registrations' ) ); ?>"><?php esc_html_e( 'Tutti', 'ton-italia-registration' ); ?></a> |</li>
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=ton-registrations&status=pending' ) ); ?>"><?php esc_html_e( 'In attesa', 'ton-italia-registration' ); ?></a> |</li>
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=ton-registrations&status=admitted' ) ); ?>"><?php esc_html_e( 'Ammessi', 'ton-italia-registration' ); ?></a> |</li>
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=ton-registrations&status=rejected' ) ); ?>"><?php esc_html_e( 'Non ammessi', 'ton-italia-registration' ); ?></a> |</li>
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=ton-registrations&status=admitted&renewal_unpaid=1' ) ); ?>"><?php esc_html_e( 'Quota da pagare', 'ton-italia-registration' ); ?></a></li>
</ul>
<p class="ton-reg-admin-list__export">
<a class="button button-secondary" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_export_libro_soci' ), 'ton_reg_export_libro_soci' ) ); ?>">
<?php esc_html_e( 'Esporta CSV Libro Soci', 'ton-italia-registration' ); ?>
</a>
<span class="description"><?php esc_html_e( 'Soci ammessi, esclusi anonimizzati.', 'ton-italia-registration' ); ?></span>
</p>
<form method="get" class="ton-reg-list-search">
<input type="hidden" name="page" value="ton-registrations" />
<?php if ( $status ) : ?>
<input type="hidden" name="status" value="<?php echo esc_attr( $status ); ?>" />
<?php endif; ?>
<?php if ( $renewal_unpaid ) : ?>
<input type="hidden" name="renewal_unpaid" value="1" />
<?php endif; ?>
<p class="search-box ton-reg-admin-list__search">
<label class="screen-reader-text" for="ton-reg-search"><?php esc_html_e( 'Cerca', 'ton-italia-registration' ); ?></label>
<input type="search" id="ton-reg-search" name="s" value="<?php echo isset( $_GET['s'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['s'] ) ) ) : ''; ?>" placeholder="<?php esc_attr_e( 'Cognome, nome, email…', 'ton-italia-registration' ); ?>" />
<input type="submit" class="button" value="<?php esc_attr_e( 'Cerca', 'ton-italia-registration' ); ?>" />
</p>
</form>
<div class="ton-reg-list-table">
<?php $table->display(); ?>
</div>
</div>
<?php
}
/**
* @param int $id Registration ID.
*/
public static function page_detail( $id ) {
$row = TON_Reg_Database::get( $id );
if ( ! $row ) {
echo '<div class="wrap"><p>' . esc_html__( 'Non trovato.', 'ton-italia-registration' ) . '</p></div>';
return;
}
$list_context = self::get_detail_list_context();
$adjacent = TON_Reg_Database::get_adjacent_ids( $id, $list_context );
?>
<div class="wrap ton-reg-admin-detail">
<h1><?php printf( esc_html__( 'Iscrizione #%d', 'ton-italia-registration' ), (int) $id ); ?></h1>
<?php self::render_detail_navigation( $id, $row, $adjacent, $list_context ); ?>
<?php self::render_admin_notices(); ?>
<h2><?php esc_html_e( 'Documenti', 'ton-italia-registration' ); ?></h2>
<p class="description">
<?php
printf(
/* translators: %s: folder path under uploads */
esc_html__( 'I file vengono salvati in Media, cartella «%s», sottocartella «%s».', 'ton-italia-registration' ),
esc_html( TON_Reg_Documents::BASE_FOLDER ),
esc_html( TON_Reg_Documents::member_folder_slug( $row ) )
);
?>
</p>
<?php foreach ( TON_Reg_Documents::get_types() as $doc_type ) : ?>
<?php self::render_document_block( $id, $row, $doc_type ); ?>
<?php endforeach; ?>
<?php self::render_membership_cards_section( $id, $row ); ?>
<?php self::render_consents_section( $row ); ?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="ton_reg_save_registration" />
<input type="hidden" name="registration_id" value="<?php echo (int) $id; ?>" />
<?php wp_nonce_field( 'ton_reg_save_' . $id, 'ton_reg_admin_nonce' ); ?>
<?php self::render_detail_list_context_fields( $list_context ); ?>
<?php self::render_renewals_section( $id, $row ); ?>
<table class="form-table">
<?php
$fields = array(
'cognome', 'nome', 'luogo_nascita', 'provincia_nascita', 'data_nascita', 'codice_fiscale',
'comune_residenza', 'provincia_residenza', 'indirizzo', 'numero_civico', 'cap', 'telefono', 'email',
'registration_ip', 'user_agent', 'created_at',
);
foreach ( $fields as $field ) :
?>
<tr>
<th><?php echo esc_html( $field ); ?></th>
<td><?php echo esc_html( $row->$field ?? '' ); ?></td>
</tr>
<?php endforeach; ?>
<tr>
<th><?php esc_html_e( 'Stato', 'ton-italia-registration' ); ?></th>
<td>
<select name="status">
<option value="pending" <?php selected( $row->status, 'pending' ); ?>><?php esc_html_e( 'In attesa', 'ton-italia-registration' ); ?></option>
<option value="admitted" <?php selected( $row->status, 'admitted' ); ?>><?php esc_html_e( 'Ammesso', 'ton-italia-registration' ); ?></option>
<option value="rejected" <?php selected( $row->status, 'rejected' ); ?>><?php esc_html_e( 'Non ammesso', 'ton-italia-registration' ); ?></option>
</select>
</td>
</tr>
<tr>
<th><?php esc_html_e( 'Data Libro Associati', 'ton-italia-registration' ); ?></th>
<td><input type="date" name="libro_associati_date" value="<?php echo esc_attr( $row->libro_associati_date ?? '' ); ?>" /></td>
</tr>
<tr>
<th><?php esc_html_e( 'Note admin', 'ton-italia-registration' ); ?></th>
<td><textarea name="admin_notes" rows="4" class="large-text"><?php echo esc_textarea( $row->admin_notes ?? '' ); ?></textarea></td>
</tr>
</table>
<?php submit_button( __( 'Salva', 'ton-italia-registration' ) ); ?>
</form>
<h2><?php esc_html_e( 'GDPR', 'ton-italia-registration' ); ?></h2>
<p>
<a class="button" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_gdpr_export&id=' . $id . '&format=json' ), 'ton_reg_gdpr_' . $id ) ); ?>"><?php esc_html_e( 'Esporta JSON', 'ton-italia-registration' ); ?></a>
<a class="button" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_gdpr_export&id=' . $id . '&format=csv' ), 'ton_reg_gdpr_' . $id ) ); ?>"><?php esc_html_e( 'Esporta CSV', 'ton-italia-registration' ); ?></a>
<?php if ( ! $row->anonymized_at ) : ?>
<a class="button" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_gdpr_anonymize&id=' . $id ), 'ton_reg_gdpr_' . $id ) ); ?>" onclick="return confirm('<?php esc_attr_e( 'Anonimizzare i dati?', 'ton-italia-registration' ); ?>');"><?php esc_html_e( 'Anonimizza', 'ton-italia-registration' ); ?></a>
<?php endif; ?>
<a class="button button-link-delete" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_gdpr_erase&id=' . $id ), 'ton_reg_gdpr_' . $id ) ); ?>" onclick="return confirm('<?php esc_attr_e( 'Eliminare definitivamente iscrizione e utente WP?', 'ton-italia-registration' ); ?>');"><?php esc_html_e( 'Elimina definitivamente', 'ton-italia-registration' ); ?></a>
</p>
</div>
<?php
}
/**
* @return array<string,mixed>
*/
public static function get_detail_list_context() {
if ( ! empty( $_POST['ton_reg_list_context'] ) && is_array( $_POST['ton_reg_list_context'] ) ) {
$source = wp_unslash( $_POST['ton_reg_list_context'] );
} else {
$source = wp_unslash( $_GET );
}
$orderby = sanitize_text_field( wp_unslash( $source['list_orderby'] ?? $source['orderby'] ?? 'created_at' ) );
$order = sanitize_text_field( wp_unslash( $source['list_order'] ?? $source['order'] ?? 'DESC' ) );
$allowed_orderby = array( 'id', 'created_at', 'cognome', 'nome', 'email', 'status' );
return array(
'status' => sanitize_text_field( wp_unslash( $source['list_status'] ?? $source['status'] ?? '' ) ),
'search' => sanitize_text_field( wp_unslash( $source['list_search'] ?? $source['s'] ?? '' ) ),
'renewal_unpaid' => ! empty( $source['list_renewal_unpaid'] ?? $source['renewal_unpaid'] ?? '' ),
'renewal_year' => TON_Reg_Membership_Renewals::current_year(),
'orderby' => in_array( $orderby, $allowed_orderby, true ) ? $orderby : 'created_at',
'order' => 'ASC' === strtoupper( $order ) ? 'ASC' : 'DESC',
);
}
/**
* @param int $id Registration ID.
* @param array<string,mixed> $context List context.
* @return string
*/
public static function detail_url( $id, $context = array() ) {
$args = array(
'page' => 'ton-registrations',
'action' => 'view',
'id' => (int) $id,
);
if ( ! empty( $context['status'] ) ) {
$args['list_status'] = $context['status'];
}
if ( ! empty( $context['search'] ) ) {
$args['list_search'] = $context['search'];
}
if ( ! empty( $context['renewal_unpaid'] ) ) {
$args['list_renewal_unpaid'] = '1';
}
if ( ! empty( $context['orderby'] ) && 'created_at' !== $context['orderby'] ) {
$args['list_orderby'] = $context['orderby'];
}
if ( ! empty( $context['order'] ) && 'DESC' !== strtoupper( (string) $context['order'] ) ) {
$args['list_order'] = $context['order'];
}
return add_query_arg( $args, admin_url( 'admin.php' ) );
}
/**
* @param array<string,mixed> $context List context.
* @return string
*/
public static function list_url( $context = array() ) {
$args = array(
'page' => 'ton-registrations',
);
if ( ! empty( $context['status'] ) ) {
$args['status'] = $context['status'];
}
if ( ! empty( $context['search'] ) ) {
$args['s'] = $context['search'];
}
if ( ! empty( $context['renewal_unpaid'] ) ) {
$args['renewal_unpaid'] = '1';
}
if ( ! empty( $context['orderby'] ) && 'created_at' !== $context['orderby'] ) {
$args['orderby'] = $context['orderby'];
}
if ( ! empty( $context['order'] ) && 'DESC' !== strtoupper( (string) $context['order'] ) ) {
$args['order'] = $context['order'];
}
return add_query_arg( $args, admin_url( 'admin.php' ) );
}
/**
* @param int $id Registration ID.
* @param object $row Registration row.
* @param array{prev:int|null,next:int|null} $adjacent Adjacent IDs.
* @param array<string,mixed> $context List context.
*/
private static function render_detail_navigation( $id, $row, $adjacent, $context ) {
$name = trim( ( $row->cognome ?? '' ) . ' ' . ( $row->nome ?? '' ) );
?>
<div class="ton-reg-detail-nav">
<p class="ton-reg-detail-nav__back">
<a href="<?php echo esc_url( self::list_url( $context ) ); ?>">← <?php esc_html_e( 'Torna all\'elenco', 'ton-italia-registration' ); ?></a>
</p>
<div class="ton-reg-detail-nav__pager">
<?php if ( ! empty( $adjacent['prev'] ) ) : ?>
<a class="button" href="<?php echo esc_url( self::detail_url( (int) $adjacent['prev'], $context ) ); ?>">← <?php esc_html_e( 'Socio precedente', 'ton-italia-registration' ); ?></a>
<?php else : ?>
<span class="button disabled" aria-disabled="true">← <?php esc_html_e( 'Socio precedente', 'ton-italia-registration' ); ?></span>
<?php endif; ?>
<span class="ton-reg-detail-nav__current"><?php echo esc_html( $name ); ?></span>
<?php if ( ! empty( $adjacent['next'] ) ) : ?>
<a class="button" href="<?php echo esc_url( self::detail_url( (int) $adjacent['next'], $context ) ); ?>"><?php esc_html_e( 'Socio successivo', 'ton-italia-registration' ); ?> →</a>
<?php else : ?>
<span class="button disabled" aria-disabled="true"><?php esc_html_e( 'Socio successivo', 'ton-italia-registration' ); ?> →</span>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* @param array<string,mixed> $context List context.
*/
private static function render_detail_list_context_fields( $context ) {
$fields = array(
'list_status' => $context['status'] ?? '',
'list_search' => $context['search'] ?? '',
'list_renewal_unpaid' => ! empty( $context['renewal_unpaid'] ) ? '1' : '',
'list_orderby' => $context['orderby'] ?? 'created_at',
'list_order' => $context['order'] ?? 'DESC',
);
foreach ( $fields as $key => $value ) {
if ( '' === (string) $value ) {
continue;
}
printf(
'<input type="hidden" name="ton_reg_list_context[%s]" value="%s" />',
esc_attr( $key ),
esc_attr( (string) $value )
);
}
}
/**
* @param int $id Registration ID.
* @param object $row Registration row.
*/
private static function render_renewals_section( $id, $row ) {
if ( 'admitted' !== ( $row->status ?? '' ) || ! empty( $row->anonymized_at ) ) {
return;
}
$year_range = TON_Reg_Membership_Renewals::get_checkbox_year_range( $row );
$records = TON_Reg_Membership_Renewals::get_by_registration( $id );
$paid_map = array();
foreach ( $records as $record ) {
if ( TON_Reg_Membership_Renewals::STATUS_PAID === (string) $record->status ) {
$paid_map[ (int) $record->year ] = true;
}
}
?>
<h2><?php esc_html_e( 'Quote associative annuali', 'ton-italia-registration' ); ?></h2>
<p class="description"><?php esc_html_e( 'Segna manualmente il pagamento della quota per ogni anno solare (1 gennaio – 31 dicembre). Viene mostrato solo l\'anno in corso e gli anni precedenti dall\'iscrizione.', 'ton-italia-registration' ); ?></p>
<table class="form-table ton-reg-renewals">
<?php for ( $year = $year_range['start']; $year <= $year_range['end']; $year++ ) : ?>
<tr>
<th scope="row">
<?php
printf(
/* translators: %d: calendar year */
esc_html__( 'Quota %d', 'ton-italia-registration' ),
(int) $year
);
?>
</th>
<td>
<label>
<input type="checkbox" name="ton_reg_renewal_paid[<?php echo (int) $year; ?>]" value="1" <?php checked( ! empty( $paid_map[ $year ] ) ); ?> />
<?php esc_html_e( 'Pagata', 'ton-italia-registration' ); ?>
</label>
</td>
</tr>
<?php endfor; ?>
</table>
<?php
}
/**
* @param int $id Registration ID.
* @param object $row Registration row before save.
*/
private static function save_renewal_checkboxes( $id, $row ) {
$new_status = sanitize_text_field( wp_unslash( $_POST['status'] ?? '' ) );
if ( 'admitted' !== $new_status ) {
return;
}
$year_range = TON_Reg_Membership_Renewals::get_checkbox_year_range( $row );
$posted = isset( $_POST['ton_reg_renewal_paid'] ) && is_array( $_POST['ton_reg_renewal_paid'] )
? wp_unslash( $_POST['ton_reg_renewal_paid'] )
: array();
$user_id = get_current_user_id();
for ( $year = $year_range['start']; $year <= $year_range['end']; $year++ ) {
$paid = ! empty( $posted[ $year ] );
TON_Reg_Membership_Renewals::set_paid( $id, $year, $paid, $user_id );
}
}
/**
* @param object $row Registration row.
*/
private static function render_consents_section( $row ) {
?>
<h2><?php esc_html_e( 'Consensi e dichiarazioni', 'ton-italia-registration' ); ?></h2>
<p class="description"><?php esc_html_e( 'Il richiedente ha espresso i consensi spuntando le caselle nel modulo pubblico (sostitutivo della firma).', 'ton-italia-registration' ); ?></p>
<table class="form-table ton-reg-consents">
<tr>
<th scope="row"><?php esc_html_e( 'Dichiarazione statuto e regolamenti', 'ton-italia-registration' ); ?></th>
<td>
<?php self::render_consent_status( ! empty( $row->consenso_statuto ) ); ?>
<?php if ( ! empty( $row->consent_statuto_at ) ) : ?>
<p class="description"><?php echo esc_html( self::format_consent_datetime( $row->consent_statuto_at ) ); ?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Privacy e trattamento dati personali', 'ton-italia-registration' ); ?></th>
<td>
<?php self::render_consent_status( ! empty( $row->consenso_privacy ) ); ?>
<?php if ( ! empty( $row->consent_privacy_at ) ) : ?>
<p class="description"><?php echo esc_html( self::format_consent_datetime( $row->consent_privacy_at ) ); ?></p>
<?php endif; ?>
<?php if ( ! empty( $row->privacy_text_version ) ) : ?>
<p class="description">
<?php
printf(
/* translators: %s: privacy text version id */
esc_html__( 'Versione informativa al momento del consenso: %s', 'ton-italia-registration' ),
esc_html( $row->privacy_text_version )
);
?>
</p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Consenso newsletter', 'ton-italia-registration' ); ?></th>
<td>
<?php if ( ! empty( $row->consenso_newsletter ) ) : ?>
<?php self::render_consent_status( true ); ?>
<?php self::render_mailchimp_sync_status( $row ); ?>
<?php else : ?>
<span class="ton-reg-consent ton-reg-consent--neutral"><?php esc_html_e( 'Non espresso / non richiesto', 'ton-italia-registration' ); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Bonifico effettuato (dichiarato)', 'ton-italia-registration' ); ?></th>
<td>
<?php self::render_consent_status( ! empty( $row->bonifico_effettuato ), false ); ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Luogo e data dichiarazione', 'ton-italia-registration' ); ?></th>
<td>
<?php
echo esc_html(
trim(
( $row->luogo_dichiarazione ?? '' ) . ( ! empty( $row->data_dichiarazione ) ? ', ' . self::format_date_display( $row->data_dichiarazione ) : '' )
)
);
?>
</td>
</tr>
</table>
<?php
}
/**
* @param bool $accepted Whether consent was given.
* @param bool $is_consent Whether this is a legal consent (vs operational flag).
*/
private static function render_consent_status( $accepted, $is_consent = true ) {
if ( $accepted ) {
$class = 'ton-reg-consent ton-reg-consent--yes';
$text = $is_consent
? __( 'Sì — accettato tramite checkbox nel form', 'ton-italia-registration' )
: __( 'Sì — indicato tramite checkbox nel form', 'ton-italia-registration' );
} else {
$class = 'ton-reg-consent ton-reg-consent--no';
$text = $is_consent
? __( 'No — non risulta accettato', 'ton-italia-registration' )
: __( 'No — non indicato', 'ton-italia-registration' );
}
echo '<span class="' . esc_attr( $class ) . '">' . esc_html( $text ) . '</span>';
}
/**
* @param object $row Registration row.
*/
private static function render_mailchimp_sync_status( $row ) {
$status = isset( $row->mailchimp_status ) ? (string) $row->mailchimp_status : '';
if ( '' === $status ) {
if ( TON_Reg_Mailchimp::is_configured() ) {
echo '<p class="description">' . esc_html__( 'Mailchimp: in attesa di sincronizzazione.', 'ton-italia-registration' ) . '</p>';
}
return;
}
$labels = array(
'pending' => __( 'Mailchimp: sincronizzazione in corso', 'ton-italia-registration' ),
'synced' => __( 'Mailchimp: iscritto correttamente', 'ton-italia-registration' ),
'error' => __( 'Mailchimp: errore di sincronizzazione', 'ton-italia-registration' ),
'skipped' => __( 'Mailchimp: non sincronizzato (integrazione disattiva)', 'ton-italia-registration' ),
);
$label = $labels[ $status ] ?? $status;
echo '<p class="description"><strong>' . esc_html( $label ) . '</strong></p>';
if ( ! empty( $row->mailchimp_synced_at ) && 'synced' === $status ) {
echo '<p class="description">' . esc_html( self::format_consent_datetime( $row->mailchimp_synced_at ) ) . '</p>';
}
if ( ! empty( $row->mailchimp_error ) && 'error' === $status ) {
echo '<p class="description">' . esc_html( $row->mailchimp_error ) . '</p>';
}
if ( TON_Reg_Mailchimp::is_configured() && in_array( $status, array( 'error', 'pending' ), true ) ) {
$retry_url = wp_nonce_url(
admin_url( 'admin-post.php?action=ton_reg_mailchimp_retry&id=' . (int) $row->id ),
'ton_reg_mc_retry_' . (int) $row->id
);
echo '<p><a class="button button-small" href="' . esc_url( $retry_url ) . '">' . esc_html__( 'Riprova iscrizione Mailchimp', 'ton-italia-registration' ) . '</a></p>';
}
}
/**
* @param string $datetime UTC datetime from DB.
* @return string
*/
private static function format_consent_datetime( $datetime ) {
$ts = strtotime( $datetime . ' UTC' );
if ( ! $ts ) {
return $datetime;
}
return sprintf(
/* translators: %s: formatted date and time */
__( 'Registrato il %s', 'ton-italia-registration' ),
wp_date( 'd/m/Y H:i', $ts )
);
}
/**
* @param string $date Date Y-m-d.
* @return string
*/
private static function format_date_display( $date ) {
$ts = strtotime( $date );
return $ts ? wp_date( 'd/m/Y', $ts ) : $date;
}
/**
* Admin notices for document upload / errors.
*/
private static function render_admin_notices() {
if ( isset( $_GET['doc_ok'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Documento caricato.', 'ton-italia-registration' ) . '</p></div>';
}
if ( isset( $_GET['doc_deleted'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Documento eliminato.', 'ton-italia-registration' ) . '</p></div>';
}
if ( ! empty( $_GET['doc_error'] ) ) {
$msg = sanitize_text_field( wp_unslash( rawurldecode( $_GET['doc_error'] ) ) );
echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
}
if ( isset( $_GET['mc_ok'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Sincronizzazione Mailchimp completata.', 'ton-italia-registration' ) . '</p></div>';
}
if ( ! empty( $_GET['mc_error'] ) ) {
$msg = sanitize_text_field( wp_unslash( rawurldecode( $_GET['mc_error'] ) ) );
echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
}
if ( isset( $_GET['card_generated'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Tessera generata.', 'ton-italia-registration' ) . '</p></div>';
}
if ( ! empty( $_GET['card_error'] ) ) {
$msg = sanitize_text_field( wp_unslash( rawurldecode( $_GET['card_error'] ) ) );
echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
}
}
/**
* @param int $id Registration ID.
* @param object $row Registration row.
* @param string $type Document type.
*/
private static function render_document_block( $id, $row, $type ) {
$column = TON_Reg_Documents::get_column( $type );
$file = $column ? TON_Reg_Documents::get_file_info( (int) ( $row->$column ?? 0 ) ) : null;
?>
<div class="ton-reg-doc-block">
<h3><?php echo esc_html( TON_Reg_Documents::type_label( $type ) ); ?></h3>
<?php if ( $file ) : ?>
<p class="ton-reg-doc-block__file">
<a href="<?php echo esc_url( $file['url'] ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $file['name'] ); ?></a>
<a class="button button-small" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=ton_reg_delete_document®istration_id=' . $id . '&document_type=' . $type ), 'ton_reg_del_doc_' . $id . '_' . $type ) ); ?>" onclick="return confirm('<?php esc_attr_e( 'Eliminare questo documento?', 'ton-italia-registration' ); ?>');"><?php esc_html_e( 'Elimina', 'ton-italia-registration' ); ?></a>
</p>
<?php else : ?>
<p class="description"><?php esc_html_e( 'Nessun file caricato.', 'ton-italia-registration' ); ?></p>
<?php endif; ?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data" class="ton-reg-doc-block__upload">
<input type="hidden" name="action" value="ton_reg_upload_document" />
<input type="hidden" name="registration_id" value="<?php echo (int) $id; ?>" />
<input type="hidden" name="document_type" value="<?php echo esc_attr( $type ); ?>" />
<?php wp_nonce_field( 'ton_reg_doc_' . $id . '_' . $type, 'ton_reg_doc_nonce' ); ?>
<input type="file" name="ton_reg_document" accept=".pdf,.jpg,.jpeg,.png,application/pdf,image/jpeg,image/png" <?php echo $file ? '' : 'required'; ?> />
<?php submit_button( $file ? __( 'Sostituisci file', 'ton-italia-registration' ) : __( 'Carica file', 'ton-italia-registration' ), 'secondary', 'submit', false ); ?>
</form>
</div>
<?php
}
/**
* Upload member document.
*/
public static function upload_document() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
$id = isset( $_POST['registration_id'] ) ? (int) $_POST['registration_id'] : 0;
$type = isset( $_POST['document_type'] ) ? sanitize_text_field( wp_unslash( $_POST['document_type'] ) ) : '';
check_admin_referer( 'ton_reg_doc_' . $id . '_' . $type, 'ton_reg_doc_nonce' );
$file = isset( $_FILES['ton_reg_document'] ) ? $_FILES['ton_reg_document'] : null;
$result = TON_Reg_Documents::upload( $id, $type, $file );
$redirect = admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id );
if ( is_wp_error( $result ) ) {
wp_safe_redirect(
add_query_arg(
array( 'doc_error' => rawurlencode( $result->get_error_message() ) ),
$redirect
)
);
exit;
}
wp_safe_redirect( add_query_arg( 'doc_ok', '1', $redirect ) );
exit;
}
/**
* Delete member document.
*/
public static function delete_document() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
$id = isset( $_GET['registration_id'] ) ? (int) $_GET['registration_id'] : 0;
$type = isset( $_GET['document_type'] ) ? sanitize_text_field( wp_unslash( $_GET['document_type'] ) ) : '';
check_admin_referer( 'ton_reg_del_doc_' . $id . '_' . $type );
TON_Reg_Documents::delete_document( $id, $type );
wp_safe_redirect(
add_query_arg(
'doc_deleted',
'1',
admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id )
)
);
exit;
}
/**
* Settings page.
*/
public static function page_settings() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1><?php esc_html_e( 'Impostazioni TON Iscrizioni', 'ton-italia-registration' ); ?></h1>
<?php if ( isset( $_GET['mc_test_ok'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Connessione Mailchimp riuscita.', 'ton-italia-registration' ); ?></p></div>
<?php endif; ?>
<?php if ( ! empty( $_GET['mc_test_error'] ) ) : ?>
<div class="notice notice-error is-dismissible"><p><?php echo esc_html( sanitize_text_field( wp_unslash( rawurldecode( $_GET['mc_test_error'] ) ) ) ); ?></p></div>
<?php endif; ?>
<?php if ( isset( $_GET['card_settings_saved'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Impostazioni tessera salvate.', 'ton-italia-registration' ); ?></p></div>
<?php endif; ?>
<?php if ( isset( $_GET['card_template_ok'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Template PDF caricato.', 'ton-italia-registration' ); ?></p></div>
<?php endif; ?>
<?php if ( ! empty( $_GET['card_batch_done'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p><?php echo esc_html( self::format_batch_report_message() ); ?></p></div>
<?php endif; ?>
<?php if ( ! empty( $_GET['card_error'] ) ) : ?>
<div class="notice notice-error is-dismissible"><p><?php echo esc_html( sanitize_text_field( wp_unslash( rawurldecode( $_GET['card_error'] ) ) ) ); ?></p></div>
<?php endif; ?>
<div class="ton-reg-shortcode-box">
<h2 class="ton-reg-shortcode-box__title"><?php esc_html_e( 'Profilo socio e tessera', 'ton-italia-registration' ); ?></h2>
<p class="ton-reg-shortcode-box__text">
<?php esc_html_e( 'Ogni socio ha un account WordPress (ruolo Subscriber). Dopo l’accesso può aprire il profilo nativo per consultare lo stato iscrizione e scaricare la tessera PDF.', 'ton-italia-registration' ); ?>
</p>
<p class="ton-reg-shortcode-box__code">
<a href="<?php echo esc_url( TON_Reg_Profile::profile_url() ); ?>"><?php echo esc_html( TON_Reg_Profile::profile_url() ); ?></a>
</p>
<p class="description ton-reg-shortcode-box__hint">
<?php esc_html_e( 'Consigliato: aggiungi al menu del sito una voce «Il mio profilo» con URL personalizzato sopra (visibile solo agli utenti loggati).', 'ton-italia-registration' ); ?>
</p>
<p class="description ton-reg-shortcode-box__hint">
<?php esc_html_e( 'Lo shortcode [ton_registration_profile] resta disponibile per retrocompatibilità, ma il profilo WordPress è l’approccio consigliato.', 'ton-italia-registration' ); ?>
</p>
</div>
<div class="ton-reg-shortcode-box">
<h2 class="ton-reg-shortcode-box__title"><?php esc_html_e( 'Pagina di conferma iscrizione', 'ton-italia-registration' ); ?></h2>
<p class="ton-reg-shortcode-box__text">
<?php esc_html_e( 'Crea una pagina pubblica (es. «Iscrizione completata»), incolla lo shortcode sotto e selezionala in «Pagina di conferma» più avanti. Dopo un invio riuscito l’utente verrà reindirizzato lì.', 'ton-italia-registration' ); ?>
</p>
<p class="ton-reg-shortcode-box__code"><code>[ton_registration_success]</code></p>
</div>
<div class="ton-reg-shortcode-box">
<h2 class="ton-reg-shortcode-box__title"><?php esc_html_e( 'Inserire il form in una pagina', 'ton-italia-registration' ); ?></h2>
<p class="ton-reg-shortcode-box__text">
<?php esc_html_e( 'Crea o modifica una pagina WordPress (es. «Iscrizione socio»), poi incolla lo shortcode nel contenuto — in un blocco «Shortcode» dell’editor o direttamente nel testo. Pubblica la pagina: il modulo di iscrizione apparirà automaticamente.', 'ton-italia-registration' ); ?>
</p>
<p class="ton-reg-shortcode-box__code"><code>[ton_registration_form]</code></p>
<p class="description ton-reg-shortcode-box__hint">
<?php esc_html_e( 'Opzionale: aggiungi una classe CSS al contenitore, ad esempio', 'ton-italia-registration' ); ?>
<code>[ton_registration_form class="mia-classe"]</code>
</p>
</div>
<form method="post" action="options.php">
<?php
settings_fields( 'ton_reg_settings' );
$textareas = array(
'ton_reg_success_message' => __( 'Messaggio successo', 'ton-italia-registration' ),
'ton_reg_payment_instructions' => __( 'Istruzioni bonifico', 'ton-italia-registration' ),
'ton_reg_statuto_declaration' => __( 'Dichiarazione statuto (checkbox 1)', 'ton-italia-registration' ),
'ton_reg_privacy_notice' => __( 'Informativa privacy', 'ton-italia-registration' ),
'ton_reg_newsletter_label' => __( 'Label consenso newsletter', 'ton-italia-registration' ),
'ton_reg_minor_notice' => __( 'Avviso minorenni', 'ton-italia-registration' ),
'ton_reg_email_admin_body' => __( 'Email admin (testo)', 'ton-italia-registration' ),
'ton_reg_email_user_body' => __( 'Email utente (testo)', 'ton-italia-registration' ),
'ton_reg_email_renewal_first_body' => __( 'Email rinnovo — primo promemoria (1 gen)', 'ton-italia-registration' ),
'ton_reg_email_renewal_second_body' => __( 'Email rinnovo — secondo promemoria (25 gen)', 'ton-italia-registration' ),
'ton_reg_email_renewal_admin_body' => __( 'Email admin rinnovi (riepilogo)', 'ton-italia-registration' ),
);
$textarea_hints = array(
'ton_reg_success_message' => __( 'Testo mostrato dallo shortcode [ton_registration_success] dopo l\'invio. HTML consentito (es. <p>...</p>).', 'ton-italia-registration' ),
);
?>
<table class="form-table">
<tr>
<th><label for="ton_reg_success_page_id"><?php esc_html_e( 'Pagina di conferma', 'ton-italia-registration' ); ?></label></th>
<td>
<?php
wp_dropdown_pages(
array(
'name' => 'ton_reg_success_page_id',
'id' => 'ton_reg_success_page_id',
'selected' => (int) get_option( 'ton_reg_success_page_id', 0 ),
'show_option_none' => __( '— Stessa pagina del form (fallback) —', 'ton-italia-registration' ),
'option_none_value' => '0',
'post_status' => array( 'publish' ),
)
);
?>
<p class="description"><?php esc_html_e( 'Pagina pubblica con [ton_registration_success]. Obbligatoria per il redirect dedicato.', 'ton-italia-registration' ); ?></p>
</td>
</tr>
<tr><th><?php esc_html_e( 'Anno sociale', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_social_year" value="<?php echo esc_attr( get_option( 'ton_reg_social_year' ) ); ?>" /></td></tr>
<tr>
<th><?php esc_html_e( 'Anno inizio promemoria rinnovo', 'ton-italia-registration' ); ?></th>
<td>
<input name="ton_reg_renewal_reminders_start_year" type="number" min="2000" max="2100" value="<?php echo esc_attr( get_option( 'ton_reg_renewal_reminders_start_year', '2027' ) ); ?>" />
<p class="description"><?php esc_html_e( 'Nessuna email automatica di rinnovo viene inviata prima del 1 gennaio di questo anno (es. 2027 = primi invii il 1/1/2027).', 'ton-italia-registration' ); ?></p>
</td>
</tr>
<tr><th><?php esc_html_e( 'Quota €', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_membership_fee" value="<?php echo esc_attr( get_option( 'ton_reg_membership_fee' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Beneficiario', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_beneficiary" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_beneficiary' ) ); ?>" /></td></tr>
<tr><th>IBAN</th><td><input name="ton_reg_iban" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_iban' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Email admin', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_admin_email" type="email" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_admin_email' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Contatto GDPR', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_gdpr_contact" type="email" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_gdpr_contact' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'CAPTCHA attivo', 'ton-italia-registration' ); ?></th><td><input type="hidden" name="ton_reg_captcha_enabled" value="0" /><input type="checkbox" name="ton_reg_captcha_enabled" value="1" <?php checked( get_option( 'ton_reg_captcha_enabled' ), '1' ); ?> /></td></tr>
<tr><th><?php esc_html_e( 'Newsletter obbligatoria', 'ton-italia-registration' ); ?></th><td><input type="hidden" name="ton_reg_newsletter_required" value="0" /><input type="checkbox" name="ton_reg_newsletter_required" value="1" <?php checked( get_option( 'ton_reg_newsletter_required' ), '1' ); ?> /></td></tr>
<tr><th><?php esc_html_e( 'Elimina utenti alla disinstallazione', 'ton-italia-registration' ); ?></th><td><input type="hidden" name="ton_reg_delete_users_on_uninstall" value="0" /><input type="checkbox" name="ton_reg_delete_users_on_uninstall" value="1" <?php checked( get_option( 'ton_reg_delete_users_on_uninstall' ), '1' ); ?> /></td></tr>
</table>
<h2><?php esc_html_e( 'Integrazione Mailchimp', 'ton-italia-registration' ); ?></h2>
<p class="description"><?php esc_html_e( 'Iscrive automaticamente a Mailchimp i richiedenti che spuntano il consenso newsletter. L\'iscrizione socio non viene mai bloccata da errori Mailchimp.', 'ton-italia-registration' ); ?></p>
<table class="form-table">
<tr>
<th><?php esc_html_e( 'Integrazione attiva', 'ton-italia-registration' ); ?></th>
<td><input type="hidden" name="ton_reg_mailchimp_enabled" value="0" /><input type="checkbox" name="ton_reg_mailchimp_enabled" value="1" <?php checked( get_option( 'ton_reg_mailchimp_enabled' ), '1' ); ?> /></td>
</tr>
<tr>
<th><label for="ton_reg_mailchimp_api_key"><?php esc_html_e( 'API key', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="password" id="ton_reg_mailchimp_api_key" name="ton_reg_mailchimp_api_key" class="regular-text" value="" autocomplete="new-password" placeholder="<?php echo esc_attr( get_option( 'ton_reg_mailchimp_api_key' ) ? '••••••••••••' : '' ); ?>" />
<p class="description"><?php esc_html_e( 'Mailchimp → Account → Extras → API keys. Lasciare vuoto per non modificare la chiave salvata.', 'ton-italia-registration' ); ?></p>
</td>
</tr>
<tr>
<th><label for="ton_reg_mailchimp_list_id"><?php esc_html_e( 'List ID (Audience)', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="text" id="ton_reg_mailchimp_list_id" name="ton_reg_mailchimp_list_id" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_mailchimp_list_id' ) ); ?>" />
<p class="description"><?php esc_html_e( 'Audience → Settings → Audience name and defaults → Audience ID.', 'ton-italia-registration' ); ?></p>
</td>
</tr>
<tr>
<th><label for="ton_reg_mailchimp_tags"><?php esc_html_e( 'Tag', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="text" id="ton_reg_mailchimp_tags" name="ton_reg_mailchimp_tags" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_mailchimp_tags', 'Socio TON ITALIA' ) ); ?>" />
<p class="description"><?php esc_html_e( 'Separati da virgola (max 5).', 'ton-italia-registration' ); ?></p>
</td>
</tr>
</table>
<table class="form-table">
<tr><th><?php esc_html_e( 'Rate limit (max tentativi)', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_rate_limit_max" type="number" min="1" value="<?php echo esc_attr( get_option( 'ton_reg_rate_limit_max', '5' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Finestra rate limit (secondi)', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_rate_limit_window" type="number" min="60" value="<?php echo esc_attr( get_option( 'ton_reg_rate_limit_window', '900' ) ); ?>" /></td></tr>
<?php foreach ( $textareas as $key => $label ) : ?>
<tr>
<th><?php echo esc_html( $label ); ?></th>
<td>
<textarea name="<?php echo esc_attr( $key ); ?>" rows="6" class="large-text"><?php echo esc_textarea( get_option( $key, TON_Reg_Defaults::options()[ $key ] ?? '' ) ); ?></textarea>
<?php if ( isset( $textarea_hints[ $key ] ) ) : ?>
<p class="description"><?php echo esc_html( $textarea_hints[ $key ] ); ?></p>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<tr><th><?php esc_html_e( 'Oggetto email admin', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_email_admin_subject" class="large-text" value="<?php echo esc_attr( get_option( 'ton_reg_email_admin_subject' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Oggetto email utente', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_email_user_subject" class="large-text" value="<?php echo esc_attr( get_option( 'ton_reg_email_user_subject' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Oggetto primo promemoria rinnovo', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_email_renewal_first_subject" class="large-text" value="<?php echo esc_attr( get_option( 'ton_reg_email_renewal_first_subject', TON_Reg_Defaults::options()['ton_reg_email_renewal_first_subject'] ?? '' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Oggetto secondo promemoria rinnovo', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_email_renewal_second_subject" class="large-text" value="<?php echo esc_attr( get_option( 'ton_reg_email_renewal_second_subject', TON_Reg_Defaults::options()['ton_reg_email_renewal_second_subject'] ?? '' ) ); ?>" /></td></tr>
<tr><th><?php esc_html_e( 'Oggetto email admin rinnovi', 'ton-italia-registration' ); ?></th><td><input name="ton_reg_email_renewal_admin_subject" class="large-text" value="<?php echo esc_attr( get_option( 'ton_reg_email_renewal_admin_subject', TON_Reg_Defaults::options()['ton_reg_email_renewal_admin_subject'] ?? '' ) ); ?>" /></td></tr>
</table>
<p class="description"><?php esc_html_e( 'Placeholder email iscrizione: {nome}, {cognome}, {email}, {codice_fiscale}, {ip}, {login_url}, {profile_url}, {gdpr_contact}, {admin_url}', 'ton-italia-registration' ); ?></p>
<p class="description"><?php esc_html_e( 'Placeholder email rinnovo: {nome}, {cognome}, {email}, {anno}, {quota}, {beneficiario}, {iban}, {payment_instructions}, {profile_url}, {gdpr_contact}. Admin rinnovi: {lista_soci}, {numero_soci}, {anno}, {admin_url}.', 'ton-italia-registration' ); ?></p>
<p class="description"><?php esc_html_e( 'I promemoria rinnovo vengono inviati automaticamente il 1 gennaio (primo promemoria) e il 25 gennaio (sollecito) agli ammessi senza quota pagata per l\'anno in corso.', 'ton-italia-registration' ); ?></p>
<?php submit_button(); ?>
</form>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="ton-reg-mailchimp-test-form">
<input type="hidden" name="action" value="ton_reg_mailchimp_test" />
<?php wp_nonce_field( 'ton_reg_mc_test', 'ton_reg_mc_test_nonce' ); ?>
<h2><?php esc_html_e( 'Test connessione Mailchimp', 'ton-italia-registration' ); ?></h2>
<p class="description"><?php esc_html_e( 'Verifica API key e List ID senza salvare le altre impostazioni. Lascia vuota la API key per usare quella già salvata.', 'ton-italia-registration' ); ?></p>
<table class="form-table">
<tr>
<th><label for="ton_reg_mailchimp_test_api_key"><?php esc_html_e( 'API key (test)', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="password" id="ton_reg_mailchimp_test_api_key" name="ton_reg_mailchimp_api_key" class="regular-text" value="" autocomplete="new-password" />
</td>
</tr>
<tr>
<th><label for="ton_reg_mailchimp_test_list_id"><?php esc_html_e( 'List ID (test)', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="text" id="ton_reg_mailchimp_test_list_id" name="ton_reg_mailchimp_list_id" class="regular-text" value="<?php echo esc_attr( get_option( 'ton_reg_mailchimp_list_id' ) ); ?>" />
</td>
</tr>
</table>
<?php submit_button( __( 'Test connessione Mailchimp', 'ton-italia-registration' ), 'secondary', 'submit', false ); ?>
</form>
<?php self::render_card_settings_section(); ?>
</div>
<?php
}
/**
* Save registration admin fields.
*/
public static function save_registration() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
$id = isset( $_POST['registration_id'] ) ? (int) $_POST['registration_id'] : 0;
check_admin_referer( 'ton_reg_save_' . $id, 'ton_reg_admin_nonce' );
$row = TON_Reg_Database::get( $id );
if ( ! $row ) {
wp_die( esc_html__( 'Non trovato.', 'ton-italia-registration' ) );
}
$old_status = (string) $row->status;
$new_status = sanitize_text_field( wp_unslash( $_POST['status'] ?? 'pending' ) );
TON_Reg_Database::update(
$id,
array(
'status' => $new_status,
'libro_associati_date' => sanitize_text_field( wp_unslash( $_POST['libro_associati_date'] ?? '' ) ) ?: null,
'admin_notes' => sanitize_textarea_field( wp_unslash( $_POST['admin_notes'] ?? '' ) ),
)
);
$redirect_args = array( 'updated' => '1' );
$card_result = TON_Reg_Membership_Card::maybe_generate_on_admission( $id, $old_status, $new_status );
if ( is_wp_error( $card_result ) ) {
$redirect_args['card_error'] = rawurlencode( $card_result->get_error_message() );
} elseif ( null !== $card_result ) {
$redirect_args['card_generated'] = '1';
}
self::save_renewal_checkboxes( $id, $row );
wp_safe_redirect(
add_query_arg(
$redirect_args,
self::detail_url( $id, self::get_detail_list_context() )
)
);
exit;
}
/**
* Export admitted members as Libro Soci CSV.
*/
public static function export_libro_soci() {
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'ton_reg_export_libro_soci' ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ), 403 );
}
TON_Reg_Members_Export::stream_libro_soci_csv();
}
/**
* GDPR export download.
*/
public static function gdpr_export() {
$id = isset( $_GET['id'] ) ? (int) $_GET['id'] : 0;
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'ton_reg_gdpr_' . $id ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
$data = TON_Reg_Gdpr::export( $id );
if ( ! $data ) {
wp_die( esc_html__( 'Non trovato.', 'ton-italia-registration' ) );
}
$format = isset( $_GET['format'] ) ? sanitize_text_field( wp_unslash( $_GET['format'] ) ) : 'json';
if ( 'csv' === $format ) {
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=ton-registration-' . $id . '.csv' );
$out = fopen( 'php://output', 'w' );
foreach ( $data as $key => $value ) {
if ( is_array( $value ) ) {
$value = wp_json_encode( $value, JSON_UNESCAPED_UNICODE );
}
fputcsv( $out, array( (string) $key, (string) $value ) );
}
fclose( $out );
exit;
}
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=ton-registration-' . $id . '.json' );
echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
exit;
}
/**
* GDPR anonymize.
*/
public static function gdpr_anonymize() {
$id = isset( $_GET['id'] ) ? (int) $_GET['id'] : 0;
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'ton_reg_gdpr_' . $id ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
TON_Reg_Gdpr::anonymize( $id, 'admin action' );
wp_safe_redirect( admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id . '&updated=1' ) );
exit;
}
/**
* GDPR erase.
*/
public static function gdpr_erase() {
$id = isset( $_GET['id'] ) ? (int) $_GET['id'] : 0;
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'ton_reg_gdpr_' . $id ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
TON_Reg_Gdpr::erase( $id, 'admin action' );
wp_safe_redirect( admin_url( 'admin.php?page=ton-registrations&updated=1' ) );
exit;
}
/**
* Test Mailchimp API connection from settings.
*/
public static function mailchimp_test() {
$redirect = admin_url( 'admin.php?page=ton-reg-settings' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_safe_redirect(
add_query_arg(
'mc_test_error',
rawurlencode( __( 'Non autorizzato.', 'ton-italia-registration' ) ),
$redirect
)
);
exit;
}
$nonce = isset( $_POST['ton_reg_mc_test_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['ton_reg_mc_test_nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'ton_reg_mc_test' ) ) {
wp_safe_redirect(
add_query_arg(
'mc_test_error',
rawurlencode( __( 'Sessione scaduta. Ricarica la pagina e riprova.', 'ton-italia-registration' ) ),
$redirect
)
);
exit;
}
$api_key_post = isset( $_POST['ton_reg_mailchimp_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['ton_reg_mailchimp_api_key'] ) ) : '';
$list_id_post = isset( $_POST['ton_reg_mailchimp_list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['ton_reg_mailchimp_list_id'] ) ) : '';
if ( ! class_exists( 'TON_Reg_Mailchimp' ) ) {
wp_safe_redirect(
add_query_arg(
'mc_test_error',
rawurlencode( __( 'Modulo Mailchimp non caricato. Reinstalla o aggiorna il plugin.', 'ton-italia-registration' ) ),
$redirect
)
);
exit;
}
$result = TON_Reg_Mailchimp::test_connection( $api_key_post, $list_id_post );
if ( is_wp_error( $result ) ) {
wp_safe_redirect(
add_query_arg(
'mc_test_error',
rawurlencode( $result->get_error_message() ),
$redirect
)
);
exit;
}
wp_safe_redirect( add_query_arg( 'mc_test_ok', '1', $redirect ) );
exit;
}
/**
* Retry Mailchimp sync for a single registration.
*/
public static function mailchimp_retry() {
$id = isset( $_GET['id'] ) ? (int) $_GET['id'] : 0;
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'ton_reg_mc_retry_' . $id ) ) {
wp_die( esc_html__( 'Non autorizzato.', 'ton-italia-registration' ) );
}
$row = TON_Reg_Database::get( $id );
if ( ! $row || empty( $row->consenso_newsletter ) ) {
wp_safe_redirect(
add_query_arg(
'mc_error',
rawurlencode( __( 'Consenso newsletter assente.', 'ton-italia-registration' ) ),
admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id )
)
);
exit;
}
if ( ! TON_Reg_Mailchimp::is_configured() ) {
wp_safe_redirect(
add_query_arg(
'mc_error',
rawurlencode( __( 'Integrazione Mailchimp non configurata.', 'ton-italia-registration' ) ),
admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id )
)
);
exit;
}
$ok = TON_Reg_Mailchimp::sync_registration( $id );
$redirect = admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id );
if ( $ok ) {
wp_safe_redirect( add_query_arg( 'mc_ok', '1', $redirect ) );
exit;
}
$row = TON_Reg_Database::get( $id );
$error = ! empty( $row->mailchimp_error ) ? $row->mailchimp_error : __( 'Sincronizzazione Mailchimp non riuscita.', 'ton-italia-registration' );
wp_safe_redirect(
add_query_arg(
'mc_error',
rawurlencode( $error ),
$redirect
)
);
exit;
}
/**
* Membership cards section on registration detail.
*
* @param int $id Registration ID.
* @param object $row Registration row.
*/
private static function render_membership_cards_section( $id, $row ) {
$cards = TON_Reg_Membership_Cards::get_by_registration( $id );
$active_year = TON_Reg_Membership_Card::get_active_year();
?>
<h2><?php esc_html_e( 'Tessere generate', 'ton-italia-registration' ); ?></h2>
<?php if ( empty( $cards ) ) : ?>
<p class="description"><?php esc_html_e( 'Nessuna tessera generata.', 'ton-italia-registration' ); ?></p>
<?php else : ?>
<table class="widefat striped ton-reg-cards-table">
<thead>
<tr>
<th><?php esc_html_e( 'Anno', 'ton-italia-registration' ); ?></th>
<th><?php esc_html_e( 'Data', 'ton-italia-registration' ); ?></th>
<th><?php esc_html_e( 'Origine', 'ton-italia-registration' ); ?></th>
<th><?php esc_html_e( 'Download', 'ton-italia-registration' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $cards as $card ) : ?>
<tr>
<td><?php echo esc_html( (string) $card->year ); ?></td>
<td><?php echo esc_html( mysql2date( 'd/m/Y H:i', $card->created_at ) ); ?></td>
<td><?php echo esc_html( TON_Reg_Membership_Card::context_label( (string) $card->generation_context ) ); ?></td>
<td>
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'ton_reg_download_card', 'card_id' => (int) $card->id ), admin_url( 'admin-post.php' ) ), 'ton_reg_download_card_' . (int) $card->id, 'ton_reg_card_nonce' ) ); ?>">
<?php esc_html_e( 'Scarica PDF', 'ton-italia-registration' ); ?>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php if ( 'admitted' === $row->status && empty( $row->anonymized_at ) ) : ?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="margin-top:1em;">
<input type="hidden" name="action" value="ton_reg_generate_card" />
<input type="hidden" name="registration_id" value="<?php echo (int) $id; ?>" />
<input type="hidden" name="year" value="<?php echo (int) $active_year; ?>" />
<input type="hidden" name="force_regenerate" value="1" />
<?php wp_nonce_field( 'ton_reg_generate_card_' . $id, 'ton_reg_card_admin_nonce' ); ?>
<?php
submit_button(
sprintf(
/* translators: %d: year */
__( 'Genera / rigenera tessera %d', 'ton-italia-registration' ),
(int) $active_year
),
'secondary',
'submit',
false
);
?>
</form>
<?php endif; ?>
<?php
}
/**
* Card settings section on settings page.
*/
private static function render_card_settings_section() {
if ( ! TON_Reg_Membership_Card::is_available() ) {
echo '<div class="notice notice-error"><p>' . esc_html__( 'Libreria PDF (FPDI) non trovata in vendor/.', 'ton-italia-registration' ) . '</p></div>';
return;
}
$active_year = TON_Reg_Membership_Card::get_active_year();
$config = TON_Reg_Membership_Card::get_template_config( $active_year );
$template_id = (int) ( $config['template_attachment_id'] ?? 0 );
$template_url = $template_id ? wp_get_attachment_url( $template_id ) : '';
$admitted_count = TON_Reg_Database::count(
array(
'status' => 'admitted',
'exclude_anonymized' => true,
)
);
?>
<hr />
<h2><?php esc_html_e( 'Tessera socio (PDF)', 'ton-italia-registration' ); ?></h2>
<p class="description"><?php esc_html_e( 'Carica un template PDF annuale e configura la posizione del nome. La tessera viene generata automaticamente quando un iscritto passa ad «Ammesso».', 'ton-italia-registration' ); ?></p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="ton_reg_save_card_settings" />
<?php wp_nonce_field( 'ton_reg_save_card_settings', 'ton_reg_card_settings_nonce' ); ?>
<table class="form-table">
<tr>
<th><label for="ton_reg_card_active_year"><?php esc_html_e( 'Anno attivo', 'ton-italia-registration' ); ?></label></th>
<td><input type="number" id="ton_reg_card_active_year" name="active_year" min="2000" max="2100" value="<?php echo (int) $active_year; ?>" /></td>
</tr>
<tr>
<th><label for="ton_reg_card_name_x"><?php esc_html_e( 'Posizione nome X (mm)', 'ton-italia-registration' ); ?></label></th>
<td><input type="number" step="0.1" id="ton_reg_card_name_x" name="name_x" value="<?php echo esc_attr( (string) $config['name_x'] ); ?>" /></td>
</tr>
<tr>
<th><label for="ton_reg_card_name_y"><?php esc_html_e( 'Posizione nome Y (mm)', 'ton-italia-registration' ); ?></label></th>
<td><input type="number" step="0.1" id="ton_reg_card_name_y" name="name_y" value="<?php echo esc_attr( (string) $config['name_y'] ); ?>" /></td>
</tr>
<tr>
<th><label for="ton_reg_card_font_family"><?php esc_html_e( 'Font', 'ton-italia-registration' ); ?></label></th>
<td>
<select id="ton_reg_card_font_family" name="font_family">
<?php foreach ( array( 'Helvetica', 'Times', 'Courier', 'Arial' ) as $font ) : ?>
<option value="<?php echo esc_attr( $font ); ?>" <?php selected( $config['font_family'], $font ); ?>><?php echo esc_html( $font ); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th><label for="ton_reg_card_font_style"><?php esc_html_e( 'Stile font', 'ton-italia-registration' ); ?></label></th>
<td>
<select id="ton_reg_card_font_style" name="font_style">
<?php
$styles = array( '' => __( 'Normale', 'ton-italia-registration' ), 'B' => __( 'Grassetto', 'ton-italia-registration' ), 'I' => __( 'Corsivo', 'ton-italia-registration' ), 'BI' => __( 'Grassetto corsivo', 'ton-italia-registration' ) );
foreach ( $styles as $value => $label ) :
?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $config['font_style'], $value ); ?>><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th><label for="ton_reg_card_font_size"><?php esc_html_e( 'Dimensione font (pt)', 'ton-italia-registration' ); ?></label></th>
<td><input type="number" step="0.5" min="6" max="72" id="ton_reg_card_font_size" name="font_size" value="<?php echo esc_attr( (string) $config['font_size'] ); ?>" /></td>
</tr>
<tr>
<th><label for="ton_reg_card_text_color"><?php esc_html_e( 'Colore testo', 'ton-italia-registration' ); ?></label></th>
<td><input type="text" id="ton_reg_card_text_color" name="text_color" value="<?php echo esc_attr( (string) $config['text_color'] ); ?>" placeholder="#000000" /></td>
</tr>
<tr>
<th><?php esc_html_e( 'Nome in maiuscolo', 'ton-italia-registration' ); ?></th>
<td><input type="hidden" name="uppercase" value="0" /><input type="checkbox" name="uppercase" value="1" <?php checked( ! empty( $config['uppercase'] ) ); ?> /></td>
</tr>
<tr>
<th><label for="ton_reg_card_align"><?php esc_html_e( 'Allineamento', 'ton-italia-registration' ); ?></label></th>
<td>
<select id="ton_reg_card_align" name="align">
<option value="L" <?php selected( $config['align'], 'L' ); ?>><?php esc_html_e( 'Sinistra', 'ton-italia-registration' ); ?></option>
<option value="C" <?php selected( $config['align'], 'C' ); ?>><?php esc_html_e( 'Centro', 'ton-italia-registration' ); ?></option>
<option value="R" <?php selected( $config['align'], 'R' ); ?>><?php esc_html_e( 'Destra', 'ton-italia-registration' ); ?></option>
</select>
</td>
</tr>
<tr>
<th><label for="ton_reg_card_cell_width"><?php esc_html_e( 'Larghezza cella (mm)', 'ton-italia-registration' ); ?></label></th>
<td>
<input type="number" step="0.1" min="0" id="ton_reg_card_cell_width" name="cell_width" value="<?php echo esc_attr( (string) $config['cell_width'] ); ?>" />
<p class="description"><?php esc_html_e( 'Se il nome completo supera questa larghezza, Nome e Cognome vengono stampati su due righe.', 'ton-italia-registration' ); ?></p>
</td>
</tr>
</table>
<?php if ( $template_url ) : ?>
<p class="description">
<?php esc_html_e( 'Template corrente:', 'ton-italia-registration' ); ?>
<a href="<?php echo esc_url( $template_url ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Apri PDF', 'ton-italia-registration' ); ?></a>
</p>
<?php endif; ?>
<?php submit_button( __( 'Salva impostazioni tessera', 'ton-italia-registration' ) ); ?>
</form>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data" style="margin-top:1.5em;">
<input type="hidden" name="action" value="ton_reg_upload_card_template" />
<input type="hidden" name="active_year" value="<?php echo (int) $active_year; ?>" />
<?php wp_nonce_field( 'ton_reg_upload_card_template', 'ton_reg_card_template_nonce' ); ?>
<h3><?php esc_html_e( 'Template PDF', 'ton-italia-registration' ); ?></h3>
<p class="description"><?php esc_html_e( 'Carica il PDF per l\'anno attivo. Lascia l\'area del nome vuota nel template.', 'ton-italia-registration' ); ?></p>
<p><input type="file" name="card_template" accept="application/pdf,.pdf" required /></p>
<?php submit_button( __( 'Carica template PDF', 'ton-italia-registration' ), 'secondary', 'submit', false ); ?>
</form>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="margin-top:1.5em;" onsubmit="return confirm('<?php echo esc_js( sprintf( __( 'Generare le tessere %d per tutti i soci ammessi (%d)?', 'ton-italia-registration' ), (int) $active_year, (int) $admitted_count ) ); ?>');">
<input type="hidden" name="action" value="ton_reg_batch_cards" />
<input type="hidden" name="year" value="<?php echo (int) $active_year; ?>" />
<input type="hidden" name="offset" value="0" />
<?php wp_nonce_field( 'ton_reg_batch_cards', 'ton_reg_batch_cards_nonce' ); ?>
<h3><?php esc_html_e( 'Generazione batch', 'ton-italia-registration' ); ?></h3>
<p class="description">
<?php
printf(
/* translators: 1: year, 2: count */
esc_html__( 'Genera tessere %1$d per tutti i soci ammessi (%2$d). Salta chi ha già la tessera per quell\'anno.', 'ton-italia-registration' ),
(int) $active_year,
(int) $admitted_count
);
?>
</p>
<p>
<label>
<input type="checkbox" name="force_regenerate" value="1" />
<?php esc_html_e( 'Sovrascrivi tessere già generate per questo anno', 'ton-italia-registration' ); ?>
</label>
</p>
<?php submit_button( __( 'Genera tessere nuovo anno', 'ton-italia-registration' ), 'primary', 'submit', false ); ?>
</form>
<?php
}
/**
* @return string
*/
private static function format_batch_report_message() {
$generated = isset( $_GET['batch_generated'] ) ? (int) $_GET['batch_generated'] : 0;
$skipped = isset( $_GET['batch_skipped'] ) ? (int) $_GET['batch_skipped'] : 0;
$errors = isset( $_GET['batch_errors'] ) ? (int) $_GET['batch_errors'] : 0;
return sprintf(
/* translators: 1: generated, 2: skipped, 3: errors */
__( 'Batch completato: %1$d generate, %2$d saltate, %3$d errori.', 'ton-italia-registration' ),
$generated,
$skipped,
$errors
);
}
/**
* Save card layout settings.
*/
public static function save_card_settings() {
TON_Reg_Membership_Card::assert_can_manage_cards();
check_admin_referer( 'ton_reg_save_card_settings', 'ton_reg_card_settings_nonce' );
$year = isset( $_POST['active_year'] ) ? (int) $_POST['active_year'] : TON_Reg_Membership_Card::get_active_year();
TON_Reg_Membership_Card::set_active_year( $year );
$current = TON_Reg_Membership_Card::get_template_config( $year );
TON_Reg_Membership_Card::save_template_config(
$year,
array(
'template_attachment_id' => (int) ( $current['template_attachment_id'] ?? 0 ),
'name_x' => isset( $_POST['name_x'] ) ? (float) wp_unslash( $_POST['name_x'] ) : 42.5,
'name_y' => isset( $_POST['name_y'] ) ? (float) wp_unslash( $_POST['name_y'] ) : 118.0,
'font_family' => sanitize_text_field( wp_unslash( $_POST['font_family'] ?? 'Helvetica' ) ),
'font_style' => sanitize_text_field( wp_unslash( $_POST['font_style'] ?? '' ) ),
'font_size' => isset( $_POST['font_size'] ) ? (float) wp_unslash( $_POST['font_size'] ) : 14,
'text_color' => sanitize_text_field( wp_unslash( $_POST['text_color'] ?? '#000000' ) ),
'uppercase' => ! empty( $_POST['uppercase'] ),
'align' => sanitize_text_field( wp_unslash( $_POST['align'] ?? 'C' ) ),
'cell_width' => isset( $_POST['cell_width'] ) ? (float) wp_unslash( $_POST['cell_width'] ) : 80,
)
);
wp_safe_redirect( admin_url( 'admin.php?page=ton-reg-settings&card_settings_saved=1' ) );
exit;
}
/**
* Upload card template PDF for active year.
*/
public static function upload_card_template() {
TON_Reg_Membership_Card::assert_can_manage_cards();
check_admin_referer( 'ton_reg_upload_card_template', 'ton_reg_card_template_nonce' );
$year = isset( $_POST['active_year'] ) ? (int) $_POST['active_year'] : TON_Reg_Membership_Card::get_active_year();
$file = isset( $_FILES['card_template'] ) ? $_FILES['card_template'] : null;
$attachment_id = TON_Reg_Membership_Card::upload_template_pdf( $file, $year );
if ( is_wp_error( $attachment_id ) ) {
wp_safe_redirect(
add_query_arg(
'card_error',
rawurlencode( $attachment_id->get_error_message() ),
admin_url( 'admin.php?page=ton-reg-settings' )
)
);
exit;
}
update_post_meta( $attachment_id, '_ton_reg_card_template_year', $year );
$current = TON_Reg_Membership_Card::get_template_config( $year );
$old_id = (int) ( $current['template_attachment_id'] ?? 0 );
if ( $old_id && $old_id !== (int) $attachment_id ) {
wp_delete_attachment( $old_id, true );
}
TON_Reg_Membership_Card::save_template_config(
$year,
array(
'template_attachment_id' => (int) $attachment_id,
)
);
wp_safe_redirect( admin_url( 'admin.php?page=ton-reg-settings&card_template_ok=1' ) );
exit;
}
/**
* Chunked batch card generation.
*/
public static function batch_cards() {
TON_Reg_Membership_Card::assert_can_manage_cards();
check_admin_referer( 'ton_reg_batch_cards', 'ton_reg_batch_cards_nonce' );
$year = isset( $_POST['year'] ) ? (int) $_POST['year'] : TON_Reg_Membership_Card::get_active_year();
$offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
$force_regenerate = ! empty( $_POST['force_regenerate'] );
$chunk_size = 20;
$generated = isset( $_POST['batch_generated'] ) ? (int) $_POST['batch_generated'] : 0;
$skipped = isset( $_POST['batch_skipped'] ) ? (int) $_POST['batch_skipped'] : 0;
$errors = isset( $_POST['batch_errors'] ) ? (int) $_POST['batch_errors'] : 0;
$rows = TON_Reg_Database::query(
array(
'status' => 'admitted',
'exclude_anonymized' => true,
'limit' => $chunk_size,
'offset' => $offset,
'orderby' => 'id',
'order' => 'ASC',
)
);
foreach ( $rows as $row ) {
$existing = TON_Reg_Membership_Cards::get_by_registration_year( (int) $row->id, $year );
if ( $existing && ! $force_regenerate ) {
++$skipped;
continue;
}
$result = TON_Reg_Membership_Card::generate_for_registration(
(int) $row->id,
$year,
array(
'context' => TON_Reg_Membership_Card::CONTEXT_BATCH,
'force_regenerate' => $force_regenerate,
)
);
if ( is_wp_error( $result ) ) {
++$errors;
} else {
++$generated;
}
}
$processed = count( $rows );
if ( $processed < $chunk_size ) {
wp_safe_redirect(
add_query_arg(
array(
'card_batch_done' => '1',
'batch_generated' => $generated,
'batch_skipped' => $skipped,
'batch_errors' => $errors,
),
admin_url( 'admin.php?page=ton-reg-settings' )
)
);
exit;
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title><?php esc_html_e( 'Generazione tessere…', 'ton-italia-registration' ); ?></title></head>
<body onload="document.getElementById('ton-reg-batch-form').submit();">
<p><?php esc_html_e( 'Generazione tessere in corso…', 'ton-italia-registration' ); ?></p>
<form id="ton-reg-batch-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="ton_reg_batch_cards" />
<input type="hidden" name="year" value="<?php echo (int) $year; ?>" />
<input type="hidden" name="offset" value="<?php echo (int) ( $offset + $chunk_size ); ?>" />
<input type="hidden" name="batch_generated" value="<?php echo (int) $generated; ?>" />
<input type="hidden" name="batch_skipped" value="<?php echo (int) $skipped; ?>" />
<input type="hidden" name="batch_errors" value="<?php echo (int) $errors; ?>" />
<?php if ( $force_regenerate ) : ?>
<input type="hidden" name="force_regenerate" value="1" />
<?php endif; ?>
<?php wp_nonce_field( 'ton_reg_batch_cards', 'ton_reg_batch_cards_nonce' ); ?>
</form>
</body></html>
<?php
exit;
}
/**
* Generate or regenerate a single membership card.
*/
public static function generate_card() {
TON_Reg_Membership_Card::assert_can_manage_cards();
$id = isset( $_POST['registration_id'] ) ? (int) $_POST['registration_id'] : 0;
$year = isset( $_POST['year'] ) ? (int) $_POST['year'] : TON_Reg_Membership_Card::get_active_year();
check_admin_referer( 'ton_reg_generate_card_' . $id, 'ton_reg_card_admin_nonce' );
$result = TON_Reg_Membership_Card::generate_for_registration(
$id,
$year,
array(
'context' => TON_Reg_Membership_Card::CONTEXT_MANUAL,
'force_regenerate' => ! empty( $_POST['force_regenerate'] ),
)
);
$redirect = admin_url( 'admin.php?page=ton-registrations&action=view&id=' . $id );
if ( is_wp_error( $result ) ) {
wp_safe_redirect( add_query_arg( 'card_error', rawurlencode( $result->get_error_message() ), $redirect ) );
exit;
}
wp_safe_redirect( add_query_arg( 'card_generated', '1', $redirect ) );
exit;
}
}