__( 'Forms', 'tiboform' ), 'public' => false, 'show_ui' => false, 'supports' => array( 'title' ) ) ); register_post_type( 'tiboform_entry', array( 'label' => __( 'Submissions', 'tiboform' ), 'public' => false, 'show_ui' => false, 'supports' => array( 'title' ) ) ); } add_action( 'init', 'tiboform_register_types' ); /** * Pro etkin mi? (Pro eklentisi lisans geçerliyse filtreyi true yapar.) * * @return bool */ function tiboform_is_pro() { return (bool) apply_filters( 'tiboform_is_pro', false ); } /** * Alan türleri. * * @return array */ function tiboform_types() { return apply_filters( 'tiboform_field_types', array( 'text' => __( 'Text', 'tiboform' ), 'email' => __( 'Email', 'tiboform' ), 'tel' => __( 'Phone', 'tiboform' ), 'textarea' => __( 'Long text', 'tiboform' ), 'url' => __( 'Website (URL)', 'tiboform' ), 'number' => __( 'Number', 'tiboform' ), 'select' => __( 'Dropdown', 'tiboform' ), 'radio' => __( 'Single choice', 'tiboform' ), 'checkbox' => __( 'Multiple choice', 'tiboform' ), 'consent' => __( 'Checkbox (single)', 'tiboform' ), 'date' => __( 'Date', 'tiboform' ), 'datetime' => __( 'Date and time', 'tiboform' ), 'hidden' => __( 'Hidden field', 'tiboform' ), 'heading' => __( 'Heading / text', 'tiboform' ), 'page' => __( 'Section / page break', 'tiboform' ), 'kvkk' => __( 'Privacy consent (KVKK)', 'tiboform' ), ) ); } /** * Değer taşımayan (yalnız görsel) alan türleri. * * @return array */ function tiboform_layout_types() { return array( 'heading', 'page' ); } /** * Alan kısaltıcı. * * @param string $id Kimlik. * @param string $type Tür. * @param string $label Etiket. * @param int $required Zorunlu. * @param string $width full|half. * @param array $extra Ek anahtarlar. * @return array */ function tiboform_f( $id, $type, $label, $required = 0, $width = 'full', $extra = array() ) { return array_merge( array( 'id' => $id, 'type' => $type, 'label' => $label, 'required' => $required, 'width' => $width ), $extra ); } /** * Hazır form şablonları. * * @return array anahtar => [ title, desc, fields ] */ function tiboform_templates() { $kvkk = tiboform_f( 'kvkk', 'kvkk', __( 'I have read the privacy notice.', 'tiboform' ), 1 ); $t = array( 'contact' => array( 'title' => __( 'Contact form', 'tiboform' ), 'desc' => __( 'Name, phone, email, subject and message.', 'tiboform' ), 'fields' => array( tiboform_f( 'ad', 'text', __( 'Full name', 'tiboform' ), 1, 'half' ), tiboform_f( 'telefon', 'tel', __( 'Phone', 'tiboform' ), 1, 'half' ), tiboform_f( 'eposta', 'email', __( 'Email', 'tiboform' ), 1 ), tiboform_f( 'konu', 'select', __( 'Subject', 'tiboform' ), 0, 'full', array( 'options' => array( __( 'Information', 'tiboform' ), __( 'Quote request', 'tiboform' ), __( 'Other', 'tiboform' ) ) ) ), tiboform_f( 'mesaj', 'textarea', __( 'Message', 'tiboform' ), 1 ), $kvkk, ), ), 'quote' => array( 'title' => __( 'Quote request', 'tiboform' ), 'desc' => __( 'Company, service, budget and project details.', 'tiboform' ), 'fields' => array( tiboform_f( 'ad', 'text', __( 'Full name', 'tiboform' ), 1, 'half' ), tiboform_f( 'firma', 'text', __( 'Company', 'tiboform' ), 0, 'half' ), tiboform_f( 'eposta', 'email', __( 'Email', 'tiboform' ), 1, 'half' ), tiboform_f( 'telefon', 'tel', __( 'Phone', 'tiboform' ), 1, 'half' ), tiboform_f( 'hizmet', 'select', __( 'Service', 'tiboform' ), 1, 'half', array( 'options' => array( __( 'Service A', 'tiboform' ), __( 'Service B', 'tiboform' ), __( 'Other', 'tiboform' ) ) ) ), tiboform_f( 'butce', 'select', __( 'Budget', 'tiboform' ), 0, 'half', array( 'options' => array( __( 'Under 10,000 TL', 'tiboform' ), __( '10,000–50,000 TL', 'tiboform' ), __( 'Over 50,000 TL', 'tiboform' ) ) ) ), tiboform_f( 'tarih', 'date', __( 'Preferred start date', 'tiboform' ), 0, 'half' ), tiboform_f( 'web', 'url', __( 'Website', 'tiboform' ), 0, 'half' ), tiboform_f( 'detay', 'textarea', __( 'Project details', 'tiboform' ), 1 ), $kvkk, ), ), 'appointment' => array( 'title' => __( 'Appointment request', 'tiboform' ), 'desc' => __( 'Preferred date and time with contact details.', 'tiboform' ), 'fields' => array( tiboform_f( 'ad', 'text', __( 'Full name', 'tiboform' ), 1, 'half' ), tiboform_f( 'telefon', 'tel', __( 'Phone', 'tiboform' ), 1, 'half' ), tiboform_f( 'eposta', 'email', __( 'Email', 'tiboform' ), 0 ), tiboform_f( 'hizmet', 'select', __( 'Service', 'tiboform' ), 1, 'full', array( 'options' => array( __( 'Consultation', 'tiboform' ), __( 'Check-up', 'tiboform' ), __( 'Other', 'tiboform' ) ) ) ), tiboform_f( 'zaman', 'datetime', __( 'Preferred date and time', 'tiboform' ), 1, 'half' ), tiboform_f( 'alternatif', 'radio', __( 'Best time to call you', 'tiboform' ), 0, 'half', array( 'options' => array( __( 'Morning', 'tiboform' ), __( 'Afternoon', 'tiboform' ), __( 'Evening', 'tiboform' ) ) ) ), tiboform_f( 'not', 'textarea', __( 'Note', 'tiboform' ), 0 ), $kvkk, ), ), 'job' => array( 'title' => __( 'Job application', 'tiboform' ), 'desc' => __( 'Position, experience and a short cover letter (no file).', 'tiboform' ), 'fields' => array( tiboform_f( 'ad', 'text', __( 'Full name', 'tiboform' ), 1, 'half' ), tiboform_f( 'telefon', 'tel', __( 'Phone', 'tiboform' ), 1, 'half' ), tiboform_f( 'eposta', 'email', __( 'Email', 'tiboform' ), 1, 'half' ), tiboform_f( 'dogum', 'date', __( 'Date of birth', 'tiboform' ), 0, 'half' ), tiboform_f( 'pozisyon', 'select', __( 'Position', 'tiboform' ), 1, 'half', array( 'options' => array( __( 'Sales', 'tiboform' ), __( 'Customer support', 'tiboform' ), __( 'Internship', 'tiboform' ), __( 'Other', 'tiboform' ) ) ) ), tiboform_f( 'deneyim', 'number', __( 'Years of experience', 'tiboform' ), 0, 'half', array( 'min' => '0', 'max' => '50' ) ), tiboform_f( 'linkedin', 'url', __( 'LinkedIn or portfolio link', 'tiboform' ), 0 ), tiboform_f( 'onyazi', 'textarea', __( 'Tell us about yourself', 'tiboform' ), 1 ), $kvkk, ), ), 'feedback' => array( 'title' => __( 'Feedback / survey', 'tiboform' ), 'desc' => __( 'Satisfaction score, choices and comments.', 'tiboform' ), 'fields' => array( tiboform_f( 'puan', 'radio', __( 'How satisfied are you?', 'tiboform' ), 1, 'full', array( 'options' => array( '1', '2', '3', '4', '5' ) ) ), tiboform_f( 'begendiniz', 'checkbox', __( 'What did you like?', 'tiboform' ), 0, 'full', array( 'options' => array( __( 'Quality', 'tiboform' ), __( 'Price', 'tiboform' ), __( 'Speed', 'tiboform' ), __( 'Staff', 'tiboform' ) ) ) ), tiboform_f( 'tavsiye', 'select', __( 'Would you recommend us?', 'tiboform' ), 0, 'full', array( 'options' => array( __( 'Yes', 'tiboform' ), __( 'Maybe', 'tiboform' ), __( 'No', 'tiboform' ) ) ) ), tiboform_f( 'yorum', 'textarea', __( 'Comments', 'tiboform' ), 0 ), tiboform_f( 'eposta', 'email', __( 'Email (optional)', 'tiboform' ), 0 ), ), ), 'newsletter' => array( 'title' => __( 'Newsletter signup', 'tiboform' ), 'desc' => __( 'Name, email and permission to send messages.', 'tiboform' ), 'fields' => array( tiboform_f( 'ad', 'text', __( 'Name', 'tiboform' ), 0, 'half' ), tiboform_f( 'eposta', 'email', __( 'Email', 'tiboform' ), 1, 'half' ), tiboform_f( 'izin', 'consent', __( 'I agree to receive email newsletters.', 'tiboform' ), 1 ), $kvkk, ), ), ); return apply_filters( 'tiboform_templates', $t ); } /** * Örnek form (iletişim şablonu). * * @param string $template Şablon anahtarı. * @return array */ function tiboform_sample_form( $template = 'contact' ) { $t = tiboform_templates(); $t = isset( $t[ $template ] ) ? $t[ $template ] : $t['contact']; return array( 'fields' => $t['fields'], 'settings' => tiboform_default_settings(), ); } /** * Şablondan form oluştur. * * @param string $template Şablon. * @param string $title Başlık (boşsa şablon adı). * @param array $settings Ayar ezmeleri. * @return int Form kimliği (0: hata). */ function tiboform_create_form( $template = 'contact', $title = '', $settings = array() ) { $all = tiboform_templates(); $key = isset( $all[ $template ] ) ? $template : 'contact'; $s = tiboform_sample_form( $key ); $s['settings'] = array_merge( $s['settings'], (array) $settings ); $s = tiboform_clean_schema( $s ); $id = wp_insert_post( array( 'post_type' => 'tiboform_form', 'post_status' => 'publish', 'post_title' => $title ? $title : $all[ $key ]['title'], 'post_content' => wp_slash( wp_json_encode( $s ) ) ) ); return is_wp_error( $id ) ? 0 : (int) $id; } /** * TEMA API'si: tema için kalıcı bir form döndürür; yoksa şablondan oluşturur. * Formun rengi boş bırakılır, böylece temanın vurgu rengi (--tfm-ac) kullanılır. * * Örnek: $id = tiboform_get_or_create_form( 'tiboklinik-iletisim', 'contact', 'İletişim' ); * echo tiboform_render_form( $id ); * * @param string $key Tema anahtarı (ör. "tiboklinik-iletisim"). * @param string $template Şablon: contact, quote, appointment, job, feedback, newsletter. * @param string $title Form başlığı. * @return int Form kimliği (0: oluşturulamadı). */ function tiboform_get_or_create_form( $key, $template = 'contact', $title = '' ) { $key = sanitize_key( $key ); if ( '' === $key ) { return 0; } $map = get_option( 'tiboform_theme_forms', array() ); $map = is_array( $map ) ? $map : array(); if ( ! empty( $map[ $key ] ) && 'tiboform_form' === get_post_type( (int) $map[ $key ] ) && 'trash' !== get_post_status( (int) $map[ $key ] ) ) { return (int) $map[ $key ]; } if ( ! post_type_exists( 'tiboform_form' ) ) { tiboform_register_types(); } $id = tiboform_create_form( $template, $title, array( 'color' => '' ) ); if ( $id ) { $map[ $key ] = $id; update_option( 'tiboform_theme_forms', $map, false ); do_action( 'tiboform_theme_form_created', $id, $key, $template ); } return $id; } /** * Varsayılan form ayarları. * * @return array */ function tiboform_default_settings() { return apply_filters( 'tiboform_default_settings', array( 'to' => get_option( 'admin_email' ), 'subject' => __( 'New form submission: {form_adi}', 'tiboform' ), 'body' => '{tum_alanlar}', 'button' => __( 'Send', 'tiboform' ), 'success' => __( 'Thank you! We received your message and will get back to you soon.', 'tiboform' ), 'redirect' => '', 'reply' => 0, 'reply_subject' => __( 'We received your message', 'tiboform' ), 'reply_body' => __( 'Hello, thank you for contacting us. We will get back to you as soon as possible.', 'tiboform' ), 'kvkk_url' => '', 'keep_days' => 180, 'color' => '', ) ); } /** * Form şeması. * * @param int $id Form. * @return array|null */ function tiboform_form( $id ) { $p = get_post( $id ); if ( ! $p || 'tiboform_form' !== $p->post_type ) { return null; } $s = json_decode( $p->post_content, true ); $s = is_array( $s ) ? $s : array(); return array( 'id' => $p->ID, 'title' => $p->post_title, 'fields' => isset( $s['fields'] ) && is_array( $s['fields'] ) ? $s['fields'] : array(), 'settings' => wp_parse_args( isset( $s['settings'] ) ? (array) $s['settings'] : array(), tiboform_default_settings() ), ); } /** * Dizi içindeki tüm değerleri ve anahtarları temizler (iç içe diziler dahil). * * @param mixed $v Ham değer. * @param bool $html true: güvenli HTML korunur (wp_kses_post), false: düz metin (satır sonları korunur). * @return mixed */ function tiboform_sanitize_deep( $v, $html = false ) { if ( is_array( $v ) ) { $out = array(); foreach ( $v as $k => $x ) { $out[ is_int( $k ) ? $k : sanitize_key( $k ) ] = tiboform_sanitize_deep( $x, $html ); } return $out; } if ( is_bool( $v ) || is_int( $v ) || is_float( $v ) ) { return $v; } if ( ! is_string( $v ) ) { return ''; } return $html ? wp_kses_post( $v ) : sanitize_textarea_field( $v ); } /** * Şemayı temizle (yönetimden gelen JSON). * * @param array $raw Ham şema. * @return array */ function tiboform_clean_schema( $raw ) { $fields = array(); $seen = array(); $known = array( 'id', 'type', 'label', 'required', 'options', 'placeholder', 'width', 'min', 'max', 'value' ); foreach ( isset( $raw['fields'] ) ? (array) $raw['fields'] : array() as $f ) { if ( ! is_array( $f ) ) { continue; } // Bilinmeyen tür (ör. Pro kapalıyken Pro alanı) korunur; çizimde atlanır. $type = sanitize_key( isset( $f['type'] ) ? $f['type'] : '' ); $type = $type ? $type : 'text'; $id = sanitize_key( isset( $f['id'] ) ? $f['id'] : '' ); $id = $id ? $id : $type . '_' . wp_rand( 100, 999 ); while ( isset( $seen[ $id ] ) ) { $id .= '_2'; } $seen[ $id ] = 1; $num = function ( $k ) use ( $f ) { return isset( $f[ $k ] ) && is_numeric( $f[ $k ] ) ? (string) ( 0 + $f[ $k ] ) : ''; }; $clean = array( 'id' => $id, 'type' => $type, 'label' => wp_kses( isset( $f['label'] ) ? (string) $f['label'] : '', array( 'b' => array(), 'strong' => array(), 'em' => array(), 'a' => array( 'href' => array(), 'target' => array() ) ) ), 'required' => empty( $f['required'] ) ? 0 : 1, 'options' => array_values( array_filter( array_map( 'sanitize_text_field', isset( $f['options'] ) ? (array) $f['options'] : array() ), 'strlen' ) ), 'placeholder' => sanitize_text_field( isset( $f['placeholder'] ) ? $f['placeholder'] : '' ), 'width' => isset( $f['width'] ) && 'half' === $f['width'] ? 'half' : 'full', 'min' => $num( 'min' ), 'max' => $num( 'max' ), 'value' => sanitize_text_field( isset( $f['value'] ) ? $f['value'] : '' ), ); // Ek anahtarlar (Pro: koşul, formül, dosya ayarları) kaybolmasın diye düz metin olarak korunur. $n = 0; foreach ( $f as $k => $v ) { $k = sanitize_key( $k ); if ( '' === $k || in_array( $k, $known, true ) || ! is_scalar( $v ) || ++$n > 30 ) { continue; } $clean[ $k ] = tiboform_substr( sanitize_text_field( (string) $v ), 0, 500 ); } $fields[] = apply_filters( 'tiboform_clean_field', $clean, $f ); } $d = tiboform_default_settings(); $s = isset( $raw['settings'] ) ? (array) $raw['settings'] : array(); $g = function ( $k ) use ( $s, $d ) { return isset( $s[ $k ] ) ? $s[ $k ] : ( isset( $d[ $k ] ) ? $d[ $k ] : '' ); }; $col = sanitize_hex_color( (string) $g( 'color' ) ); $out = array( 'to' => implode( ',', array_filter( array_map( 'sanitize_email', explode( ',', (string) $g( 'to' ) ) ) ) ), 'subject' => sanitize_text_field( $g( 'subject' ) ), 'body' => sanitize_textarea_field( $g( 'body' ) ), 'button' => sanitize_text_field( $g( 'button' ) ), 'success' => sanitize_text_field( $g( 'success' ) ), 'redirect' => esc_url_raw( (string) $g( 'redirect' ), array( 'http', 'https' ) ), 'reply' => empty( $s['reply'] ) ? 0 : 1, 'reply_subject' => sanitize_text_field( $g( 'reply_subject' ) ), 'reply_body' => sanitize_textarea_field( $g( 'reply_body' ) ), 'kvkk_url' => esc_url_raw( (string) $g( 'kvkk_url' ) ), 'keep_days' => min( 3650, absint( $g( 'keep_days' ) ) ), 'color' => $col ? $col : '', ); return array( 'fields' => array_slice( $fields, 0, 80 ), 'settings' => apply_filters( 'tiboform_clean_settings', $out, $s ), ); } /** * Gönderim sonrası durum mesajları. * * @param array $form Form. * @return array */ function tiboform_status_messages( $form ) { return apply_filters( 'tiboform_status_messages', array( 'err' => __( 'Please check the highlighted fields and try again.', 'tiboform' ), 'closed' => __( 'This form is not accepting submissions right now.', 'tiboform' ), 'dup' => __( 'You have already submitted this form.', 'tiboform' ), 'spam' => __( 'Your submission could not be verified. Please try again.', 'tiboform' ), ), $form ); } /** * HTML öznitelik dizisini yaz. * * @param array $a Öznitelikler. * @return string */ function tiboform_attrs( $a ) { $h = ''; foreach ( (array) $a as $k => $v ) { if ( null === $v || false === $v ) { continue; } $h .= ' ' . esc_attr( $k ) . ( true === $v ? '' : '="' . esc_attr( (string) $v ) . '"' ); } return $h; } /** * Gizli alan değeri: {get:utm_source} gibi adres çubuğu parametreleri desteklenir. * * @param string $v Ayar. * @return string */ function tiboform_hidden_value( $v ) { return preg_replace_callback( '/\{get:([a-z0-9_\-]+)\}/i', function ( $m ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only prefill of a hidden field on a public, cacheable page (e.g. utm_source); nothing is changed and the value is sanitized. return isset( $_GET[ $m[1] ] ) && is_scalar( $_GET[ $m[1] ] ) ? sanitize_text_field( wp_unslash( $_GET[ $m[1] ] ) ) : ''; }, (string) $v ); } /** * Tek alanın HTML'i. * * @param array $f Alan. * @param string $uid Benzersiz önek. * @param array $form Form. * @return string */ function tiboform_field_html( $f, $uid, $form ) { $s = $form['settings']; $types = tiboform_types(); $name = 'tiboform_f[' . $f['id'] . ']'; $fid = $uid . '-' . $f['id']; $req = ! empty( $f['required'] ) ? ' required' : ''; $star = ! empty( $f['required'] ) ? ' ' : ''; $attrs = apply_filters( 'tiboform_field_attrs', array( 'class' => 'tfm-f tfm-' . $f['type'] . ( 'half' === $f['width'] ? ' is-half' : '' ), 'data-tiboform-id' => $f['id'], ), $f, $form ); $open = '
' . wp_kses_post( $f['label'] ) . '
' . esc_html( $s['success'] ) . '
' . esc_html( $closed ) . '
' . esc_html__( 'Open submission', 'tiboform' ) . '
'; $html = tiboform_mail_wrap( $inner, $form ); $hdrs = array( 'Content-Type: text/html; charset=UTF-8' ); if ( $reply ) { $hdrs[] = 'Reply-To: ' . $reply; } $to = array_filter( array_map( 'trim', explode( ',', (string) $s['to'] ) ), 'is_email' ); $to = array_unique( (array) apply_filters( 'tiboform_notify_recipients', $to, $form, $data, $eid ) ); $subj = wp_strip_all_tags( tiboform_merge( $s['subject'], $form, $data, false, $eid ) ); foreach ( $to as $addr ) { wp_mail( $addr, $subj, $html, $hdrs ); } if ( $s['reply'] && $reply ) { $rh = tiboform_mail_wrap( '' . esc_html( get_bloginfo( 'name' ) ) . '
', $form ); wp_mail( $reply, wp_strip_all_tags( tiboform_merge( $s['reply_subject'], $form, $data, false, $eid ) ), $rh, array( 'Content-Type: text/html; charset=UTF-8' ) ); } } /** * Başvuru durumları. * * @return array */ function tiboform_statuses() { return array( 'new' => __( 'New', 'tiboform' ), 'read' => __( 'Read', 'tiboform' ), 'replied' => __( 'Replied', 'tiboform' ), 'spam' => __( 'Spam', 'tiboform' ), ); } /** * Başvurunun durumu. * * @param int $id Başvuru. * @return string */ function tiboform_entry_status( $id ) { $s = (string) get_post_meta( $id, '_tiboform_status', true ); if ( '' === $s ) { $s = (int) get_post_meta( $id, '_tiboform_read', true ) ? 'read' : 'new'; } return isset( tiboform_statuses()[ $s ] ) ? $s : 'new'; } /** * KVKK: saklama süresi dolan başvuruları sil. */ add_action( 'tiboform_cleanup', function () { foreach ( get_posts( array( 'post_type' => 'tiboform_form', 'posts_per_page' => 200, 'fields' => 'ids' ) ) as $fid ) { $f = tiboform_form( $fid ); $d = (int) $f['settings']['keep_days']; if ( $d < 1 ) { continue; } foreach ( get_posts( array( 'post_type' => 'tiboform_entry', 'post_parent' => $fid, 'posts_per_page' => 500, 'fields' => 'ids', 'date_query' => array( array( 'before' => $d . ' days ago' ) ) ) ) as $eid ) { wp_delete_post( $eid, true ); } } } );