PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.1.2
Fluent Support – Helpdesk & Customer Support Ticket System v2.1.2
2.4.5 2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 All 69 releases
← All changes | app/Services/Helper.php +15 -225 2.2.1 → 2.1.2 View file →
@@ -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.