.) for AwoQuote. * * @param string $lease_token Signed lease token. * @return bool */ function awoquote_is_pro_lease_valid( $lease_token = '' ) { $token = trim( (string) $lease_token ); if ( '' === $token ) { $master = get_option( 'awo_suite_master_settings_v1', null ); if ( is_array( $master ) && ! empty( $master['lease_token'] ) ) { $token = trim( (string) $master['lease_token'] ); } } if ( 0 !== strpos( $token, 'awog1.' ) ) { return false; } $parts = explode( '.', $token ); if ( 3 !== count( $parts ) || strlen( $parts[2] ) < 32 ) { return false; } $b64 = strtr( $parts[1], '-_', '+/' ); $pad = strlen( $b64 ) % 4; if ( $pad > 0 ) { $b64 .= str_repeat( '=', 4 - $pad ); } $payload = json_decode( (string) base64_decode( $b64 ), true ); if ( ! is_array( $payload ) || empty( $payload['exp'] ) ) { return false; } $grace_ms = 7 * 24 * 3600 * 1000; $now_ms = (int) round( microtime( true ) * 1000 ); $host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); $host = preg_replace( '/^www\./', '', $host ); $tok_dom = isset( $payload['dom'] ) ? strtolower( preg_replace( '/^www\./', '', (string) $payload['dom'] ) ) : '*'; $dom_ok = ( empty( $tok_dom ) || '*' === $tok_dom || $tok_dom === $host || 'localhost' === $host || '127.0.0.1' === $host ); $plugins = isset( $payload['plugins'] ) && is_array( $payload['plugins'] ) ? $payload['plugins'] : array( '*' ); $plug_ok = in_array( '*', $plugins, true ) || in_array( 'awoquote', $plugins, true ); return ( ( (int) $payload['exp'] + $grace_ms ) > $now_ms && $dom_ok && $plug_ok ); } /** * Default AwoQuote workspace state. * * @return array */ function awoquote_get_default_workspace() { $site_name = get_bloginfo( 'name' ); return array( 'version' => 220, 'licenseKey' => '', 'leaseToken' => '', 'isPro' => false, 'theme' => 'auto', 'company' => array( 'name' => $site_name ? $site_name : 'Örnek Mühendislik & Teknoloji A.Ş.', 'taxInfo' => 'Kadıköy V.D. · 1234567890', 'phone' => '+90 216 555 00 00', 'email' => 'teklif@ornek.com.tr', 'address' => 'Teknopark Bulvarı No: 14, Pendik / İstanbul', 'bankName' => 'Garanti BBVA — Kurumsal Şube', 'iban' => 'TR33 0006 2000 0000 0001 2345 67', 'logo' => '', 'stamp' => '', 'terms' => "1. Bu fiyat teklifi düzenlenme tarihinden itibaren 15 gün süreyle geçerlidir.\n2. Ödemeler teklifte belirtilen banka IBAN hesabına (%50 sipariş avansı, %50 teslimatta) yapılır.\n3. KDV oranları yürürlükteki mevzuata göre satır bazında hesaplanmıştır.", ), 'quotes' => array( array( 'id' => 'QT-2026-101', 'quoteNo' => 'TKF-2026-001', 'date' => gmdate( 'Y-m-d' ), 'validUntil' => gmdate( 'Y-m-d', strtotime( '+15 days' ) ), 'customerName' => 'Marmara Lojistik ve Depolama A.Ş.', 'customerAttn' => 'Sn. Kerem Aydın (Satınalma Müdürü)', 'customerTax' => 'Büyük Mükellefler V.D. · 9876543210', 'customerTel' => '+90 532 444 11 22', 'subject' => 'Merkez Depo Kamera, Network ve Saha Kurulum Projesi', 'currency' => 'TRY', 'discountRate' => 5, 'status' => 'Gönderildi', 'items' => array( array( 'title' => 'Endüstriyel IP Kamera & PoE Switch Altyapı Paketi', 'unit' => 'Set', 'qty' => 2, 'price' => 28500, 'vat' => 20, ), array( 'title' => 'Fiber Optik Kablolama ve Saha Montaj İşçiliği', 'unit' => 'Hizmet', 'qty' => 1, 'price' => 18000, 'vat' => 20, ), ), ), ), ); } /** * Load saved workspace from wp_options. * * @return array */ function awoquote_get_workspace() { $saved = get_option( AWOQUOTE_OPTION_KEY, null ); if ( ! is_array( $saved ) || empty( $saved['company'] ) ) { return awoquote_get_default_workspace(); } $lease = isset( $saved['leaseToken'] ) ? (string) $saved['leaseToken'] : ''; $saved['isPro'] = awoquote_is_pro_lease_valid( $lease ); return $saved; } /** * Register WP Admin Menu. */ function awoquote_register_menu() { add_menu_page( 'AwoQuote — Fiyat Teklifi & Proforma', 'AwoQuote', 'manage_options', 'awoquote', 'awoquote_render_admin_page', 'dashicons-media-spreadsheet', 59 ); } add_action( 'admin_menu', 'awoquote_register_menu' ); /** * Enqueue Admin Assets. * * @param string $hook Current admin hook. */ function awoquote_enqueue_assets( $hook ) { if ( 'toplevel_page_awoquote' !== $hook || ! current_user_can( 'manage_options' ) ) { return; } wp_enqueue_style( 'awoquote-css', plugins_url( 'assets/awoquote.css', __FILE__ ), array(), AWOQUOTE_VERSION ); wp_enqueue_script( 'awoquote-js', plugins_url( 'assets/awoquote.js', __FILE__ ), array(), AWOQUOTE_VERSION, true ); wp_localize_script( 'awoquote-js', 'awoquoteWpConfig', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'awoquote_save_nonce' ), 'workspace' => awoquote_get_workspace(), 'bomWorkspace' => get_option( 'awobom_workspace_v1', null ), 'stockWorkspace' => get_option( 'awostock_workspace_v1', null ), 'aiSettings' => get_option( 'awo_ai_settings_v1', null ), ) ); } add_action( 'admin_enqueue_scripts', 'awoquote_enqueue_assets' ); /** * Render Admin Page. */ function awoquote_render_admin_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Bu sayfaya erişim yetkiniz yok.', 'awoquote' ) ); } require __DIR__ . '/quote-view.php'; } /** * AJAX Handler: Activate / Deactivate AWO-Guard v1.0 Polar PRO License. */ function awoquote_ajax_activate_license() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'Yetkisiz işlem.' ), 403 ); } check_ajax_referer( 'awoquote_save_nonce', 'nonce' ); $license_key = isset( $_POST['licenseKey'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_POST['licenseKey'] ) ) ) : ''; $ws = awoquote_get_workspace(); if ( '' === $license_key ) { $ws['licenseKey'] = ''; $ws['leaseToken'] = ''; $ws['isPro'] = false; update_option( AWOQUOTE_OPTION_KEY, $ws, false ); wp_send_json_success( array( 'message' => 'AwoQuote Free sürüme döndürüldü.', 'workspace' => $ws, ) ); } $host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); $host = preg_replace( '/^www\./', '', $host ); $company = isset( $ws['company']['name'] ) ? (string) $ws['company']['name'] : (string) get_bloginfo( 'name' ); $response = wp_remote_post( 'https://awosign.awoscan.com/api/license/activate', array( 'timeout' => 12, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'licenseKey' => $license_key, 'pluginSlug' => 'awoquote', 'domain' => $host ? $host : 'localhost', 'fingerprint' => 'wp_' . substr( hash( 'sha256', home_url() ), 0, 24 ), 'companyName' => $company, ) ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => 'AWO-Guard sunucusuna ulaşılamadı: ' . $response->get_error_message() ), 502 ); } $body = json_decode( (string) wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) || empty( $body['ok'] ) || empty( $body['leaseToken'] ) ) { $err = isset( $body['message'] ) ? sanitize_text_field( (string) $body['message'] ) : 'Geçersiz veya süresi dolmuş Polar lisans anahtarı.'; wp_send_json_error( array( 'message' => $err ), 400 ); } $ws['licenseKey'] = substr( $license_key, 0, 8 ) . '-****-' . substr( $license_key, -4 ); $ws['leaseToken'] = sanitize_text_field( (string) $body['leaseToken'] ); $ws['isPro'] = true; update_option( AWOQUOTE_OPTION_KEY, $ws, false ); wp_send_json_success( array( 'message' => 'Tebrikler! AwoQuote PRO (Sınırsız Teklif, Çoklu Döviz, IBAN QR + Kaşe & AwoBom/AwoStock Köprüsü) etkinleştirildi.', 'workspace' => $ws, ) ); } add_action( 'wp_ajax_awoquote_activate_license', 'awoquote_ajax_activate_license' ); /** * AJAX Handler: Save Workspace. */ function awoquote_ajax_save_workspace() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'Yetkisiz işlem.' ), 403 ); } check_ajax_referer( 'awoquote_save_nonce', 'nonce' ); $raw = isset( $_POST['workspace'] ) ? wp_unslash( $_POST['workspace'] ) : ''; $decoded = json_decode( $raw, true ); if ( ! is_array( $decoded ) || ! isset( $decoded['quotes'] ) || ! is_array( $decoded['quotes'] ) ) { wp_send_json_error( array( 'message' => 'Geçersiz veri formatı.' ), 400 ); } $existing = awoquote_get_workspace(); $lease_token = ! empty( $decoded['leaseToken'] ) ? sanitize_text_field( (string) $decoded['leaseToken'] ) : ( isset( $existing['leaseToken'] ) ? (string) $existing['leaseToken'] : '' ); $license_key = ! empty( $decoded['licenseKey'] ) ? sanitize_text_field( (string) $decoded['licenseKey'] ) : ( isset( $existing['licenseKey'] ) ? (string) $existing['licenseKey'] : '' ); $is_pro = awoquote_is_pro_lease_valid( $lease_token ); $comp_in = isset( $decoded['company'] ) && is_array( $decoded['company'] ) ? $decoded['company'] : array(); $clean_company = array( 'name' => sanitize_text_field( $comp_in['name'] ?? '' ), 'taxInfo' => sanitize_text_field( $comp_in['taxInfo'] ?? '' ), 'phone' => sanitize_text_field( $comp_in['phone'] ?? '' ), 'email' => sanitize_email( $comp_in['email'] ?? '' ), 'address' => sanitize_textarea_field( $comp_in['address'] ?? '' ), 'bankName' => sanitize_text_field( $comp_in['bankName'] ?? '' ), 'iban' => sanitize_text_field( $comp_in['iban'] ?? '' ), 'logo' => isset( $comp_in['logo'] ) && is_string( $comp_in['logo'] ) && strlen( $comp_in['logo'] ) < 2500000 ? $comp_in['logo'] : '', 'stamp' => isset( $comp_in['stamp'] ) && is_string( $comp_in['stamp'] ) && strlen( $comp_in['stamp'] ) < 2500000 ? $comp_in['stamp'] : '', 'terms' => sanitize_textarea_field( $comp_in['terms'] ?? '' ), ); $max_quotes = $is_pro ? 1000 : 5; $clean_quotes = array(); foreach ( array_slice( $decoded['quotes'], 0, $max_quotes ) as $q ) { if ( ! is_array( $q ) ) { continue; } $clean_items = array(); $items_in = isset( $q['items'] ) && is_array( $q['items'] ) ? array_slice( $q['items'], 0, 50 ) : array(); foreach ( $items_in as $it ) { if ( ! is_array( $it ) ) { continue; } $item_title = $it['title'] ?? ( $it['desc'] ?? '' ); $clean_items[] = array( 'title' => sanitize_text_field( $item_title ), 'unit' => sanitize_text_field( $it['unit'] ?? 'Adet' ), 'qty' => max( 0.01, (float) ( $it['qty'] ?? 1 ) ), 'price' => max( 0, (float) ( $it['price'] ?? 0 ) ), 'vat' => max( 0, min( 100, (float) ( $it['vat'] ?? 20 ) ) ), ); } $clean_quotes[] = array( 'id' => sanitize_text_field( $q['id'] ?? wp_unique_id( 'QT-' ) ), 'quoteNo' => sanitize_text_field( $q['quoteNo'] ?? 'TKF-001' ), 'date' => sanitize_text_field( $q['date'] ?? gmdate( 'Y-m-d' ) ), 'validUntil' => sanitize_text_field( $q['validUntil'] ?? '' ), 'customerName' => sanitize_text_field( $q['customerName'] ?? '' ), 'customerAttn' => sanitize_text_field( $q['customerAttn'] ?? '' ), 'customerTax' => sanitize_text_field( $q['customerTax'] ?? '' ), 'customerTel' => sanitize_text_field( $q['customerTel'] ?? '' ), 'subject' => sanitize_text_field( $q['subject'] ?? '' ), 'currency' => in_array( $q['currency'] ?? 'TRY', array( 'TRY', 'USD', 'EUR', 'GBP' ), true ) ? $q['currency'] : 'TRY', 'discountRate' => max( 0, min( 100, (float) ( $q['discountRate'] ?? 0 ) ) ), 'status' => in_array( $q['status'] ?? 'Taslak', array( 'Taslak', 'Gönderildi', 'Onaylandı', 'Reddedildi' ), true ) ? $q['status'] : 'Taslak', 'items' => $clean_items, ); } $to_save = array( 'version' => 220, 'licenseKey' => $license_key, 'leaseToken' => $lease_token, 'isPro' => $is_pro, 'theme' => in_array( $decoded['theme'] ?? 'auto', array( 'auto', 'light', 'dark' ), true ) ? $decoded['theme'] : 'auto', 'company' => $clean_company, 'quotes' => $clean_quotes, 'updatedAt' => current_time( 'mysql' ), ); update_option( AWOQUOTE_OPTION_KEY, $to_save, false ); if ( isset( $_POST['aiSettings'] ) ) { $raw_ai = json_decode( wp_unslash( $_POST['aiSettings'] ), true ); if ( is_array( $raw_ai ) ) { update_option( 'awo_ai_settings_v1', array( 'provider' => sanitize_text_field( $raw_ai['provider'] ?? 'ollama' ), 'ollamaUrl' => esc_url_raw( $raw_ai['ollamaUrl'] ?? 'http://localhost:11434' ), 'ollamaModel' => sanitize_text_field( $raw_ai['ollamaModel'] ?? 'qwen2.5:7b' ), 'groqKey' => sanitize_text_field( $raw_ai['groqKey'] ?? '' ), 'groqModel' => sanitize_text_field( $raw_ai['groqModel'] ?? 'llama-3.3-70b-versatile' ), ), false ); } } wp_send_json_success( array( 'message' => 'AwoQuote teklifleri WordPress veritabanına kaydedildi.', 'workspace' => $to_save, ) ); } add_action( 'wp_ajax_awoquote_save_workspace', 'awoquote_ajax_save_workspace' ); /** * Shortcode: [awoquote] * * @return string */ function awoquote_render_shortcode() { if ( ! current_user_can( 'manage_options' ) ) { return ''; } wp_enqueue_style( 'awoquote-css', plugins_url( 'assets/awoquote.css', __FILE__ ), array(), AWOQUOTE_VERSION ); wp_enqueue_script( 'awoquote-js', plugins_url( 'assets/awoquote.js', __FILE__ ), array(), AWOQUOTE_VERSION, true ); wp_localize_script( 'awoquote-js', 'awoquoteWpConfig', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'awoquote_save_nonce' ), 'workspace' => awoquote_get_workspace(), 'bomWorkspace' => get_option( 'awobom_workspace_v1', null ), 'stockWorkspace' => get_option( 'awostock_workspace_v1', null ), 'aiSettings' => get_option( 'awo_ai_settings_v1', null ), ) ); ob_start(); require __DIR__ . '/quote-view.php'; return (string) ob_get_clean(); } add_shortcode( 'awoquote', 'awoquote_render_shortcode' );