| @@ -3,9 +3,8 @@ | ||
| 3 | 3 | namespace FluentSupport\App\Services; |
| 4 | 4 | |
| 5 | 5 | use FluentSupport\App\App; |
| 6 | 6 | use FluentSupport\App\Models\Agent; |
| 7 | -use FluentSupport\App\Models\Attachment; | |
| 8 | 7 | use FluentSupport\App\Models\Ticket; |
| 9 | 8 | use FluentSupport\App\Models\Conversation; |
| 10 | 9 | use FluentSupport\App\Models\Customer; |
| 11 | 10 | use FluentSupport\App\Models\MailBox; |
| @@ -342,10 +341,9 @@ | ||
| 342 | 341 | |
| 343 | 342 | public static function getTicketViewUrl($ticket) |
| 344 | 343 | { |
| 345 | 344 | $baseUrl = self::getPortalBaseUrl(); |
| 346 | - $ticketNumber = $ticket->serial_number ?? $ticket->id; | |
| 347 | - return $baseUrl . '/#/ticket/' . $ticketNumber . '/view'; | |
| 345 | + return $baseUrl . '/#/ticket/' . $ticket->id . '/view'; | |
| 348 | 346 | } |
| 349 | 347 | |
| 350 | 348 | public static function getTicketViewSignedUrl($ticket) |
| 351 | 349 | { |
| @@ -353,17 +351,16 @@ | ||
| 353 | 351 | return self::getTicketViewUrl($ticket); |
| 354 | 352 | } |
| 355 | 353 | |
| 356 | 354 | $baseUrl = self::getPortalBaseUrl(); |
| 357 | - $ticketNumber = $ticket->serial_number ?? $ticket->id; | |
| 358 | 355 | |
| 359 | 356 | $baseUrl = add_query_arg([ |
| 360 | 357 | 'fs_view' => 'ticket', |
| 361 | 358 | 'support_hash' => $ticket->hash, |
| 362 | - 'ticket_id' => $ticketNumber, | |
| 359 | + 'ticket_id' => $ticket->id, | |
| 363 | 360 | ], $baseUrl); |
| 364 | 361 | |
| 365 | - return $baseUrl . '#/ticket/' . $ticketNumber . '/view'; | |
| 362 | + return $baseUrl . '#/ticket/' . $ticket->id . '/view'; | |
| 366 | 363 | } |
| 367 | 364 | |
| 368 | 365 | public static function saveOpenAIData($objectType, $key, $data) |
| 369 | 366 | { |
| @@ -414,48 +411,13 @@ | ||
| 414 | 411 | */ |
| 415 | 412 | public static function getPortalBaseUrl() |
| 416 | 413 | { |
| 417 | 414 | $businessSettings = self::getBusinessSettings(); |
| 418 | - $portalType = Arr::get($businessSettings, 'ticket_link_portal', 'default'); | |
| 419 | - $baseUrl = null; | |
| 420 | - | |
| 421 | - if (self::isPortalActive($portalType)) { | |
| 422 | - if ($portalType === 'woocommerce' && function_exists('wc_get_endpoint_url') && function_exists('wc_get_page_permalink')) { | |
| 423 | - $accountUrl = wc_get_page_permalink('myaccount'); | |
| 424 | - if ($accountUrl && $accountUrl !== '#') { | |
| 425 | - $baseUrl = wc_get_endpoint_url('support-tickets', '', $accountUrl); | |
| 426 | - } | |
| 427 | - } elseif ($portalType === 'fluent_cart') { | |
| 428 | - $baseUrl = \FluentCart\App\Services\URL::getCustomerDashboardUrl('fluent-support') ?: null; | |
| 429 | - } elseif ($portalType === 'fluent_community') { | |
| 430 | - $baseUrl = \FluentCommunity\App\Services\Helper::baseUrl('support/') ?: null; | |
| 431 | - } | |
| 432 | - } | |
| 433 | - | |
| 434 | - if (!$baseUrl) { | |
| 435 | - $baseUrl = get_permalink(absint(Arr::get($businessSettings, 'portal_page_id'))); | |
| 436 | - } | |
| 437 | - | |
| 438 | - return apply_filters('fluent_support/portal_base_url', rtrim((string) $baseUrl, '/\\')); | |
| 415 | + $baseUrl = get_permalink($businessSettings['portal_page_id']); | |
| 416 | + $baseUrl = rtrim($baseUrl, '/\\'); | |
| 417 | + return apply_filters('fluent_support/portal_base_url', $baseUrl); | |
| 439 | 418 | } |
| 440 | 419 | |
| 441 | - public static function isPortalActive($portalType) | |
| 442 | - { | |
| 443 | - if ($portalType === 'woocommerce') { | |
| 444 | - return defined('FLUENTSUPPORTPRO_PLUGIN_VERSION') && defined('WC_PLUGIN_FILE'); | |
| 445 | - } | |
| 446 | - | |
| 447 | - if ($portalType === 'fluent_cart') { | |
| 448 | - return defined('FLUENTCART_VERSION'); | |
| 449 | - } | |
| 450 | - | |
| 451 | - if ($portalType === 'fluent_community') { | |
| 452 | - return defined('FLUENT_COMMUNITY_PLUGIN_VERSION'); | |
| 453 | - } | |
| 454 | - | |
| 455 | - return false; | |
| 456 | - } | |
| 457 | - | |
| 458 | 420 | public static function getPortalAdminBaseUrl() |
| 459 | 421 | { |
| 460 | 422 | return apply_filters('fluent_support/portal_admin_base_url', admin_url('admin.php?page=fluent-support/#/')); |
| 461 | 423 | } |
| @@ -687,20 +649,13 @@ | ||
| 687 | 649 | } |
| 688 | 650 | |
| 689 | 651 | public static function fluentBotIntegrationStatus() |
| 690 | 652 | { |
| 691 | - // Include object_id and order by id desc to always read the latest row; the | |
| 692 | - // write path (saveFluentBotSettings) does not prune siblings, so duplicates | |
| 693 | - // may exist and a non-deterministic read can return stale state. | |
| 694 | - $meta = Meta::where([ | |
| 695 | - 'object_type' => 'fluent_bot_settings', | |
| 696 | - 'object_id' => 1, | |
| 697 | - 'key' => '_fs_fluent_bot_config' | |
| 698 | - ])->orderByDesc('id')->first(); | |
| 653 | + $settings = static::safeUnserialize( | |
| 654 | + Meta::where('object_type', 'fluent_bot_settings')->value('value') | |
| 655 | + ); | |
| 699 | 656 | |
| 700 | - $settings = static::safeUnserialize($meta ? $meta->value : null); | |
| 701 | - | |
| 702 | - return isset($settings['isEnabled']) && filter_var($settings['isEnabled'], FILTER_VALIDATE_BOOLEAN); | |
| 657 | + return !empty($settings['isEnabled']) && filter_var($settings['isEnabled'], FILTER_VALIDATE_BOOLEAN); | |
| 703 | 658 | } |
| 704 | 659 | |
| 705 | 660 | |
| 706 | 661 | public static function showTicketSummaryAdminBar() |
| @@ -1472,9 +1427,9 @@ | ||
| 1472 | 1427 | ->get(); |
| 1473 | 1428 | return $businessEmailBoxes; |
| 1474 | 1429 | } |
| 1475 | 1430 | |
| 1476 | - public static function tempImageMoveUploadDir($ticketId, $contentType, $replyId = null, $personId = null) | |
| 1431 | + public static function tempImageMoveUploadDir($ticketId, $contentType, $replyId = null) | |
| 1477 | 1432 | { |
| 1478 | 1433 | // Fetch content based on the content type |
| 1479 | 1434 | $content = self::getContentByType($ticketId, $contentType , $replyId); |
| 1480 | 1435 | if (empty($content)) { |
| @@ -1487,9 +1442,9 @@ | ||
| 1487 | 1442 | return; |
| 1488 | 1443 | } |
| 1489 | 1444 | |
| 1490 | 1445 | // Move images to the upload directory and update content |
| 1491 | - self::moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId, $personId); | |
| 1446 | + self::moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId); | |
| 1492 | 1447 | } |
| 1493 | 1448 | |
| 1494 | 1449 | /** |
| 1495 | 1450 | * Fetch content based on the content type. |
| @@ -1509,30 +1464,23 @@ | ||
| 1509 | 1464 | * Extract image URLs from the content. |
| 1510 | 1465 | */ |
| 1511 | 1466 | private static function extractImageUrls($content) |
| 1512 | 1467 | { |
| 1513 | - preg_match_all('/<img[^>]+src=(["\'])(.*?)\1/i', (string) $content, $matches); | |
| 1514 | - return $matches[2] ?? []; | |
| 1468 | + preg_match_all('/<img[^>]+src="([^">]+)"/', $content, $matches); | |
| 1469 | + return $matches[1] ?? []; | |
| 1515 | 1470 | } |
| 1516 | 1471 | |
| 1517 | 1472 | /** |
| 1518 | 1473 | * Move images to the upload directory and update content. |
| 1519 | 1474 | */ |
| 1520 | - private static function moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId, $personId) | |
| 1475 | + private static function moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId) | |
| 1521 | 1476 | { |
| 1522 | 1477 | // Get the current site's upload directory |
| 1523 | 1478 | $uploadDirInfo = wp_upload_dir(); |
| 1524 | 1479 | $uploadsDir = $uploadDirInfo['basedir']; |
| 1525 | 1480 | $tempDir = $uploadsDir . '/fluent-support/temp_files/'; |
| 1526 | - $signedAttachments = self::getSignedImageAttachments($imageUrls, $ticketId, $personId); | |
| 1527 | 1481 | |
| 1528 | 1482 | foreach ($imageUrls as $imageUrl) { |
| 1529 | - $fileHash = self::extractAttachmentHashFromUrl($imageUrl); | |
| 1530 | - if ($fileHash && isset($signedAttachments[$fileHash]) && ($referenceUrl = self::finalizeSignedTempImage($signedAttachments[$fileHash], $ticketId, $contentType, $replyId, $personId))) { | |
| 1531 | - $content = str_replace($imageUrl, $referenceUrl, $content); | |
| 1532 | - continue; | |
| 1533 | - } | |
| 1534 | - | |
| 1535 | 1483 | // Build the absolute path for the temporary file |
| 1536 | 1484 | $imageRelativePath = $tempDir . basename($imageUrl); |
| 1537 | 1485 | $absolutePath = $imageRelativePath; |
| 1538 | 1486 | |
| @@ -1552,166 +1500,8 @@ | ||
| 1552 | 1500 | } |
| 1553 | 1501 | |
| 1554 | 1502 | // Save the updated content |
| 1555 | 1503 | self::saveUpdatedContent($ticketId, $contentType, $content, $replyId); |
| 1556 | - } | |
| 1557 | - | |
| 1558 | - private static function getSignedImageAttachments($imageUrls, $ticketId, $personId) | |
| 1559 | - { | |
| 1560 | - $fileHashes = []; | |
| 1561 | - foreach ($imageUrls as $imageUrl) { | |
| 1562 | - $fileHash = self::extractAttachmentHashFromUrl($imageUrl); | |
| 1563 | - if ($fileHash) { | |
| 1564 | - $fileHashes[] = $fileHash; | |
| 1565 | - } | |
| 1566 | - } | |
| 1567 | - | |
| 1568 | - $fileHashes = array_values(array_unique($fileHashes)); | |
| 1569 | - if (!$fileHashes) { | |
| 1570 | - return []; | |
| 1571 | - } | |
| 1572 | - | |
| 1573 | - $attachments = Attachment::whereIn('file_hash', $fileHashes) | |
| 1574 | - ->where(function ($query) use ($ticketId, $personId) { | |
| 1575 | - $query->where('ticket_id', $ticketId); | |
| 1576 | - | |
| 1577 | - if ($personId) { | |
| 1578 | - $query->orWhere(function ($query) use ($personId) { | |
| 1579 | - $query->whereNull('ticket_id') | |
| 1580 | - ->where('person_id', $personId); | |
| 1581 | - }); | |
| 1582 | - } | |
| 1583 | - }) | |
| 1584 | - ->get(); | |
| 1585 | - $mappedAttachments = []; | |
| 1586 | - foreach ($attachments as $attachment) { | |
| 1587 | - $mappedAttachments[$attachment->file_hash] = $attachment; | |
| 1588 | - } | |
| 1589 | - | |
| 1590 | - return $mappedAttachments; | |
| 1591 | - } | |
| 1592 | - | |
| 1593 | - private static function finalizeSignedTempImage($attachment, $ticketId, $contentType, $replyId, $personId) | |
| 1594 | - { | |
| 1595 | - if (!$attachment || $attachment->driver !== 'local') { | |
| 1596 | - return false; | |
| 1597 | - } | |
| 1598 | - | |
| 1599 | - if ($attachment->ticket_id && intval($attachment->ticket_id) !== intval($ticketId)) { | |
| 1600 | - return false; | |
| 1601 | - } | |
| 1602 | - | |
| 1603 | - if (!$attachment->ticket_id && $personId && intval($attachment->person_id) !== intval($personId)) { | |
| 1604 | - return false; | |
| 1605 | - } | |
| 1606 | - | |
| 1607 | - if ($attachment->conversation_id && $replyId && intval($attachment->conversation_id) !== intval($replyId)) { | |
| 1608 | - return false; | |
| 1609 | - } | |
| 1610 | - | |
| 1611 | - if ($attachment->conversation_id && $contentType === 'ticket-create') { | |
| 1612 | - return false; | |
| 1613 | - } | |
| 1614 | - | |
| 1615 | - if ($attachment->status !== 'in-active') { | |
| 1616 | - return $attachment->ticket_id ? self::getAttachmentReferenceUrl($attachment) : false; | |
| 1617 | - } | |
| 1618 | - | |
| 1619 | - if (!$attachment->file_path || !file_exists($attachment->file_path)) { | |
| 1620 | - return false; | |
| 1621 | - } | |
| 1622 | - | |
| 1623 | - $newFileInfo = UploadService::copyFileTicketFolder($attachment->file_path, $ticketId); | |
| 1624 | - if (empty($newFileInfo['file_path'])) { | |
| 1625 | - return false; | |
| 1626 | - } | |
| 1627 | - | |
| 1628 | - $attachment->file_path = $newFileInfo['file_path']; | |
| 1629 | - $attachment->full_url = $newFileInfo['url']; | |
| 1630 | - $attachment->ticket_id = $attachment->ticket_id ?: $ticketId; | |
| 1631 | - $attachment->status = 'inline'; | |
| 1632 | - | |
| 1633 | - if ($contentType !== 'ticket-create' && $replyId) { | |
| 1634 | - $attachment->conversation_id = $replyId; | |
| 1635 | - } | |
| 1636 | - | |
| 1637 | - $attachment->save(); | |
| 1638 | - | |
| 1639 | - return self::getAttachmentReferenceUrl($attachment); | |
| 1640 | - } | |
| 1641 | - | |
| 1642 | - private static function extractAttachmentHashFromUrl($imageUrl) | |
| 1643 | - { | |
| 1644 | - $decodedUrl = html_entity_decode($imageUrl, ENT_QUOTES, 'UTF-8'); | |
| 1645 | - $query = wp_parse_url($decodedUrl, PHP_URL_QUERY); | |
| 1646 | - | |
| 1647 | - if (!$query) { | |
| 1648 | - return ''; | |
| 1649 | - } | |
| 1650 | - | |
| 1651 | - parse_str($query, $params); | |
| 1652 | - | |
| 1653 | - return !empty($params['fst_file']) ? sanitize_text_field($params['fst_file']) : ''; | |
| 1654 | - } | |
| 1655 | - | |
| 1656 | - private static function getAttachmentReferenceUrl($attachment) | |
| 1657 | - { | |
| 1658 | - return add_query_arg([ | |
| 1659 | - 'fst_file' => $attachment->file_hash | |
| 1660 | - ], site_url('/index.php')); | |
| 1661 | - } | |
| 1662 | - | |
| 1663 | - public static function refreshSignedAttachmentUrls($content, $ticketId = null) | |
| 1664 | - { | |
| 1665 | - $contents = self::refreshSignedAttachmentUrlsInContents([$content], $ticketId); | |
| 1666 | - | |
| 1667 | - return $contents[0]; | |
| 1668 | - } | |
| 1669 | - | |
| 1670 | - public static function refreshSignedAttachmentUrlsInContents($contents, $ticketId = null) | |
| 1671 | - { | |
| 1672 | - $fileHashes = []; | |
| 1673 | - | |
| 1674 | - foreach ($contents as $content) { | |
| 1675 | - foreach (self::extractImageUrls($content) as $imageUrl) { | |
| 1676 | - $fileHash = self::extractAttachmentHashFromUrl($imageUrl); | |
| 1677 | - if ($fileHash) { | |
| 1678 | - $fileHashes[] = $fileHash; | |
| 1679 | - } | |
| 1680 | - } | |
| 1681 | - } | |
| 1682 | - | |
| 1683 | - $fileHashes = array_values(array_unique($fileHashes)); | |
| 1684 | - if (!$fileHashes) { | |
| 1685 | - return $contents; | |
| 1686 | - } | |
| 1687 | - | |
| 1688 | - $attachmentsQuery = Attachment::whereIn('file_hash', $fileHashes); | |
| 1689 | - if ($ticketId) { | |
| 1690 | - $attachmentsQuery->where('ticket_id', $ticketId); | |
| 1691 | - } | |
| 1692 | - | |
| 1693 | - $attachments = $attachmentsQuery->get(); | |
| 1694 | - $attachmentsByHash = []; | |
| 1695 | - | |
| 1696 | - foreach ($attachments as $attachment) { | |
| 1697 | - $attachmentsByHash[$attachment->file_hash] = $attachment; | |
| 1698 | - } | |
| 1699 | - | |
| 1700 | - foreach ($contents as $key => $content) { | |
| 1701 | - foreach (self::extractImageUrls($content) as $imageUrl) { | |
| 1702 | - $fileHash = self::extractAttachmentHashFromUrl($imageUrl); | |
| 1703 | - if (!$fileHash || empty($attachmentsByHash[$fileHash])) { | |
| 1704 | - continue; | |
| 1705 | - } | |
| 1706 | - | |
| 1707 | - $content = str_replace($imageUrl, $attachmentsByHash[$fileHash]->secureUrl, $content); | |
| 1708 | - } | |
| 1709 | - | |
| 1710 | - $contents[$key] = $content; | |
| 1711 | - } | |
| 1712 | - | |
| 1713 | - return $contents; | |
| 1714 | 1504 | } |
| 1715 | 1505 | |
| 1716 | 1506 | /** |
| 1717 | 1507 | * Save the updated content based on the content type. |