Newer
Older
wordpress-ojs-pugin / includes / class-subscription-fields.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class WP_OJS_SSO_Subscription_Fields {

    private static $instance = null;

    public static function instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {}

    public static function get_fields() {
        return [
            'ojs_birthplace' => [
                'label'    => __( 'Place of birth', 'wp-ojs-sso-bridge' ),
                'type'     => 'text',
                'required' => true,
            ],
            'ojs_birthdate' => [
                'label'    => __( 'Date of birth', 'wp-ojs-sso-bridge' ),
                'type'     => 'date',
                'required' => true,
            ],
            'ojs_fiscal_code' => [
                'label'    => __( 'Fiscal Code', 'wp-ojs-sso-bridge' ),
                'type'     => 'text',
                'required' => true,
                'pattern'  => '[A-Za-z0-9]{16}',
                'maxlength' => 16,
            ],
            'ojs_shipping_address' => [
                'label'    => __( 'Shipping address for the journal', 'wp-ojs-sso-bridge' ),
                'type'     => 'textarea',
                'required' => true,
            ],
            'ojs_membership_type' => [
                'label'    => __( 'Membership type', 'wp-ojs-sso-bridge' ),
                'type'     => 'radio',
                'required' => true,
                'options'  => [
                    'ordinary'         => __( 'Ordinary member (no TugBoat subscription)', 'wp-ojs-sso-bridge' ),
                    'ordinary_tugboat' => __( 'Ordinary member with TugBoat subscription', 'wp-ojs-sso-bridge' ),
                    'student'          => __( 'Student member (document required)', 'wp-ojs-sso-bridge' ),
                    'student_junior'   => __( 'Junior student member (document required)', 'wp-ojs-sso-bridge' ),
                    'institutional'    => __( 'Institutional member', 'wp-ojs-sso-bridge' ),
                ],
            ],
            'ojs_student_document' => [
                'label'       => __( 'Student certificate (PDF, JPG or PNG, max 10 MB)', 'wp-ojs-sso-bridge' ),
                'type'        => 'file',
                'required'    => false, // conditionally required via JS + server validation
                'accept'      => '.pdf,.jpg,.jpeg,.png',
                'meta_key'    => 'ojs_student_document', // may store attachment ID or legacy path
                'conditional' => [ 'ojs_membership_type' => [ 'student', 'student_junior' ] ],
            ],
            'ojs_bonifico_receipt' => [
                'label'    => __( 'Bank transfer receipt (PDF, JPG or PNG, max 10 MB)', 'wp-ojs-sso-bridge' ),
                'type'     => 'file',
                'required' => false, // required in validate() for subscription requests
                'accept'   => '.pdf,.jpg,.jpeg,.png',
                'meta_key' => 'ojs_bonifico_attachment_id', // file input name ojs_bonifico_receipt, meta stores attachment id
            ],
            'ojs_consent_guit_soci' => [
                'label'    => __( 'I consent to being added to the guit-soci mailing list for institutional communications (mandatory).', 'wp-ojs-sso-bridge' ),
                'type'     => 'checkbox',
                'required' => true,
            ],
            'ojs_consent_guit_members' => [
                'label'   => __( 'I consent to being added to the guit-members mailing list for group activity communications.', 'wp-ojs-sso-bridge' ),
                'type'    => 'radio',
                'required' => true,
                'options' => [
                    '1' => __( 'I consent', 'wp-ojs-sso-bridge' ),
                    '0' => __( 'I do not consent', 'wp-ojs-sso-bridge' ),
                ],
            ],
            'ojs_consent_privacy' => [
                'label'    => __( 'I consent to the processing of personal data provided through this form for the institutional purposes of the association.', 'wp-ojs-sso-bridge' ),
                'type'     => 'checkbox',
                'required' => true,
            ],
        ];
    }

    public static function render_field( $key, $field, $value = '' ) {
        $required_attr = '';
        $data_attr     = '';
        if ( ! empty( $field['required'] ) ) {
            $required_attr = ' data-conditional-required="1"';
        }
        if ( ! empty( $field['conditional'] ) ) {
            $data_attr = sprintf( ' data-show-when="%s"', esc_attr( wp_json_encode( $field['conditional'] ) ) );
        }

        $wrapper_id    = 'ojs-field-' . esc_attr( $key );
        $is_conditional = ! empty( $field['conditional'] );
        $style         = $is_conditional ? ' style="display:none;"' : '';

        echo '<div id="' . esc_attr( $wrapper_id ) . '" class="ojs-field-wrapper"' . $style . $data_attr . '>';
        echo '<label for="' . esc_attr( $key ) . '">';
        echo esc_html( $field['label'] );
        if ( ! empty( $field['required'] ) ) {
            echo ' <span class="description">*</span>';
        }
        echo '</label><br/>';

        switch ( $field['type'] ) {
            case 'text':
                printf(
                    '<input type="text" name="%1$s" id="%1$s" value="%2$s" class="input" %3$s %4$s %5$s />',
                    esc_attr( $key ),
                    esc_attr( $value ),
                    $required_attr,
                    ! empty( $field['pattern'] ) ? 'pattern="' . esc_attr( $field['pattern'] ) . '"' : '',
                    ! empty( $field['maxlength'] ) ? 'maxlength="' . (int) $field['maxlength'] . '"' : ''
                );
                break;

            case 'date':
                printf(
                    '<input type="date" name="%1$s" id="%1$s" value="%2$s" class="input" %3$s />',
                    esc_attr( $key ),
                    esc_attr( $value ),
                    $required_attr
                );
                break;

            case 'textarea':
                printf(
                    '<textarea name="%1$s" id="%1$s" class="input" rows="3" %2$s>%3$s</textarea>',
                    esc_attr( $key ),
                    $required_attr,
                    esc_textarea( $value )
                );
                break;

            case 'radio':
                foreach ( $field['options'] as $opt_val => $opt_label ) {
                    printf(
                        '<label><input type="radio" name="%1$s" value="%2$s" %3$s /> %4$s</label><br/>',
                        esc_attr( $key ),
                        esc_attr( $opt_val ),
                        checked( $value, (string) $opt_val, false ),
                        esc_html( $opt_label )
                    );
                }
                break;

            case 'checkbox':
                printf(
                    '<label><input type="checkbox" name="%1$s" id="%1$s" value="1" %2$s %3$s /> %4$s</label>',
                    esc_attr( $key ),
                    checked( $value, '1', false ),
                    $required_attr,
                    '' // label is already above
                );
                break;

            case 'file':
                printf(
                    '<input type="file" name="%1$s" id="%1$s" accept="%2$s" />',
                    esc_attr( $key ),
                    esc_attr( $field['accept'] ?? '' )
                );
                if ( $value ) {
                    $label = $value;
                    if ( is_numeric( $value ) && (int) $value > 0 ) {
                        $p = get_post( (int) $value );
                        if ( $p && 'attachment' === $p->post_type ) {
                            $f = get_attached_file( (int) $value );
                            $label = $f ? wp_basename( $f ) : $p->post_title;
                        }
                    } elseif ( is_string( $value ) && ( false !== strpos( $value, '/' ) || false !== strpos( $value, '\\' ) ) ) {
                        $label = wp_basename( $value );
                    }
                    echo '<p class="description">' . esc_html__( 'Current file:', 'wp-ojs-sso-bridge' ) . ' ' . esc_html( $label ) . '</p>';
                }
                break;
        }

        echo '</div>';
    }

    public static function validate( $data, $files = [], $for_user_id = null ) {
        $errors   = [];
        $fields   = self::get_fields();
        $uid      = ( null !== $for_user_id ) ? (int) $for_user_id : get_current_user_id();

        $required_text = [ 'ojs_birthplace', 'ojs_birthdate', 'ojs_fiscal_code', 'ojs_shipping_address', 'ojs_membership_type' ];
        foreach ( $required_text as $key ) {
            if ( empty( $data[ $key ] ) ) {
                $errors[ $key ] = sprintf(
                    /* translators: %s: field label */
                    __( 'The field "%s" is required for subscription.', 'wp-ojs-sso-bridge' ),
                    $fields[ $key ]['label']
                );
            }
        }

        if ( ! empty( $data['ojs_fiscal_code'] ) && ! preg_match( '/^[A-Z0-9]{16}$/i', $data['ojs_fiscal_code'] ) ) {
            $errors['ojs_fiscal_code'] = __( 'The Fiscal Code must be exactly 16 alphanumeric characters.', 'wp-ojs-sso-bridge' );
        }

        $type = sanitize_text_field( $data['ojs_membership_type'] ?? '' );
        if ( in_array( $type, [ 'student', 'student_junior' ], true ) ) {
            if ( empty( $files['ojs_student_document']['name'] ) ) {
                $has = class_exists( 'WP_OJS_SSO_Subscription_Media' ) && $uid > 0
                    ? WP_OJS_SSO_Subscription_Media::has_stored_file( $uid, WP_OJS_SSO_Subscription_Media::KIND_CERTIFICATO )
                    : false;
                if ( ! $has ) {
                    $errors['ojs_student_document'] = __( 'A student certificate document is required for student memberships.', 'wp-ojs-sso-bridge' );
                }
            }
        }

        if ( empty( $files['ojs_bonifico_receipt']['name'] ?? '' ) ) {
            $has_b = class_exists( 'WP_OJS_SSO_Subscription_Media' ) && $uid > 0
                ? WP_OJS_SSO_Subscription_Media::has_stored_file( $uid, WP_OJS_SSO_Subscription_Media::KIND_BONIFICO )
                : false;
            if ( ! $has_b ) {
                $errors['ojs_bonifico_receipt'] = __( 'A bank transfer receipt is required for subscription requests.', 'wp-ojs-sso-bridge' );
            }
        }

        if ( empty( $data['ojs_consent_guit_soci'] ) ) {
            $errors['ojs_consent_guit_soci'] = __( 'Consent to the guit-soci mailing list is mandatory.', 'wp-ojs-sso-bridge' );
        }

        if ( ! isset( $data['ojs_consent_guit_members'] ) || '' === $data['ojs_consent_guit_members'] ) {
            $errors['ojs_consent_guit_members'] = __( 'Please indicate your preference for the guit-members mailing list.', 'wp-ojs-sso-bridge' );
        }

        if ( empty( $data['ojs_consent_privacy'] ) ) {
            $errors['ojs_consent_privacy'] = __( 'Consent to data processing is mandatory.', 'wp-ojs-sso-bridge' );
        }

        return $errors;
    }

    public static function save_fields( $user_id, $data ) {
        $text_fields = [
            'ojs_birthplace', 'ojs_birthdate', 'ojs_fiscal_code',
            'ojs_shipping_address', 'ojs_membership_type',
            'ojs_consent_guit_soci', 'ojs_consent_guit_members', 'ojs_consent_privacy',
        ];

        foreach ( $text_fields as $key ) {
            if ( isset( $data[ $key ] ) ) {
                update_user_meta( $user_id, $key, sanitize_text_field( $data[ $key ] ) );
            }
        }

        if ( isset( $data['first_name'] ) ) {
            update_user_meta( $user_id, 'first_name', sanitize_text_field( $data['first_name'] ) );
        }
        if ( isset( $data['last_name'] ) ) {
            update_user_meta( $user_id, 'last_name', sanitize_text_field( $data['last_name'] ) );
        }
    }
}