0) { $attachment_url = wp_get_attachment_url($attachment_id); if ($attachment_url) { $file_url = esc_url_raw($attachment_url); } } $extracted_fields = array(); if (isset($record['extracted_fields']) && is_array($record['extracted_fields'])) { foreach ($record['extracted_fields'] as $field_key => $field_value) { $field_key = sanitize_key((string) $field_key); if ('' === $field_key || !is_scalar($field_value)) { continue; } $extracted_fields[$field_key] = sanitize_textarea_field((string) $field_value); } } return array( 'setup_pack_id' => $pack_id, 'attachment_id' => $attachment_id, 'file_name' => isset($record['file_name']) ? sanitize_file_name((string) $record['file_name']) : '', 'file_url' => $file_url, 'file_type' => 'application/pdf', 'uploaded_at' => isset($record['uploaded_at']) ? absint($record['uploaded_at']) : 0, 'uploaded_by' => isset($record['uploaded_by']) ? absint($record['uploaded_by']) : 0, 'extracted_at' => isset($record['extracted_at']) ? absint($record['extracted_at']) : 0, 'extracted_field_count' => isset($record['extracted_field_count']) ? absint($record['extracted_field_count']) : 0, 'extracted_fields' => $extracted_fields, ); } } if (!function_exists('matewa_agency_client_pdf_get_records')) { function matewa_agency_client_pdf_get_records() { $records = get_option(MATEWA_AGENCY_CLIENT_PDF_UPLOADS_OPTION, array()); $records = is_array($records) ? $records : array(); $safe = array(); foreach ($records as $pack_id => $record) { $clean = matewa_agency_client_pdf_clean_record($record, $pack_id); if (!empty($clean['setup_pack_id']) && !empty($clean['attachment_id'])) { $safe[$clean['setup_pack_id']] = $clean; } } return $safe; } } if (!function_exists('matewa_agency_client_pdf_store_records')) { function matewa_agency_client_pdf_store_records($records) { $records = is_array($records) ? $records : array(); $safe = array(); foreach ($records as $pack_id => $record) { $clean = matewa_agency_client_pdf_clean_record($record, $pack_id); if (!empty($clean['setup_pack_id']) && !empty($clean['attachment_id'])) { $safe[$clean['setup_pack_id']] = $clean; } } update_option(MATEWA_AGENCY_CLIENT_PDF_UPLOADS_OPTION, $safe, false); return true; } } if (!function_exists('matewa_agency_client_pdf_get_record_for_pack')) { function matewa_agency_client_pdf_get_record_for_pack($pack_id) { $pack_id = sanitize_key((string) $pack_id); if ('' === $pack_id) { return array(); } $records = matewa_agency_client_pdf_get_records(); return isset($records[$pack_id]) ? $records[$pack_id] : array(); } } if (!function_exists('matewa_agency_client_pdf_pack_exists')) { function matewa_agency_client_pdf_pack_exists($pack_id) { $pack_id = sanitize_key((string) $pack_id); if ('' === $pack_id || !function_exists('matewa_agency_client_pack_get_records')) { return false; } foreach (matewa_agency_client_pack_get_records() as $record) { $record_id = isset($record['setup_pack_id']) ? sanitize_key((string) $record['setup_pack_id']) : ''; if ('' !== $record_id && hash_equals($record_id, $pack_id)) { return true; } } return false; } } if (!function_exists('matewa_agency_client_pdf_has_available_bundle_seat')) { function matewa_agency_client_pdf_has_available_bundle_seat() { if (!function_exists('matewa_agency_bundle_get_records') || !function_exists('matewa_agency_client_pack_find_available_bundle_index')) { return false; } $bundle_records = matewa_agency_bundle_get_records(); return matewa_agency_client_pack_find_available_bundle_index($bundle_records) >= 0; } } if (!function_exists('matewa_agency_client_pdf_draft_name_from_file')) { function matewa_agency_client_pdf_draft_name_from_file($file_name) { $file_name = sanitize_file_name((string) $file_name); $base = preg_replace('/\.pdf$/i', '', $file_name); $base = preg_replace('/[\-_]+/', ' ', (string) $base); $base = preg_replace('/\b(247chatmate|client|setup|form|completed|complete|pdf|review|copy|final)\b/i', ' ', (string) $base); $base = preg_replace('/\s+/', ' ', (string) $base); $base = trim((string) $base, " \t\n\r\0\x0B-_"); if ('' === $base) { return 'PDF Review - ' . date_i18n('j M Y H:i'); } $base = sanitize_text_field($base); if (function_exists('mb_substr')) { $base = mb_substr($base, 0, 72); } else { $base = substr($base, 0, 72); } return trim($base); } } if (!function_exists('matewa_agency_client_pdf_decode_pdf_string_value')) { function matewa_agency_client_pdf_decode_pdf_string_value($raw) { $raw = (string) $raw; $out = ''; $length = strlen($raw); for ($i = 0; $i < $length; $i++) { $char = $raw[$i]; if ('\\' !== $char) { $out .= $char; continue; } $i++; if ($i >= $length) { break; } $next = $raw[$i]; if (preg_match('/[0-7]/', $next)) { $octal = $next; for ($j = 0; $j < 2 && ($i + 1) < $length && preg_match('/[0-7]/', $raw[$i + 1]); $j++) { $i++; $octal .= $raw[$i]; } $out .= chr(octdec($octal)); continue; } switch ($next) { case 'n': $out .= "\n"; break; case 'r': $out .= "\r"; break; case 't': $out .= "\t"; break; case 'b': $out .= "\x08"; break; case 'f': $out .= "\x0c"; break; case '(': case ')': case '\\': $out .= $next; break; case "\r": if (($i + 1) < $length && "\n" === $raw[$i + 1]) { $i++; } break; case "\n": break; default: $out .= $next; break; } } if (strlen($out) >= 2 && "\xfe\xff" === substr($out, 0, 2) && function_exists('mb_convert_encoding')) { $converted = @mb_convert_encoding(substr($out, 2), 'UTF-8', 'UTF-16BE'); if (is_string($converted) && '' !== $converted) { $out = $converted; } } $out = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]+/', ' ', (string) $out); $out = preg_replace('/[ \t]+/', ' ', (string) $out); $out = preg_replace('/\s*\n\s*/', "\n", (string) $out); return trim((string) $out); } } if (!function_exists('matewa_agency_client_pdf_decode_pdf_hex_string')) { function matewa_agency_client_pdf_decode_pdf_hex_string($raw) { $hex = preg_replace('/[^0-9a-f]/i', '', (string) $raw); if ('' === $hex) { return ''; } if (1 === (strlen($hex) % 2)) { $hex .= '0'; } $bytes = @hex2bin($hex); if (!is_string($bytes)) { return ''; } return matewa_agency_client_pdf_decode_pdf_string_value($bytes); } } if (!function_exists('matewa_agency_client_pdf_decode_pdf_name_token')) { function matewa_agency_client_pdf_decode_pdf_name_token($raw) { $raw = (string) $raw; $raw = preg_replace_callback('/#([0-9A-Fa-f]{2})/', function ($matches) { return chr(hexdec($matches[1])); }, $raw); $raw = str_replace(array(' ', '-'), '_', $raw); return $raw; } } if (!function_exists('matewa_agency_client_pdf_parse_objects')) { function matewa_agency_client_pdf_parse_objects($content) { $content = (string) $content; $objects = array(); if (!preg_match_all('/(\d+)\s+(\d+)\s+obj\b(.*?)endobj/s', $content, $matches, PREG_SET_ORDER)) { return $objects; } foreach ($matches as $match) { $object_id = isset($match[1]) ? absint($match[1]) : 0; if ($object_id <= 0) { continue; } $objects[$object_id] = isset($match[3]) ? (string) $match[3] : ''; } /* * Some PDF editors save form widgets inside compressed PDF object streams * (/Type /ObjStm). The first PDF import pass only inspected normal top-level * objects, so valid AcroForm fields such as business_name and business_type * could be missed even though the PDF still contained proper fillable fields. * Expand object streams into the same object map before field extraction. */ $object_streams = $objects; foreach ($object_streams as $stream_object) { if (false === strpos($stream_object, '/ObjStm') || false === strpos($stream_object, 'stream')) { continue; } $first = null; $count = null; if (preg_match('/\/First\s+(\d+)/', $stream_object, $first_match)) { $first = isset($first_match[1]) ? absint($first_match[1]) : null; } if (preg_match('/\/N\s+(\d+)/', $stream_object, $count_match)) { $count = isset($count_match[1]) ? absint($count_match[1]) : null; } if (null === $first || null === $count || $first <= 0 || $count <= 0 || $count > 300) { continue; } $decoded = function_exists('matewa_agency_client_pdf_decode_stream_data') ? matewa_agency_client_pdf_decode_stream_data($stream_object) : ''; if (!is_string($decoded) || '' === $decoded || strlen($decoded) <= $first) { continue; } $header = substr($decoded, 0, $first); $body = substr($decoded, $first); $tokens = preg_split('/\s+/', trim((string) $header)); $tokens = is_array($tokens) ? array_values(array_filter($tokens, 'strlen')) : array(); if (count($tokens) < ($count * 2)) { continue; } $pairs = array(); for ($i = 0; $i < ($count * 2); $i += 2) { if (!isset($tokens[$i], $tokens[$i + 1]) || !is_numeric($tokens[$i]) || !is_numeric($tokens[$i + 1])) { continue 2; } $pairs[] = array(absint($tokens[$i]), absint($tokens[$i + 1])); } $body_length = strlen($body); foreach ($pairs as $index => $pair) { $embedded_object_id = isset($pair[0]) ? absint($pair[0]) : 0; $offset = isset($pair[1]) ? absint($pair[1]) : 0; if ($embedded_object_id <= 0 || $offset < 0 || $offset >= $body_length) { continue; } $next_offset = $body_length; if (isset($pairs[$index + 1][1])) { $next_offset = absint($pairs[$index + 1][1]); } if ($next_offset <= $offset || $next_offset > $body_length) { $next_offset = $body_length; } $embedded_object = trim((string) substr($body, $offset, $next_offset - $offset)); if ('' === $embedded_object) { continue; } $objects[$embedded_object_id] = $embedded_object; } } ksort($objects, SORT_NUMERIC); return $objects; } } if (!function_exists('matewa_agency_client_pdf_read_token_after_key')) { function matewa_agency_client_pdf_read_token_after_key($source, $key) { $source = (string) $source; $key = (string) $key; $pattern = '/' . preg_quote($key, '/') . '(?![A-Za-z0-9_#])/'; if (!preg_match($pattern, $source, $key_match, PREG_OFFSET_CAPTURE)) { return null; } $pos = isset($key_match[0][1]) ? (int) $key_match[0][1] : -1; if ($pos < 0) { return null; } $i = $pos + strlen($key); $length = strlen($source); while ($i < $length && preg_match('/\s/', $source[$i])) { $i++; } if ($i >= $length) { return null; } $char = $source[$i]; if ('(' === $char) { $i++; $depth = 1; $raw = ''; while ($i < $length && $depth > 0) { $c = $source[$i]; if ('\\' === $c) { $raw .= $c; $i++; if ($i < $length) { $raw .= $source[$i]; if (preg_match('/[0-7]/', $source[$i])) { for ($j = 0; $j < 2 && ($i + 1) < $length && preg_match('/[0-7]/', $source[$i + 1]); $j++) { $i++; $raw .= $source[$i]; } } } $i++; continue; } if ('(' === $c) { $depth++; $raw .= $c; $i++; continue; } if (')' === $c) { $depth--; if ($depth > 0) { $raw .= $c; } $i++; continue; } $raw .= $c; $i++; } return matewa_agency_client_pdf_decode_pdf_string_value($raw); } if ('<' === $char && (($i + 1) >= $length || '<' !== $source[$i + 1])) { $end = strpos($source, '>', $i + 1); if (false === $end) { return null; } return matewa_agency_client_pdf_decode_pdf_hex_string(substr($source, $i + 1, $end - $i - 1)); } if ('/' === $char) { $i++; $raw = ''; while ($i < $length && !preg_match('/[\s<>\[\]\(\)\/]/', $source[$i])) { $raw .= $source[$i]; $i++; } return sanitize_key(matewa_agency_client_pdf_decode_pdf_name_token($raw)); } $raw = ''; while ($i < $length && !preg_match('/\s/', $source[$i])) { $raw .= $source[$i]; $i++; } return trim($raw); } } if (!function_exists('matewa_agency_client_pdf_ascii85_decode')) { function matewa_agency_client_pdf_ascii85_decode($data) { $data = preg_replace('/\s+/', '', (string) $data); $data = str_replace(array('<~', '~>'), '', $data); $out = ''; $group = array(); $length = strlen($data); for ($i = 0; $i < $length; $i++) { $ch = $data[$i]; if ('z' === $ch && empty($group)) { $out .= "\0\0\0\0"; continue; } $ord = ord($ch); if ($ord < 33 || $ord > 117) { continue; } $group[] = $ord - 33; if (5 === count($group)) { $value = 0; foreach ($group as $digit) { $value = ($value * 85) + $digit; } $out .= chr(($value >> 24) & 255) . chr(($value >> 16) & 255) . chr(($value >> 8) & 255) . chr($value & 255); $group = array(); } } if (!empty($group)) { $count = count($group); while (count($group) < 5) { $group[] = 84; } $value = 0; foreach ($group as $digit) { $value = ($value * 85) + $digit; } $bytes = chr(($value >> 24) & 255) . chr(($value >> 16) & 255) . chr(($value >> 8) & 255) . chr($value & 255); $out .= substr($bytes, 0, max(0, $count - 1)); } return $out; } } if (!function_exists('matewa_agency_client_pdf_decode_stream_data')) { function matewa_agency_client_pdf_decode_stream_data($object) { $object = (string) $object; if (!preg_match('/stream\r?\n(.*?)\r?\nendstream/s', $object, $stream_match)) { if (!preg_match('/stream\r(.*?)\rendstream/s', $object, $stream_match)) { return ''; } } $data = isset($stream_match[1]) ? (string) $stream_match[1] : ''; if ('' === $data) { return ''; } $filters = array(); if (preg_match('/\/Filter\s*\[([^\]]+)\]/s', $object, $filter_match)) { if (preg_match_all('/\/([A-Za-z0-9]+)/', $filter_match[1], $filter_names)) { $filters = $filter_names[1]; } } elseif (preg_match('/\/Filter\s*\/([A-Za-z0-9]+)/', $object, $filter_match)) { $filters = array($filter_match[1]); } foreach ($filters as $filter) { $filter = (string) $filter; if ('ASCII85Decode' === $filter || 'A85' === $filter) { $data = matewa_agency_client_pdf_ascii85_decode($data); } elseif ('FlateDecode' === $filter || 'Fl' === $filter) { $decoded = @gzuncompress($data); if (!is_string($decoded)) { $decoded = @gzinflate($data); } if (!is_string($decoded)) { $decoded = @gzdecode($data); } if (is_string($decoded) && '' !== $decoded) { $data = $decoded; } } } return is_string($data) ? $data : ''; } } if (!function_exists('matewa_agency_client_pdf_extract_text_chunks_from_stream')) { function matewa_agency_client_pdf_extract_text_chunks_from_stream($stream) { $stream = (string) $stream; if ('' === $stream) { return array(); } $chunks = array(); if (preg_match_all('/\(((?:\\.|[^\\()])*)\)\s*Tj\b/s', $stream, $matches)) { foreach ($matches[1] as $raw) { $text = matewa_agency_client_pdf_decode_pdf_string_value($raw); $text = trim(preg_replace('/\s+/', ' ', (string) $text)); if ('' !== $text) { $chunks[] = $text; } } } if (preg_match_all('/<([0-9A-Fa-f\s]+)>\s*Tj\b/s', $stream, $matches)) { foreach ($matches[1] as $raw) { $text = matewa_agency_client_pdf_decode_pdf_hex_string($raw); $text = trim(preg_replace('/\s+/', ' ', (string) $text)); if ('' !== $text) { $chunks[] = $text; } } } if (preg_match_all('/\[(.*?)\]\s*TJ\b/s', $stream, $matches)) { foreach ($matches[1] as $array_source) { $parts = array(); if (preg_match_all('/\(((?:\\.|[^\\()])*)\)|<([0-9A-Fa-f\s]+)>/s', $array_source, $part_matches, PREG_SET_ORDER)) { foreach ($part_matches as $part) { if (isset($part[1]) && '' !== $part[1]) { $parts[] = matewa_agency_client_pdf_decode_pdf_string_value($part[1]); } elseif (isset($part[2]) && '' !== $part[2]) { $parts[] = matewa_agency_client_pdf_decode_pdf_hex_string($part[2]); } } } $text = trim(preg_replace('/\s+/', ' ', implode('', $parts))); if ('' !== $text) { $chunks[] = $text; } } } $out = array(); foreach ($chunks as $chunk) { $chunk = trim(wp_strip_all_tags((string) $chunk)); if ('' === $chunk) { continue; } $out[] = $chunk; } return array_values(array_unique($out)); } } if (!function_exists('matewa_agency_client_pdf_first_safe_appearance_text')) { function matewa_agency_client_pdf_first_safe_appearance_text($texts) { $texts = is_array($texts) ? $texts : array(); foreach ($texts as $text) { $text = trim((string) $text); if ('' === $text) { continue; } $normal = strtolower($text); if (in_array($normal, array('off', 'yes', 'on', 'true', 'false'), true)) { continue; } if (strlen($text) > 500) { $text = substr($text, 0, 500); } return $text; } return ''; } } if (!function_exists('matewa_agency_client_pdf_extract_fillable_fields_from_file')) { function matewa_agency_client_pdf_extract_fillable_fields_from_file($file_path) { $file_path = (string) $file_path; if ('' === $file_path || !is_readable($file_path)) { return array(); } $size = @filesize($file_path); if (false !== $size && $size > 12 * 1024 * 1024) { return array(); } $content = @file_get_contents($file_path); if (!is_string($content) || '' === $content) { return array(); } $wanted = array( 'business_name' => true, 'website_domain' => true, 'client_contact_name' => true, 'main_admin_email' => true, 'leads_email' => true, 'whatsapp_mobile' => true, 'business_type' => true, 'country_language' => true, 'placement_homepage' => true, 'placement_all_pages' => true, 'placement_specific_pages' => true, 'visitor_button_notes' => true, 'google_business_profile' => true, 'instagram_link' => true, 'facebook_link' => true, 'review_link' => true, 'additional_notes' => true, 'confirm_correct' => true, 'confirmed_by' => true, 'confirmed_date' => true, ); for ($i = 1; $i <= 8; $i++) { $wanted['page_' . $i] = true; } $fields = array(); $objects = matewa_agency_client_pdf_parse_objects($content); $parsed_objects = array(); foreach ($objects as $object_id => $object) { if (false === strpos($object, '/T') && false === strpos($object, '/V') && false === strpos($object, '/Parent') && false === strpos($object, 'stream')) { continue; } $name = matewa_agency_client_pdf_read_token_after_key($object, '/T'); $name = is_scalar($name) ? sanitize_key((string) $name) : ''; $value = matewa_agency_client_pdf_read_token_after_key($object, '/V'); $has_value = null !== $value; $parent_id = 0; if (preg_match('/\/Parent\s+(\d+)\s+\d+\s+R\b/', $object, $parent_match)) { $parent_id = isset($parent_match[1]) ? absint($parent_match[1]) : 0; } $appearance_id = 0; if (preg_match('/\/AP\s*<<.*?\/N\s+(\d+)\s+\d+\s+R.*?>>/s', $object, $appearance_match)) { $appearance_id = isset($appearance_match[1]) ? absint($appearance_match[1]) : 0; } $stream_texts = array(); if (function_exists('matewa_agency_client_pdf_decode_stream_data') && function_exists('matewa_agency_client_pdf_extract_text_chunks_from_stream')) { $stream_texts = matewa_agency_client_pdf_extract_text_chunks_from_stream(matewa_agency_client_pdf_decode_stream_data($object)); } $parsed_objects[$object_id] = array( 'name' => $name, 'value' => $has_value && is_scalar($value) ? trim((string) $value) : '', 'has_value' => $has_value, 'parent_id' => $parent_id, 'appearance_id' => $appearance_id, 'stream_texts' => $stream_texts, ); } /* First pass: standard AcroForm fields where /T and /V live in the same object. */ foreach ($parsed_objects as $parsed) { $name = isset($parsed['name']) ? sanitize_key((string) $parsed['name']) : ''; if ('' === $name || !isset($wanted[$name]) || empty($parsed['has_value'])) { continue; } $value = isset($parsed['value']) && is_scalar($parsed['value']) ? trim((string) $parsed['value']) : ''; if (!isset($fields[$name]) || '' === $fields[$name] || '' !== $value) { $fields[$name] = $value; } } /* Second pass: some PDF editors save /V on a child widget and keep /T on the parent field. */ foreach ($parsed_objects as $parsed) { if (empty($parsed['has_value'])) { continue; } $name = isset($parsed['name']) ? sanitize_key((string) $parsed['name']) : ''; if ('' === $name && !empty($parsed['parent_id']) && isset($parsed_objects[$parsed['parent_id']]['name'])) { $name = sanitize_key((string) $parsed_objects[$parsed['parent_id']]['name']); } if ('' === $name || !isset($wanted[$name])) { continue; } $value = isset($parsed['value']) && is_scalar($parsed['value']) ? trim((string) $parsed['value']) : ''; if (!isset($fields[$name]) || '' === $fields[$name] || '' !== $value) { $fields[$name] = $value; } } /* Third pass: if a parent has a value but a child provided the display name, keep the non-empty value. */ foreach ($parsed_objects as $object_id => $parsed) { if (!empty($parsed['has_value']) || empty($parsed['name'])) { continue; } $name = sanitize_key((string) $parsed['name']); if ('' === $name || !isset($wanted[$name])) { continue; } foreach ($parsed_objects as $child) { $child_parent = isset($child['parent_id']) ? absint($child['parent_id']) : 0; if ($child_parent !== absint($object_id) || empty($child['has_value'])) { continue; } $value = isset($child['value']) && is_scalar($child['value']) ? trim((string) $child['value']) : ''; if (!isset($fields[$name]) || '' === $fields[$name] || '' !== $value) { $fields[$name] = $value; } } } /* Fourth pass: recover values from PDF appearance streams when a PDF editor draws text but does not save /V cleanly. */ foreach ($parsed_objects as $object_id => $parsed) { $name = isset($parsed['name']) ? sanitize_key((string) $parsed['name']) : ''; if ('' === $name || !isset($wanted[$name])) { continue; } if (isset($fields[$name]) && '' !== trim((string) $fields[$name]) && 'off' !== sanitize_key((string) $fields[$name])) { continue; } $appearance_text = ''; if (!empty($parsed['stream_texts']) && function_exists('matewa_agency_client_pdf_first_safe_appearance_text')) { $appearance_text = matewa_agency_client_pdf_first_safe_appearance_text($parsed['stream_texts']); } $appearance_id = isset($parsed['appearance_id']) ? absint($parsed['appearance_id']) : 0; if ('' === $appearance_text && $appearance_id > 0 && isset($parsed_objects[$appearance_id]['stream_texts']) && function_exists('matewa_agency_client_pdf_first_safe_appearance_text')) { $appearance_text = matewa_agency_client_pdf_first_safe_appearance_text($parsed_objects[$appearance_id]['stream_texts']); } if ('' === $appearance_text) { $next_id = absint($object_id) + 1; if (isset($parsed_objects[$next_id]['stream_texts']) && function_exists('matewa_agency_client_pdf_first_safe_appearance_text')) { $appearance_text = matewa_agency_client_pdf_first_safe_appearance_text($parsed_objects[$next_id]['stream_texts']); } } if ('' !== $appearance_text) { $fields[$name] = $appearance_text; } } foreach ($wanted as $wanted_name => $_enabled) { if (!isset($fields[$wanted_name])) { $fields[$wanted_name] = ''; } } foreach ($fields as $field_name => $value) { if (0 === strpos($field_name, 'placement_') || in_array($field_name, array('confirm_correct'), true)) { $value = sanitize_key((string) $value); $fields[$field_name] = in_array($value, array('yes', 'on', 'true', '1'), true) ? 'yes' : 'off'; } else { $value = wp_strip_all_tags((string) $value); $value = str_replace(array("\r\n", "\r"), "\n", $value); $value = preg_replace('/[ \t]+/', ' ', $value); $fields[$field_name] = trim((string) $value); } } return $fields; } } if (!function_exists('matewa_agency_client_pdf_field_value')) { function matewa_agency_client_pdf_field_value($fields, $key) { $fields = is_array($fields) ? $fields : array(); $key = sanitize_key((string) $key); return isset($fields[$key]) && is_scalar($fields[$key]) ? trim((string) $fields[$key]) : ''; } } if (!function_exists('matewa_agency_client_pdf_checkbox_is_on')) { function matewa_agency_client_pdf_checkbox_is_on($fields, $key) { $value = sanitize_key(matewa_agency_client_pdf_field_value($fields, $key)); return in_array($value, array('yes', 'on', 'true', '1'), true); } } if (!function_exists('matewa_agency_client_pdf_build_page_items_from_fields')) { function matewa_agency_client_pdf_build_page_items_from_fields($fields, $client_domain = '') { $fields = is_array($fields) ? $fields : array(); $client_domain = function_exists('matewa_agency_client_pack_normalise_domain') ? matewa_agency_client_pack_normalise_domain($client_domain) : sanitize_text_field((string) $client_domain); $pages = array(); for ($i = 1; $i <= 8; $i++) { $raw = matewa_agency_client_pdf_field_value($fields, 'page_' . $i); if ('' === $raw) { continue; } $raw = trim($raw); if ('' !== $client_domain && 0 === strpos($raw, '/')) { $raw = 'https://' . $client_domain . '/' . ltrim($raw, '/'); } elseif ('' !== $client_domain && !preg_match('#^https?://#i', $raw) && false === strpos($raw, '.')) { $raw = 'https://' . $client_domain . '/' . ltrim($raw, '/'); } $url = function_exists('matewa_agency_client_pack_clean_page_url') ? matewa_agency_client_pack_clean_page_url($raw, $client_domain) : esc_url_raw($raw); if ('' === $url) { continue; } $path = wp_parse_url($url, PHP_URL_PATH); $path = trim((string) $path, '/'); $title = '' === $path ? 'Homepage' : ucwords(str_replace(array('-', '_', '/'), ' ', $path)); $pages[] = array( 'url' => $url, 'title' => $title, 'source' => 'pdf', ); } return function_exists('matewa_agency_client_pack_clean_page_list') ? matewa_agency_client_pack_clean_page_list($pages, $client_domain, 8) : $pages; } } if (!function_exists('matewa_agency_client_pdf_build_import_data_from_fields')) { function matewa_agency_client_pdf_build_import_data_from_fields($fields, $file_name = '', $business_type_hint = '') { $fields = is_array($fields) ? $fields : array(); $client_business = sanitize_text_field(matewa_agency_client_pdf_field_value($fields, 'business_name')); $client_domain = function_exists('matewa_agency_client_pack_normalise_domain') ? matewa_agency_client_pack_normalise_domain(matewa_agency_client_pdf_field_value($fields, 'website_domain')) : sanitize_text_field(matewa_agency_client_pdf_field_value($fields, 'website_domain')); $client_admin_email = sanitize_email(matewa_agency_client_pdf_field_value($fields, 'main_admin_email')); $leads_email = sanitize_email(matewa_agency_client_pdf_field_value($fields, 'leads_email')); $whatsapp_number = function_exists('matewa_agency_client_pack_clean_phone') ? matewa_agency_client_pack_clean_phone(matewa_agency_client_pdf_field_value($fields, 'whatsapp_mobile')) : sanitize_text_field(matewa_agency_client_pdf_field_value($fields, 'whatsapp_mobile')); $business_type_text = sanitize_text_field(matewa_agency_client_pdf_field_value($fields, 'business_type')); $selected_pages = matewa_agency_client_pdf_build_page_items_from_fields($fields, $client_domain); if (!empty($selected_pages) || matewa_agency_client_pdf_checkbox_is_on($fields, 'placement_specific_pages')) { $page_mode = 'specific_pages'; } elseif (matewa_agency_client_pdf_checkbox_is_on($fields, 'placement_homepage')) { $page_mode = 'homepage'; } else { $page_mode = 'all_pages'; } $raw_summary = array(); foreach (array( 'business_name', 'website_domain', 'main_admin_email', 'leads_email', 'whatsapp_mobile', 'business_type', 'country_language', 'visitor_button_notes', 'google_business_profile', 'instagram_link', 'facebook_link', 'review_link', 'additional_notes', 'confirmed_by', 'confirmed_date' ) as $field_key) { $value = matewa_agency_client_pdf_field_value($fields, $field_key); if ('' !== $value) { $raw_summary[$field_key] = sanitize_textarea_field($value); } } $found = 0; foreach ($fields as $value) { if (is_scalar($value) && '' !== trim((string) $value) && 'off' !== sanitize_key((string) $value)) { $found++; } } return array( 'client_business' => $client_business, 'client_domain' => $client_domain, 'client_admin_email' => $client_admin_email, 'leads_email' => $leads_email, 'whatsapp_number' => $whatsapp_number, 'business_type_text' => $business_type_text, 'country_language' => sanitize_text_field(matewa_agency_client_pdf_field_value($fields, 'country_language')), 'page_mode' => $page_mode, 'detected_pages' => $selected_pages, 'selected_pages' => $selected_pages, 'extracted_fields' => $raw_summary, 'extracted_field_count' => absint($found), 'has_extracted_fields' => $found > 0, ); } } if (!function_exists('matewa_agency_client_pdf_match_normalise_text')) { function matewa_agency_client_pdf_match_normalise_text($value) { $value = is_scalar($value) ? (string) $value : ''; $value = html_entity_decode(wp_strip_all_tags($value), ENT_QUOTES, 'UTF-8'); $value = strtolower($value); $value = str_replace(array('&', '+'), ' and ', $value); $value = preg_replace('/[^a-z0-9]+/i', ' ', (string) $value); $value = preg_replace('/\s+/', ' ', (string) $value); return trim((string) $value); } } if (!function_exists('matewa_agency_client_pdf_match_text_contains_phrase')) { function matewa_agency_client_pdf_match_text_contains_phrase($haystack, $phrase) { $haystack = ' ' . matewa_agency_client_pdf_match_normalise_text($haystack) . ' '; $phrase = matewa_agency_client_pdf_match_normalise_text($phrase); if ('' === $phrase) { return false; } if (false !== strpos($haystack, ' ' . $phrase . ' ')) { return true; } $words = preg_split('/\s+/', $phrase); if (!is_array($words) || empty($words)) { return false; } $last_index = count($words) - 1; if (isset($words[$last_index]) && strlen((string) $words[$last_index]) > 3 && 's' !== substr((string) $words[$last_index], -1)) { $words[$last_index] .= 's'; return false !== strpos($haystack, ' ' . implode(' ', $words) . ' '); } return false; } } if (!function_exists('matewa_agency_client_pdf_business_type_synonyms')) { function matewa_agency_client_pdf_business_type_synonyms() { return array( 'aerial_installer' => array('aerial installer', 'tv aerial', 'satellite installer', 'antenna installer'), 'appliance_repair' => array('appliance repair', 'appliance repairs', 'washing machine repair', 'oven repair', 'dishwasher repair'), 'bathroom_fitter' => array('bathroom fitter', 'bathroom fitters', 'bathroom installer', 'bathroom installation'), 'builder' => array('builder', 'builders', 'building contractor', 'building work'), 'car_dealer' => array('car dealer', 'car dealers', 'used cars', 'used car', 'car sales', 'vehicle sales', 'motors', 'cars'), 'car_detailing' => array('car detailing', 'detailing', 'car valeting', 'valeting'), 'car_repair' => array('car repair', 'car repairs', 'mechanic', 'mechanics', 'garage', 'vehicle repair', 'vehicle repairs', 'auto repair'), 'carpenter_joiner' => array('carpenter', 'carpenters', 'joiner', 'joiners', 'joinery', 'carpentry'), 'cleaner' => array('cleaner', 'cleaners', 'cleaning company', 'domestic cleaning', 'commercial cleaning'), 'decorator' => array('decorator', 'decorators', 'painter', 'painters', 'painting', 'decorating', 'painter decorator'), 'painter_decorator' => array('decorator', 'decorators', 'painter', 'painters', 'painting', 'decorating', 'painter decorator'), 'drainage' => array('drainage', 'drains', 'blocked drain', 'blocked drains', 'drain unblocking'), 'electric_gates' => array('electric gates', 'gate automation', 'automated gates'), 'electrician' => array('electrician', 'electricians', 'electrical', 'electrical contractor', 'sparky'), 'fencer' => array('fencer', 'fencing', 'fence installer', 'fence installation'), 'flooring_installer' => array('flooring installer', 'floor fitter', 'flooring', 'carpet fitter', 'laminate floor'), 'gardener' => array('gardener', 'gardeners', 'gardening', 'garden maintenance'), 'glazier' => array('glazier', 'glazing', 'glass repair', 'window glass'), 'guttering_fascias_soffits'=> array('guttering', 'gutters', 'fascias', 'soffits', 'fascia soffit'), 'handyman' => array('handyman', 'handy man', 'property maintenance'), 'home_networking' => array('home networking', 'wifi installer', 'network installer', 'home wifi'), 'hvac' => array('hvac', 'air conditioning', 'air con', 'heating ventilation'), 'it_services' => array('it services', 'it support', 'computer support', 'managed it'), 'landscaper' => array('landscaper', 'landscapers', 'landscaping', 'garden design'), 'locksmith' => array('locksmith', 'locksmiths', 'locks', 'lock repair'), 'mobile_mechanic' => array('mobile mechanic', 'mobile mechanics', 'mobile car repair', 'mobile vehicle repair'), 'mot_garage' => array('mot garage', 'mot test', 'mot testing', 'mot'), 'plasterer' => array('plasterer', 'plasterers', 'plastering', 'renderer', 'rendering'), 'plumber' => array('plumber', 'plumbers', 'plumbing', 'boiler', 'boilers', 'gas engineer', 'heating engineer', 'central heating'), 'roofer' => array('roofer', 'roofers', 'roofing', 'roof repair', 'roof repairs', 'leaking roof'), 'tiler' => array('tiler', 'tilers', 'tiling', 'wall tiling', 'floor tiling'), 'tree_surgeon' => array('tree surgeon', 'tree surgeons', 'tree surgery', 'arborist'), 'tyre_fitting' => array('tyre fitting', 'tyres', 'tyre', 'tire', 'tires'), 'window_cleaner' => array('window cleaner', 'window cleaners', 'window cleaning'), 'window_door_installer' => array('window installer', 'door installer', 'windows and doors', 'double glazing'), ); } } if (!function_exists('matewa_agency_client_pdf_trade_match_groups')) { function matewa_agency_client_pdf_trade_match_groups() { return array( 'plumber' => array( 'source' => array('plumber', 'plumbers', 'plumbing', 'boiler', 'boilers', 'gas engineer', 'gas engineers', 'heating engineer', 'heating engineers', 'central heating'), 'candidate' => array('plumber', 'plumbers', 'plumbing', 'boiler', 'boilers', 'gas', 'heating', 'central heating'), ), 'electrician' => array( 'source' => array('electrician', 'electricians', 'electrical', 'electrical contractor', 'sparky'), 'candidate' => array('electrician', 'electricians', 'electrical', 'electric'), ), 'roofer' => array( 'source' => array('roofer', 'roofers', 'roofing', 'roof repair', 'leaking roof'), 'candidate' => array('roofer', 'roofers', 'roofing', 'roof'), ), 'car_dealer' => array( 'source' => array('car dealer', 'used cars', 'used car', 'car sales', 'vehicle sales', 'jims cars', 'cars'), 'candidate' => array('car dealer', 'used cars', 'used car', 'car sales', 'vehicle sales', 'dealer', 'dealership', 'motors'), ), 'car_repair' => array( 'source' => array('car repair', 'car repairs', 'mechanic', 'mechanics', 'garage', 'vehicle repair', 'auto repair'), 'candidate' => array('car repair', 'vehicle repair', 'mechanic', 'garage', 'auto repair'), ), 'mobile_mechanic' => array( 'source' => array('mobile mechanic', 'mobile mechanics', 'mobile car repair', 'mobile vehicle repair'), 'candidate' => array('mobile mechanic', 'mobile mechanics', 'mobile car repair', 'mobile vehicle repair'), ), 'cleaner' => array( 'source' => array('cleaner', 'cleaners', 'cleaning', 'domestic cleaning', 'commercial cleaning'), 'candidate' => array('cleaner', 'cleaners', 'cleaning'), ), 'gardener' => array( 'source' => array('gardener', 'gardeners', 'gardening', 'garden maintenance'), 'candidate' => array('gardener', 'gardeners', 'gardening', 'garden maintenance'), ), 'locksmith' => array( 'source' => array('locksmith', 'locksmiths', 'locks'), 'candidate' => array('locksmith', 'locksmiths', 'locks'), ), 'carpenter_joiner' => array( 'source' => array('carpenter', 'carpenters', 'joiner', 'joiners', 'joinery', 'carpentry'), 'candidate' => array('carpenter', 'carpenters', 'joiner', 'joiners', 'joinery', 'carpentry'), ), 'builder' => array( 'source' => array('builder', 'builders', 'building contractor', 'building work'), 'candidate' => array('builder', 'builders', 'building contractor', 'building'), ), ); } } if (!function_exists('matewa_agency_client_pdf_text_has_any_phrase')) { function matewa_agency_client_pdf_text_has_any_phrase($text, $phrases) { $phrases = is_array($phrases) ? $phrases : array(); foreach ($phrases as $phrase) { if (matewa_agency_client_pdf_match_text_contains_phrase($text, $phrase)) { return true; } } return false; } } if (!function_exists('matewa_agency_client_pdf_match_business_type_from_text')) { function matewa_agency_client_pdf_match_business_type_from_text($source_text) { $source_text = matewa_agency_client_pdf_match_normalise_text($source_text); if ('' === $source_text || !function_exists('matewa_agency_client_pack_get_selector_data')) { return array(); } $selector = matewa_agency_client_pack_get_selector_data(); $categories = isset($selector['categories']) && is_array($selector['categories']) ? $selector['categories'] : array(); $synonyms = matewa_agency_client_pdf_business_type_synonyms(); $stop_words = array('and' => true, 'the' => true, 'for' => true, 'with' => true, 'setup' => true, 'pdf' => true, 'review' => true, 'test' => true, 'client' => true, 'business' => true, 'company' => true, 'limited' => true, 'ltd' => true, 'uk' => true, 'service' => true, 'services' => true); $best = array(); $best_score = 0; foreach ($categories as $category_key => $category_data) { $category_key = sanitize_key((string) $category_key); $options = isset($category_data['options']) && is_array($category_data['options']) ? $category_data['options'] : array(); foreach ($options as $vertical_key => $vertical_data) { $vertical_key = sanitize_key((string) $vertical_key); if ('' === $vertical_key || 'general' === $vertical_key) { continue; } $label = isset($vertical_data['display_label']) ? sanitize_text_field((string) $vertical_data['display_label']) : ucwords(str_replace('_', ' ', $vertical_key)); $phrases = array($vertical_key, str_replace('_', ' ', $vertical_key), $label); if (isset($synonyms[$vertical_key]) && is_array($synonyms[$vertical_key])) { $phrases = array_merge($phrases, $synonyms[$vertical_key]); } $score = 0; $unique_phrases = array_unique(array_filter($phrases)); foreach ($unique_phrases as $phrase) { $normalised_phrase = matewa_agency_client_pdf_match_normalise_text($phrase); if ('' !== $normalised_phrase && $source_text === $normalised_phrase) { $exact_score = 240 + strlen($normalised_phrase); if ($vertical_key === sanitize_key(str_replace(' ', '_', $normalised_phrase))) { $exact_score += 30; } if (isset($synonyms[$vertical_key]) && in_array($phrase, $synonyms[$vertical_key], true)) { $exact_score += 20; } $score = max($score, $exact_score); } } foreach ($unique_phrases as $phrase) { if (matewa_agency_client_pdf_match_text_contains_phrase($source_text, $phrase)) { $phrase_score = 80 + strlen(matewa_agency_client_pdf_match_normalise_text($phrase)); if (isset($synonyms[$vertical_key]) && in_array($phrase, $synonyms[$vertical_key], true)) { $phrase_score += 30; } $score = max($score, $phrase_score); } } if ($score < 70) { $tokens = preg_split('/\s+/', matewa_agency_client_pdf_match_normalise_text($label . ' ' . str_replace('_', ' ', $vertical_key))); $tokens = is_array($tokens) ? array_values(array_unique(array_filter($tokens))) : array(); $meaningful = array(); foreach ($tokens as $token) { $token = trim((string) $token); if (strlen($token) < 4 || isset($stop_words[$token])) { continue; } $meaningful[] = $token; } $hits = 0; foreach ($meaningful as $token) { if (matewa_agency_client_pdf_match_text_contains_phrase($source_text, $token)) { $hits++; } } if (!empty($meaningful) && $hits === count($meaningful)) { $score = max($score, 70 + ($hits * 8)); } elseif ($hits >= 2) { $score = max($score, 64 + ($hits * 6)); } } if ($score < 95 && function_exists('matewa_agency_client_pdf_trade_match_groups')) { $candidate_text = $label . ' ' . str_replace('_', ' ', $vertical_key); foreach (matewa_agency_client_pdf_trade_match_groups() as $trade_group) { if (!is_array($trade_group)) { continue; } $source_phrases = isset($trade_group['source']) && is_array($trade_group['source']) ? $trade_group['source'] : array(); $candidate_phrases = isset($trade_group['candidate']) && is_array($trade_group['candidate']) ? $trade_group['candidate'] : array(); if (matewa_agency_client_pdf_text_has_any_phrase($source_text, $source_phrases) && matewa_agency_client_pdf_text_has_any_phrase($candidate_text, $candidate_phrases)) { $score = max($score, 118); break; } } } if ($score > $best_score) { $best_score = $score; $best = array( 'category_key' => $category_key, 'vertical_key' => $vertical_key, 'vertical_label' => $label, 'score' => $score, ); } } } return $best_score >= 80 ? $best : array(); } } if (!function_exists('matewa_agency_client_pdf_resolve_vertical_selection')) { function matewa_agency_client_pdf_resolve_vertical_selection($file_name, $business_type_hint = '', $pdf_business_type = '', $pdf_client_business = '') { $business_type_hint = sanitize_text_field((string) $business_type_hint); $pdf_business_type = sanitize_text_field((string) $pdf_business_type); $file_name = sanitize_file_name((string) $file_name); $sources = array(); if ('' !== trim($pdf_business_type)) { $sources['pdf_field'] = $pdf_business_type; } if ('' !== trim($business_type_hint)) { $sources['hint'] = $business_type_hint; } if ('' !== trim($file_name)) { $sources['filename'] = preg_replace('/\.pdf$/i', '', $file_name); } foreach ($sources as $source => $text) { $match = matewa_agency_client_pdf_match_business_type_from_text($text); if (!empty($match['vertical_key']) && function_exists('matewa_agency_client_pack_resolve_vertical_selection')) { $selection = matewa_agency_client_pack_resolve_vertical_selection($match['category_key'], $match['vertical_key']); $selection['pdf_trade_match_source'] = $source; $selection['pdf_trade_match_score'] = isset($match['score']) ? absint($match['score']) : 0; return $selection; } } $selection = function_exists('matewa_agency_client_pack_resolve_vertical_selection') ? matewa_agency_client_pack_resolve_vertical_selection('', '') : array( 'category_key' => 'general', 'category_label' => 'General', 'vertical_key' => 'general', 'vertical_label' => 'General', 'chips' => array('Get a quote', 'Pricing', 'Availability', 'Ask a question', 'Book a job', 'Something else'), ); $selection['pdf_trade_match_source'] = ''; $selection['pdf_trade_match_score'] = 0; return $selection; } } if (!function_exists('matewa_agency_client_pdf_create_pending_pack')) { function matewa_agency_client_pdf_create_pending_pack($file_name, $business_type_hint = '', $pdf_import_data = array()) { if (!function_exists('matewa_agency_client_pack_get_records') || !function_exists('matewa_agency_client_pack_store_records') || !function_exists('matewa_agency_client_pack_find_available_bundle_index')) { return new WP_Error('pdf_new_pack_unavailable', 'Client setup pack storage is not available.'); } if (!function_exists('matewa_agency_bundle_get_records') || !function_exists('matewa_agency_bundle_store_records')) { return new WP_Error('pdf_new_pack_unavailable', 'Agency bundle storage is not available.'); } $bundle_records = matewa_agency_bundle_get_records(); $bundle_index = matewa_agency_client_pack_find_available_bundle_index($bundle_records); if ($bundle_index < 0 || empty($bundle_records[$bundle_index]) || !is_array($bundle_records[$bundle_index])) { return new WP_Error('pdf_no_seats', 'No active client seats are available.'); } $bundle = $bundle_records[$bundle_index]; $bundle_id = isset($bundle['bundle_id']) ? sanitize_key((string) $bundle['bundle_id']) : ''; $bundle_used = isset($bundle['used_seats']) ? absint($bundle['used_seats']) : 0; $bundle_seats = isset($bundle['seats']) ? absint($bundle['seats']) : 0; $now = time(); $agency_summary = function_exists('matewa_agency_entitlement_get_summary') ? matewa_agency_entitlement_get_summary() : array(); $agency_summary = is_array($agency_summary) ? $agency_summary : array(); $agency_id = isset($agency_summary['agency_id']) ? sanitize_key((string) $agency_summary['agency_id']) : ''; $pdf_import_data = is_array($pdf_import_data) ? $pdf_import_data : array(); $pdf_business_type_text = isset($pdf_import_data['business_type_text']) ? sanitize_text_field((string) $pdf_import_data['business_type_text']) : ''; $pdf_client_business = isset($pdf_import_data['client_business']) ? sanitize_text_field((string) $pdf_import_data['client_business']) : ''; $vertical_selection = matewa_agency_client_pdf_resolve_vertical_selection($file_name, $business_type_hint, $pdf_business_type_text, $pdf_client_business); $existing_packs = matewa_agency_client_pack_get_records(); $existing_ids = array(); foreach ($existing_packs as $existing_pack) { if (is_array($existing_pack) && !empty($existing_pack['setup_pack_id'])) { $existing_ids[sanitize_key((string) $existing_pack['setup_pack_id'])] = true; } } $seed = $agency_id . '|pdf|' . sanitize_file_name((string) $file_name) . '|' . (string) $now . '|' . (string) wp_rand(10000, 999999); $pack_id = 'pack_' . substr(matewa_agency_client_pack_hash_value($seed), 0, 12); $guard = 0; while (isset($existing_ids[$pack_id]) && $guard < 10) { $seed .= '|' . (string) wp_rand(10000, 999999); $pack_id = 'pack_' . substr(matewa_agency_client_pack_hash_value($seed), 0, 12); $guard++; } if (isset($existing_ids[$pack_id])) { return new WP_Error('pdf_new_pack_unavailable', 'A unique setup pack ID could not be generated.'); } $pdf_client_domain = isset($pdf_import_data['client_domain']) && function_exists('matewa_agency_client_pack_normalise_domain') ? matewa_agency_client_pack_normalise_domain((string) $pdf_import_data['client_domain']) : ''; $pdf_client_admin_email = isset($pdf_import_data['client_admin_email']) ? sanitize_email((string) $pdf_import_data['client_admin_email']) : ''; $pdf_whatsapp_number = isset($pdf_import_data['whatsapp_number']) && function_exists('matewa_agency_client_pack_clean_phone') ? matewa_agency_client_pack_clean_phone((string) $pdf_import_data['whatsapp_number']) : ''; $pdf_leads_email = isset($pdf_import_data['leads_email']) ? sanitize_email((string) $pdf_import_data['leads_email']) : ''; $pdf_page_mode = isset($pdf_import_data['page_mode']) && function_exists('matewa_agency_client_pack_allowed_page_mode') ? matewa_agency_client_pack_allowed_page_mode((string) $pdf_import_data['page_mode']) : 'all_pages'; $pdf_detected_pages = isset($pdf_import_data['detected_pages']) && is_array($pdf_import_data['detected_pages']) && function_exists('matewa_agency_client_pack_clean_page_list') ? matewa_agency_client_pack_clean_page_list($pdf_import_data['detected_pages'], $pdf_client_domain, 8) : array(); $pdf_selected_pages = isset($pdf_import_data['selected_pages']) && is_array($pdf_import_data['selected_pages']) && function_exists('matewa_agency_client_pack_clean_page_list') ? matewa_agency_client_pack_clean_page_list($pdf_import_data['selected_pages'], $pdf_client_domain, 8) : array(); $data = array( 'client_business' => '' !== $pdf_client_business ? $pdf_client_business : matewa_agency_client_pdf_draft_name_from_file($file_name), 'client_domain' => $pdf_client_domain, 'client_admin_email' => $pdf_client_admin_email, 'whatsapp_number' => $pdf_whatsapp_number, 'leads_email' => $pdf_leads_email, 'industry' => isset($vertical_selection['category_label']) ? sanitize_text_field((string) $vertical_selection['category_label']) : 'General', 'business_category' => isset($vertical_selection['category_label']) ? sanitize_text_field((string) $vertical_selection['category_label']) : 'General', 'business_category_key' => isset($vertical_selection['category_key']) ? sanitize_key((string) $vertical_selection['category_key']) : 'general', 'business_type' => isset($vertical_selection['vertical_label']) ? sanitize_text_field((string) $vertical_selection['vertical_label']) : 'General', 'business_type_key' => isset($vertical_selection['vertical_key']) ? sanitize_key((string) $vertical_selection['vertical_key']) : 'general', 'page_mode' => $pdf_page_mode, 'detected_pages' => $pdf_detected_pages, 'selected_pages' => $pdf_selected_pages, 'chip_mode' => 'defaults', 'visitor_buttons' => isset($vertical_selection['chips']) && is_array($vertical_selection['chips']) ? array_values($vertical_selection['chips']) : array(), 'pdf_trade_match_source'=> isset($vertical_selection['pdf_trade_match_source']) ? sanitize_key((string) $vertical_selection['pdf_trade_match_source']) : '', 'pdf_trade_match_score' => isset($vertical_selection['pdf_trade_match_score']) ? absint($vertical_selection['pdf_trade_match_score']) : 0, ); $record = array_merge(array( 'setup_pack_id' => $pack_id, 'agency_id' => $agency_id, 'assigned_bundle_id' => $bundle_id, 'seat_status' => 'assigned', 'licence_status' => 'draft', 'expires_at' => isset($bundle['expires_at']) ? sanitize_text_field((string) $bundle['expires_at']) : '', 'renews_at' => isset($bundle['renews_at']) ? sanitize_text_field((string) $bundle['renews_at']) : '', 'status' => 'pdf_uploaded_review', 'created_at' => $now, 'updated_at' => $now, ), $data); $existing_packs[] = $record; matewa_agency_client_pack_store_records($existing_packs); $bundle_records[$bundle_index]['used_seats'] = min($bundle_seats, $bundle_used + 1); $bundle_records[$bundle_index]['available_seats'] = max(0, $bundle_seats - absint($bundle_records[$bundle_index]['used_seats'])); $bundle_records[$bundle_index]['updated_at'] = $now; matewa_agency_bundle_store_records($bundle_records); if (function_exists('matewa_agency_client_pack_reconcile_bundle_seats')) { matewa_agency_client_pack_reconcile_bundle_seats($existing_packs); } return $record; } } if (!function_exists('matewa_agency_client_pdf_notice_message')) { function matewa_agency_client_pdf_notice_message($code = '') { $code = sanitize_key((string) $code); $messages = array( 'pdf_uploaded' => 'Client setup PDF uploaded successfully.', 'pdf_uploaded_new_pack'=> 'Client setup PDF uploaded and opened for review. Check the prefilled boxes, choose the right business type if needed, then save the setup pack.', 'pdf_uploaded_new_pack_no_fields' => 'Client setup PDF uploaded and opened for review, but no fillable PDF fields were read. Fill the boxes manually or re-save the original fillable PDF and upload again.', 'pdf_removed' => 'Client setup PDF removed.', 'pdf_missing_pack' => 'Choose a saved client setup pack before uploading a PDF.', 'pdf_pack_not_found' => 'Client setup pack could not be found.', 'pdf_missing_file' => 'Choose a client setup PDF file before uploading.', 'pdf_upload_failed' => 'Client setup PDF could not be uploaded. Please try again.', 'pdf_invalid_type' => 'Please upload a PDF file only.', 'pdf_agency_required' => 'Agency Hub access is required before uploading client setup PDFs.', 'pdf_removed_missing' => 'No uploaded PDF was found for that setup pack.', 'pdf_no_seats' => 'No active client seats are available. Add or enable an agency bundle before starting a setup pack from PDF.', 'pdf_new_pack_failed' => 'Client setup PDF uploaded, but the new setup pack could not be started.', 'pdf_new_pack_unavailable' => 'Client setup pack storage is not available right now.', ); return isset($messages[$code]) ? $messages[$code] : ('' !== $code ? 'Client setup PDF status: ' . str_replace('_', ' ', $code) . '.' : ''); } } if (!function_exists('matewa_agency_client_pdf_redirect')) { function matewa_agency_client_pdf_redirect($notice_code, $pack_id = '', $target = 'pdf_panel') { $args = array( 'page' => '247chatmate', 'matewa_saved_section' => 'agency', 'matewa_agency_client_pdf_notice' => sanitize_key((string) $notice_code), ); $pack_id = sanitize_key((string) $pack_id); if ('' !== $pack_id) { $args['matewa_agency_pdf_pack'] = $pack_id; } $target = sanitize_key((string) $target); $anchor = '#matewa-agency-client-pdf-upload-panel'; if ('edit_pack' === $target && '' !== $pack_id) { $args['matewa_agency_edit_pack'] = $pack_id; $anchor = '#matewa-agency-client-pack-editor'; } elseif ('client_packs' === $target) { $anchor = '#matewa-agency-client-packs'; } elseif ('pdf_list' === $target) { $anchor = '#matewa-agency-client-pdf-list'; } wp_safe_redirect(add_query_arg($args, admin_url('admin.php')) . $anchor); exit; } } if (!function_exists('matewa_agency_client_pdf_handle_upload')) { function matewa_agency_client_pdf_handle_upload() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to upload client setup PDFs.', '247chatmate')); } check_admin_referer('matewa_agency_client_pdf_upload', 'matewa_agency_client_pdf_nonce'); $upload_mode = isset($_POST['matewa_agency_client_pdf_upload_mode']) ? sanitize_key(wp_unslash($_POST['matewa_agency_client_pdf_upload_mode'])) : 'existing_pack'; $create_new_pack = 'new_pack' === $upload_mode; $pack_id = isset($_POST['matewa_agency_client_pdf_pack_id']) ? sanitize_key(wp_unslash($_POST['matewa_agency_client_pdf_pack_id'])) : ''; $business_type_hint = isset($_POST['matewa_agency_client_pdf_business_type_hint']) && is_scalar($_POST['matewa_agency_client_pdf_business_type_hint']) ? sanitize_text_field(wp_unslash($_POST['matewa_agency_client_pdf_business_type_hint'])) : ''; if (!function_exists('matewa_agency_hub_is_active') || !matewa_agency_hub_is_active()) { matewa_agency_client_pdf_redirect('pdf_agency_required', $pack_id); } if ($create_new_pack) { if (!matewa_agency_client_pdf_has_available_bundle_seat()) { matewa_agency_client_pdf_redirect('pdf_no_seats'); } } else { if ('' === $pack_id) { matewa_agency_client_pdf_redirect('pdf_missing_pack'); } if (!matewa_agency_client_pdf_pack_exists($pack_id)) { matewa_agency_client_pdf_redirect('pdf_pack_not_found', $pack_id); } } if (empty($_FILES['matewa_agency_client_pdf_file']) || !is_array($_FILES['matewa_agency_client_pdf_file'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- File arrays are validated by the WordPress upload handler below. matewa_agency_client_pdf_redirect('pdf_missing_file', $pack_id); } $file = $_FILES['matewa_agency_client_pdf_file']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- File arrays are validated by the WordPress upload handler below. $upload_error = isset($file['error']) ? absint($file['error']) : UPLOAD_ERR_NO_FILE; if (UPLOAD_ERR_OK !== $upload_error) { matewa_agency_client_pdf_redirect(UPLOAD_ERR_NO_FILE === $upload_error ? 'pdf_missing_file' : 'pdf_upload_failed', $pack_id); } $file_name = isset($file['name']) ? sanitize_file_name((string) $file['name']) : ''; $tmp_name = isset($file['tmp_name']) ? (string) $file['tmp_name'] : ''; if ('' === $file_name || '' === $tmp_name || !is_uploaded_file($tmp_name)) { matewa_agency_client_pdf_redirect('pdf_missing_file', $pack_id); } $allowed_mimes = array('pdf' => 'application/pdf'); $checked = wp_check_filetype_and_ext($tmp_name, $file_name, $allowed_mimes); $checked_ext = isset($checked['ext']) ? strtolower((string) $checked['ext']) : ''; $checked_type = isset($checked['type']) ? strtolower((string) $checked['type']) : ''; if ('pdf' !== $checked_ext || 'application/pdf' !== $checked_type) { matewa_agency_client_pdf_redirect('pdf_invalid_type', $pack_id); } $pdf_raw_fields = function_exists('matewa_agency_client_pdf_extract_fillable_fields_from_file') ? matewa_agency_client_pdf_extract_fillable_fields_from_file($tmp_name) : array(); $pdf_import_data = function_exists('matewa_agency_client_pdf_build_import_data_from_fields') ? matewa_agency_client_pdf_build_import_data_from_fields($pdf_raw_fields, $file_name, $business_type_hint) : array(); $pdf_extracted_field_count = isset($pdf_import_data['extracted_field_count']) ? absint($pdf_import_data['extracted_field_count']) : 0; $pdf_extracted_fields = isset($pdf_import_data['extracted_fields']) && is_array($pdf_import_data['extracted_fields']) ? $pdf_import_data['extracted_fields'] : array(); require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; $attachment_id = media_handle_upload( 'matewa_agency_client_pdf_file', 0, array( 'post_title' => preg_replace('/\.pdf$/i', '', $file_name), 'post_content' => '247ChatMate Agency Hub client setup PDF.', 'post_status' => 'private', ), array( 'test_form' => false, 'mimes' => $allowed_mimes, ) ); if (is_wp_error($attachment_id) || empty($attachment_id)) { matewa_agency_client_pdf_redirect('pdf_upload_failed', $pack_id); } $notice_code = 'pdf_uploaded'; $previous_attachment_id = 0; if ($create_new_pack) { $created_pack = matewa_agency_client_pdf_create_pending_pack($file_name, $business_type_hint, $pdf_import_data); if (is_wp_error($created_pack) || !is_array($created_pack) || empty($created_pack['setup_pack_id'])) { wp_delete_attachment(absint($attachment_id), true); $error_code = is_wp_error($created_pack) ? sanitize_key($created_pack->get_error_code()) : 'pdf_new_pack_failed'; matewa_agency_client_pdf_redirect($error_code ?: 'pdf_new_pack_failed'); } $pack_id = sanitize_key((string) $created_pack['setup_pack_id']); $notice_code = $pdf_extracted_field_count > 0 ? 'pdf_uploaded_new_pack' : 'pdf_uploaded_new_pack_no_fields'; } $records = matewa_agency_client_pdf_get_records(); if (!$create_new_pack) { $previous_attachment_id = isset($records[$pack_id]['attachment_id']) ? absint($records[$pack_id]['attachment_id']) : 0; } $records[$pack_id] = array( 'setup_pack_id' => $pack_id, 'attachment_id' => absint($attachment_id), 'file_name' => $file_name, 'file_url' => wp_get_attachment_url($attachment_id), 'file_type' => 'application/pdf', 'uploaded_at' => time(), 'uploaded_by' => get_current_user_id(), 'extracted_at' => $pdf_extracted_field_count > 0 ? time() : 0, 'extracted_field_count' => $pdf_extracted_field_count, 'extracted_fields' => $pdf_extracted_fields, ); matewa_agency_client_pdf_store_records($records); if ($previous_attachment_id > 0 && $previous_attachment_id !== absint($attachment_id)) { wp_delete_attachment($previous_attachment_id, true); } matewa_agency_client_pdf_redirect($notice_code, $pack_id, $create_new_pack ? 'edit_pack' : 'pdf_panel'); } } add_action('admin_post_matewa_agency_upload_client_pdf', 'matewa_agency_client_pdf_handle_upload'); if (!function_exists('matewa_agency_client_pdf_handle_remove')) { function matewa_agency_client_pdf_handle_remove() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to remove client setup PDFs.', '247chatmate')); } check_admin_referer('matewa_agency_client_pdf_remove', 'matewa_agency_client_pdf_remove_nonce'); $pack_id = isset($_POST['matewa_agency_client_pdf_remove_pack_id']) ? sanitize_key(wp_unslash($_POST['matewa_agency_client_pdf_remove_pack_id'])) : ''; if (!function_exists('matewa_agency_hub_is_active') || !matewa_agency_hub_is_active()) { matewa_agency_client_pdf_redirect('pdf_agency_required', $pack_id); } if ('' === $pack_id) { matewa_agency_client_pdf_redirect('pdf_missing_pack'); } $records = matewa_agency_client_pdf_get_records(); if (empty($records[$pack_id])) { matewa_agency_client_pdf_redirect('pdf_removed_missing', $pack_id); } $attachment_id = isset($records[$pack_id]['attachment_id']) ? absint($records[$pack_id]['attachment_id']) : 0; unset($records[$pack_id]); matewa_agency_client_pdf_store_records($records); if ($attachment_id > 0) { wp_delete_attachment($attachment_id, true); } matewa_agency_client_pdf_redirect('pdf_removed', $pack_id, 'pdf_list'); } } add_action('admin_post_matewa_agency_remove_client_pdf', 'matewa_agency_client_pdf_handle_remove'); if (!function_exists('matewa_agency_client_pdf_render_panel')) { function matewa_agency_client_pdf_render_panel($client_pack_records = null) { $client_pack_records = null === $client_pack_records && function_exists('matewa_agency_client_pack_get_records') ? matewa_agency_client_pack_get_records() : (is_array($client_pack_records) ? array_values($client_pack_records) : array()); $pdf_records = matewa_agency_client_pdf_get_records(); $notice_code = isset($_GET['matewa_agency_client_pdf_notice']) && is_scalar($_GET['matewa_agency_client_pdf_notice']) ? sanitize_key(wp_unslash($_GET['matewa_agency_client_pdf_notice'])) : ''; $selected_pack_id = isset($_GET['matewa_agency_pdf_pack']) && is_scalar($_GET['matewa_agency_pdf_pack']) ? sanitize_key(wp_unslash($_GET['matewa_agency_pdf_pack'])) : ''; if ('' === $selected_pack_id && !empty($client_pack_records[0]['setup_pack_id'])) { $selected_pack_id = sanitize_key((string) $client_pack_records[0]['setup_pack_id']); } $valid_pack_ids = array(); foreach ($client_pack_records as $pack) { if (is_array($pack) && !empty($pack['setup_pack_id'])) { $valid_pack_ids[] = sanitize_key((string) $pack['setup_pack_id']); } } $valid_pack_ids = array_values(array_unique(array_filter($valid_pack_ids))); $visible_pdf_records = array_intersect_key($pdf_records, array_flip($valid_pack_ids)); $notice_message = '' !== $notice_code ? matewa_agency_client_pdf_notice_message($notice_code) : ''; $notice_type = in_array($notice_code, array('pdf_uploaded', 'pdf_uploaded_new_pack', 'pdf_removed'), true) ? 'success' : 'warning'; $has_packs = !empty($client_pack_records); $has_available_seat = matewa_agency_client_pdf_has_available_bundle_seat(); $panel_open = '' !== $notice_code || !empty($visible_pdf_records); ?>
> Client Setup PDF Upload Upload Client Setup PDF
Client Setup PDF Upload Upload the client’s completed setup PDF here. Fillable form fields are read where available, then a reviewable draft setup pack can be started.
Structured PDF import
Start new setup pack from PDF Creates a draft client setup pack, reserves one available client seat, attaches the PDF, and prefills fields from the structured PDF where available.
No client seats available. Add or enable an agency bundle before starting a new setup pack from a PDF.
Drag and drop the completed client setup PDF here or > No PDF selected yet.
This creates a reviewable draft record. Check the prefilled details before export/import.
Attach or replace PDF on existing pack Use this when the client setup pack already exists and you only need to store or replace its source PDF.
No existing setup packs yet. Use the new setup pack upload option first, or create a pack manually below.
Drag and drop the completed client setup PDF here or No PDF selected yet.
Browse or drag in a PDF. Uploading another PDF for the same setup pack will replace the stored PDF reference.
Uploaded PDFs

No client setup PDFs uploaded yet.

  • 0 ? esc_html(' · ' . date_i18n(get_option('date_format', 'j M Y') . ' ' . get_option('time_format', 'H:i'), $uploaded_at)) : ''; ?> 0 ? esc_html(' · ' . $extracted_count . ' PDF fields read') : esc_html(' · no fillable fields read'); ?>
    View / Open PDF