CHAT_OUTPUT_BEGIN
COMMAND_ID=SUPPORT-260918-GRAM-SUPPORT-NOTIFY-CAPTCHA-SOURCE-READONLY-516R1R3
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=gram-support-notify-captcha-source-readonly
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
COMMAND_SHA256=0f7a476d59191ec993679cb3fe3a569eb7e5b72805ab8c6e63c390f6a14915fc
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGE_DECLARED=false
RESULT_CONTRACT_VALID=true
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
RESULT_CONTRACT_ERROR=NONE
COMMAND_RC=0
CHANGES_MADE=false
ROLLBACK_STARTED=false
ROLLBACK_RESTORED=null
MUTATION_OUTCOME=NO_MUTATION
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=6eb6b9ac7ab7b4c61eb06b40dd1f2390fcac654e496097728a954feacd2b3c9c
SANITIZED_OUTPUT_SHA256=6eb6b9ac7ab7b4c61eb06b40dd1f2390fcac654e496097728a954feacd2b3c9c
OUTPUT_BEGIN
GRAM516R1R3_SAFE_REPORT={"err_truncated":false,"guest_stderr_present":false,"inner_rc":0,"ok":true,"out_truncated":false,"source":{"notification_files":["Notification/AlertConsumer.php","Notification/DeliveryGate.php","Notification/Dispatcher.php","Notification/EmailConsumer.php","Notification/EntityDeliveryGate.php","Notification/EventDispatcher.php","Notification/Policy.php","Notification/RecipientKey.php"],"ok":true,"root_tree_files":["Attachment/TicketHandler.php","Entity/AttachmentMeta.php","Entity/Delivery.php","Entity/Message.php","Entity/RateLimit.php","Entity/SupportEvent.php","Entity/Ticket.php","Listener/SourcePageContext.php","Notification/AlertConsumer.php","Notification/DeliveryGate.php","Notification/Dispatcher.php","Notification/EmailConsumer.php","Notification/EntityDeliveryGate.php","Notification/EventDispatcher.php","Notification/Policy.php","Notification/RecipientKey.php","Pub/Controller/AccountSupport.php","Pub/Controller/Pwa.php","Pub/Controller/StaffSupport.php","Pub/Controller/Support.php","Pub/View/PwaServiceWorker.php","Repository/Message.php","Repository/Ticket.php","Security/GuestTicketSession.php","Security/TicketAccess.php","Service/GuestScreenshotService.php","Service/GuestTicketCreateAttachmentFlow.php","Service/RateLimiter.php","Service/SourceContextResolver.php","Service/Ticket/Creator.php","Service/Ticket/Reply.php","Service/Ticket/StaffUpdate.php","Setup.php","Util/GuestToken.php","Util/PublicRef.php","Util/RateKey.php","Util/SafeContext.php","Util/SiteConfig.php","Util/StatusPolicy.php","_data/code_event_listeners.xml","_data/content_type_fields.xml","_data/option_groups.xml","_data/options.xml","_data/permission_interface_groups.xml","_data/permissions.xml","_data/phrases.xml","_data/routes.xml","_data/template_modifications.xml","_data/templates.xml","addon.json","hashes.json"],"targets":{"Notification/AlertConsumer.php":{"line_count":74,"matched_context":[{"sep":true},{"line":1,"text":"<?php"},{"line":2,"text":""},{"line":3,"text":"namespace Gram\\Support\\Notification;"},{"line":4,"text":""},{"line":5,"text":"use Gram\\Support\\Entity\\SupportEvent;"},{"line":6,"text":"use Gram\\Support\\Entity\\Ticket;"},{"line":7,"text":"use XF\\Entity\\User;"},{"line":8,"text":""},{"line":9,"text":"final class AlertConsumer"},{"line":10,"text":"{"},{"line":11,"text":"    private DeliveryGate $gate;"},{"line":12,"text":""},{"line":13,"text":"    public function __construct(DeliveryGate $gate)"},{"line":14,"text":"    {"},{"line":15,"text":"        $this->gate = $gate;"},{"line":16,"text":"    }"},{"line":17,"text":""},{"line":18,"text":"    public function consumeForUser(SupportEvent $event, Ticket $ticket, User $recipient): bool"},{"line":19,"text":"    {"},{"line":20,"text":"        if (!Policy::mayEmitExternal($event) || !$recipient->user_id)"},{"line":21,"text":"        {"},{"line":22,"text":"            return false;"},{"line":23,"text":"        }"},{"line":24,"text":""},{"line":25,"text":"        // Never alert a user about a ticket they cannot access."},{"line":26,"text":"        $isOwner = $ticket->user_id && (int)$ticket->user_id === (int)$recipient->user_id;"},{"line":27,"text":"        $isStaff = $recipient->hasPermission('gramSupport', 'viewAll')"},{"line":28,"text":"            || $recipient->hasPermission('gramSupport', 'manage');"},{"line":29,"text":""},{"line":30,"text":"        if (!$isOwner && !$isStaff)"},{"line":31,"text":"        {"},{"line":32,"text":"            return false;"},{"line":33,"text":"        }"},{"line":34,"text":""},{"line":35,"text":"        $recipientKey = RecipientKey::user((int)$recipient->user_id);"},{"line":36,"text":"        if (!$this->gate->acquire((int)$event->event_id, 'xf_alert', $recipientKey))"},{"line":37,"text":"        {"},{"line":38,"text":"            return false;"},{"line":39,"text":"        }"},{"line":40,"text":""},{"line":41,"text":"        try"},{"line":42,"text":"        {"},{"line":43,"text":"            /** @var \\XF\\Repository\\UserAlertRepository $alerts */"},{"line":44,"text":"            $alerts = \\XF::repository('XF:UserAlert');"},{"line":45,"text":""},{"line":46,"text":"            // Back the alert with the recipient's own User content so the"},{"line":47,"text":"            // core alert handler remains valid/viewable. Ticket access is"},{"line":48,"text":"            // separately enforced before emission and again on ticket route."},{"line":49,"text":"            $alerts->alert("},{"line":50,"text":"                $recipient,"},{"line":51,"text":"                0,"},{"line":52,"text":"                'Support',"},{"line":53,"text":"                'user',"},{"line":54,"text":"                (int)$recipient->user_id,"},{"line":55,"text":"                'gram_support_evt',"},{"line":56,"text":"                Policy::alertExtraData($event),"},{"line":57,"text":"                ['usePush' => false]"},{"line":58,"text":"            );"},{"line":59,"text":""},{"line":60,"text":"            $this->gate->markSent((int)$event->event_id, 'xf_alert', $recipientKey);"},{"line":61,"text":"            return true;"},{"line":62,"text":"        }"},{"line":63,"text":"        catch (\\Throwable $e)"},{"line":64,"text":"        {"},{"line":65,"text":"            $this->gate->markFailed("},{"line":66,"text":"                (int)$event->event_id,"},{"line":67,"text":"                'xf_alert',"},{"line":68,"text":"                $recipientKey,"},{"line":69,"text":"                get_class($e)"},{"line":70,"text":"            );"},{"line":71,"text":"            throw $e;"},{"line":72,"text":"        }"}],"present":true,"sha256":"e22d0cdb0a0806c6a231504409bd17cd781caab0f28181aff7246b0295d8cd98"},"Notification/Dispatcher.php":{"line_count":67,"matched_context":[{"sep":true},{"line":1,"text":"<?php"},{"line":2,"text":""},{"line":3,"text":"namespace Gram\\Support\\Notification;"},{"line":4,"text":""},{"line":5,"text":"use Gram\\Support\\Entity\\SupportEvent;"},{"line":6,"text":"use Gram\\Support\\Entity\\Ticket;"},{"line":7,"text":"use XF\\Entity\\User;"},{"line":8,"text":""},{"line":9,"text":"final class Dispatcher"},{"line":10,"text":"{"},{"line":11,"text":"    private AlertConsumer $alerts;"},{"line":12,"text":"    private EmailConsumer $email;"},{"line":13,"text":""},{"line":14,"text":"    public function __construct(AlertConsumer $alerts, EmailConsumer $email)"},{"line":15,"text":"    {"},{"line":16,"text":"        $this->alerts = $alerts;"},{"line":17,"text":"        $this->email = $email;"},{"line":18,"text":"    }"},{"line":19,"text":""},{"line":20,"text":"    /**"},{"line":21,"text":"     * Owner-facing delivery. Staff alert fan-out is intentionally provided by"},{"line":22,"text":"     * dispatchStaff() so staff recipients must still pass Gram Support ACL."},{"line":23,"text":"     */"},{"line":24,"text":"    public function dispatchOwner(SupportEvent $event, Ticket $ticket, ?User $owner): void"},{"line":25,"text":"    {"},{"line":26,"text":"        if (!Policy::mayEmitExternal($event))"},{"line":27,"text":"        {"},{"line":28,"text":"            return;"},{"line":29,"text":"        }"},{"line":30,"text":""},{"line":31,"text":"        if ($owner && $owner->user_id)"},{"line":32,"text":"        {"},{"line":33,"text":"            $this->alerts->consumeForUser($event, $ticket, $owner);"},{"line":34,"text":"            $this->email->consumeForRegistered($event, $ticket, $owner);"},{"line":35,"text":"            return;"},{"line":36,"text":"        }"},{"line":37,"text":""},{"line":38,"text":"        $this->email->consumeForGuest($event, $ticket);"},{"line":39,"text":"    }"},{"line":40,"text":""},{"line":41,"text":"    /**"},{"line":42,"text":"     * @param User[] $staffUsers"},{"line":43,"text":"     */"},{"line":44,"text":"    public function dispatchStaff(SupportEvent $event, Ticket $ticket, array $staffUsers): void"},{"line":45,"text":"    {"},{"line":46,"text":"        if (!Policy::mayEmitExternal($event))"},{"line":47,"text":"        {"},{"line":48,"text":"            return;"},{"line":49,"text":"        }"},{"line":50,"text":""},{"line":51,"text":"        foreach ($staffUsers as $staff)"},{"line":52,"text":"        {"},{"line":53,"text":"            if (!$staff instanceof User || !$staff->user_id)"},{"line":54,"text":"            {"},{"line":55,"text":"                continue;"},{"line":56,"text":"            }"},{"line":57,"text":""},{"line":58,"text":"            if (!$staff->hasPermission('gramSupport', 'viewAll')"},{"line":59,"text":"                && !$staff->hasPermission('gramSupport', 'manage'))"},{"line":60,"text":"            {"},{"line":61,"text":"                continue;"},{"line":62,"text":"            }"},{"line":63,"text":""},{"line":64,"text":"            $this->alerts->consumeForUser($event, $ticket, $staff);"},{"line":65,"text":"        }"},{"line":66,"text":"    }"},{"line":67,"text":"}"}],"present":true,"sha256":"a043d94bc91feeed0969ad218566cebac76b2d804e7c69cc2b87b94d42f5bb85"},"Pub/Controller/Support.php":{"line_count":236,"matched_context":[{"sep":true},{"line":1,"text":"<?php"},{"line":2,"text":""},{"line":3,"text":"namespace Gram\\Support\\Pub\\Controller;"},{"line":4,"text":""},{"line":5,"text":"use Gram\\Support\\Notification\\EventDispatcher;"},{"line":6,"text":"use Gram\\Support\\Security\\GuestTicketSession;"},{"line":7,"text":"use Gram\\Support\\Security\\TicketAccess;"},{"line":8,"text":"use Gram\\Support\\Service\\GuestTicketCreateAttachmentFlow;"},{"line":9,"text":"use Gram\\Support\\Service\\RateLimiter;"},{"line":10,"text":"use Gram\\Support\\Service\\Ticket\\Creator;"},{"line":11,"text":"use Gram\\Support\\Service\\Ticket\\Reply;"},{"line":12,"text":"use Gram\\Support\\Util\\SiteConfig;"},{"line":13,"text":"use XF\\Mvc\\ParameterBag;"},{"line":14,"text":""},{"line":15,"text":"final class Support extends \\XF\\Pub\\Controller\\AbstractController"},{"line":16,"text":"{"},{"sep":true},{"line":35,"text":""},{"line":36,"text":"        if ($this->isPost())"},{"line":37,"text":"        {"},{"line":38,"text":"            $this->assertPostOnly();"},{"line":39,"text":""},{"line":40,"text":"            $email = $this->filter('email', 'str');"},{"line":41,"text":"            $category = $this->filter('category', 'str');"},{"line":42,"text":"            $subject = $this->filter('subject', 'str');"},{"line":43,"text":"            $message = $this->filter('message', 'str');"},{"line":44,"text":""},{"line":45,"text":"            if (!$visitor->user_id)"},{"line":46,"text":"            {"},{"line":47,"text":"                if (!$this->captchaIsValid(true))"},{"line":48,"text":"                {"},{"line":49,"text":"                    return $this->error("},{"line":50,"text":"                        \\XF::phrase('did_not_complete_the_captcha_verification_properly')"},{"line":51,"text":"                    );"},{"line":52,"text":"                }"},{"line":53,"text":""},{"line":54,"text":"                /** @var RateLimiter $rateLimiter */"},{"line":55,"text":"                $rateLimiter = $this->service(RateLimiter::class);"},{"line":56,"text":"                $allowed = $rateLimiter->consumeGuest("},{"line":57,"text":"                    SiteConfig::siteId(),"},{"line":58,"text":"                    $email,"},{"line":59,"text":"                    SiteConfig::guestRateLimitCount(),"},{"line":60,"text":"                    SiteConfig::guestRateLimitWindowSeconds()"},{"line":61,"text":"                );"},{"line":62,"text":""},{"line":63,"text":"                if (!$allowed)"},{"sep":true},{"line":74,"text":"            if ($upload)"},{"line":75,"text":"            {"},{"line":76,"text":"                $attachmentId = $attachmentFlow->prepare($upload, $tempHash);"},{"line":77,"text":"            }"},{"line":78,"text":""},{"line":79,"text":"            /** @var Creator $creator */"},{"line":80,"text":"            $creator = $this->service(Creator::class);"},{"line":81,"text":"            $created = $creator->create(["},{"line":82,"text":"                'email' => $email,"},{"line":83,"text":"                'category' => $category,"},{"line":84,"text":"                'subject' => $subject,"},{"line":85,"text":"                'message' => $message"},{"line":86,"text":"            ]);"},{"line":87,"text":""},{"sep":true},{"line":127,"text":"        }"},{"line":128,"text":""},{"line":129,"text":"        return $this->view("},{"line":130,"text":"            'Gram\\Support:Create',"},{"line":131,"text":"            'gram_support_create',"},{"line":132,"text":"            ['forceCaptcha' => !$visitor->user_id]"},{"line":133,"text":"        );"},{"line":134,"text":"    }"},{"line":135,"text":""},{"line":136,"text":"    public function actionAccess()"},{"line":137,"text":"    {"},{"sep":true},{"line":183,"text":"                'messages' => $messages"},{"line":184,"text":"            ]"},{"line":185,"text":"        );"},{"line":186,"text":"    }"},{"line":187,"text":""},{"line":188,"text":"    public function actionReply()"},{"line":189,"text":"    {"},{"line":190,"text":"        $this->assertPostOnly();"},{"line":191,"text":""},{"line":192,"text":"        $ticket = $this->assertViewableTicketByRef($this->filter('ref', 'str'));"},{"line":193,"text":"        $visitor = \\XF::visitor();"},{"line":194,"text":""},{"line":195,"text":"        if ($visitor->user_id)"},{"line":196,"text":"        {"},{"line":197,"text":"            if ((int)$ticket->user_id !== (int)$visitor->user_id"},{"line":198,"text":"                || !$visitor->hasPermission('gramSupport', 'replyOwn'))"},{"line":199,"text":"            {"},{"line":200,"text":"                return $this->noPermission();"},{"line":201,"text":"            }"},{"line":202,"text":"        }"},{"line":203,"text":"        elseif (!GuestTicketSession::hasGrant($ticket))"},{"sep":true},{"line":205,"text":"            return $this->noPermission();"},{"line":206,"text":"        }"},{"line":207,"text":""},{"line":208,"text":"        $message = $this->filter('message', 'str');"},{"line":209,"text":""},{"line":210,"text":"        /** @var Reply $reply */"},{"line":211,"text":"        $reply = $this->service(Reply::class);"},{"line":212,"text":"        $reply->add($ticket, $message, false, false);"},{"line":213,"text":""},{"line":214,"text":"        return $this->redirect("},{"line":215,"text":"            $this->buildLink('support/ticket', null, ['ref' => $ticket->public_ref])"},{"line":216,"text":"        );"},{"line":217,"text":"    }"}],"present":true,"sha256":"6cb34b1d27e9f0132263637cefa62fe0e998a9a86620b7ac33199f41ee86f9d4"},"Service/Ticket/Creator.php":{"line_count":139,"matched_context":[{"sep":true},{"line":8,"text":"use Gram\\Support\\Util\\PublicRef;"},{"line":9,"text":"use Gram\\Support\\Util\\SafeContext;"},{"line":10,"text":"use Gram\\Support\\Util\\SiteConfig;"},{"line":11,"text":"use Gram\\Support\\Util\\StatusPolicy;"},{"line":12,"text":"use XF\\Service\\AbstractService;"},{"line":13,"text":"use Gram\\Support\\Notification\\EventDispatcher;"},{"line":14,"text":""},{"line":15,"text":"class Creator extends AbstractService"},{"line":16,"text":"{"},{"line":17,"text":"    public function create(array $input, ?int $now = null): array"},{"line":18,"text":"    {"},{"line":19,"text":"        $now = $now ?? \\XF::$time;"},{"line":20,"text":"        $visitor = \\XF::visitor();"},{"line":21,"text":"        $siteId = SiteConfig::siteId();"},{"line":22,"text":""},{"line":23,"text":"        $email = strtolower(trim((string)($input['email'] ?? '')));"},{"line":24,"text":"        $category = trim((string)($input['category'] ?? ''));"},{"line":25,"text":"        $subject = trim((string)($input['subject'] ?? ''));"},{"line":26,"text":"        $messageText = trim((string)($input['message'] ?? ''));"},{"line":27,"text":""},{"line":28,"text":"        if ($category === '' || $messageText === '')"},{"line":29,"text":"        {"},{"line":30,"text":"            throw new \\InvalidArgumentException('GRAM_SUPPORT_REQUIRED_FIELDS');"},{"line":31,"text":"        }"},{"line":32,"text":""},{"line":33,"text":"        if (!$visitor->user_id && !filter_var($email, FILTER_VALIDATE_EMAIL))"},{"line":34,"text":"        {"},{"line":35,"text":"            throw new \\InvalidArgumentException('GRAM_SUPPORT_VALID_EMAIL_REQUIRED');"},{"line":36,"text":"        }"},{"line":37,"text":""},{"line":38,"text":"        if ($subject === '')"},{"line":39,"text":"        {"},{"line":40,"text":"            $subject = function_exists('mb_substr')"},{"sep":true},{"line":68,"text":"            /** @var Ticket $ticket */"},{"line":69,"text":"            $ticket = $this->em()->create('Gram\\Support:Ticket');"},{"line":70,"text":"            $ticket->public_ref = 'TMP-' . bin2hex(random_bytes(12));"},{"line":71,"text":"            $ticket->site_id = $siteId;"},{"line":72,"text":"            $ticket->user_id = (int)$visitor->user_id;"},{"line":73,"text":"            $ticket->guest_email = $visitor->user_id ? '' : $email;"},{"line":74,"text":"            $ticket->guest_access_token_hash = $guest ? $guest['hash'] : '';"},{"line":75,"text":"            $ticket->guest_access_expires = $guest"},{"line":76,"text":"                ? $now + SiteConfig::guestTokenTtlSeconds()"},{"line":77,"text":"                : 0;"},{"line":78,"text":"            $ticket->node_id = (int)($context['node_id'] ?? 0);"},{"sep":true},{"line":96,"text":"            $ticket->save();"},{"line":97,"text":""},{"line":98,"text":"            $message = $this->em()->create('Gram\\Support:Message');"},{"line":99,"text":"            $message->ticket_id = (int)$ticket->ticket_id;"},{"line":100,"text":"            $message->user_id = (int)$visitor->user_id;"},{"line":101,"text":"            $message->is_staff = 0;"},{"line":102,"text":"            $message->is_internal = 0;"},{"line":103,"text":"            $message->message = $messageText;"},{"line":104,"text":"            $message->message_date = $now;"},{"line":105,"text":"            $message->save();"},{"line":106,"text":""},{"line":107,"text":"            $ticket->last_message_id = (int)$message->message_id;"},{"line":108,"text":"            $ticket->save();"},{"line":109,"text":""},{"line":110,"text":"            $event = $this->em()->create('Gram\\Support:SupportEvent');"},{"line":111,"text":"            $event->ticket_id = (int)$ticket->ticket_id;"},{"line":112,"text":"            $event->site_id = $siteId;"},{"line":113,"text":"            $event->actor_user_id = (int)$visitor->user_id;"},{"line":114,"text":"            $event->event_type = 'ticket_created';"},{"line":115,"text":"            $event->event_date = $now;"},{"line":116,"text":"            $event->safe_payload_json = json_encode(["},{"line":117,"text":"                'public_ref' => (string)$ticket->public_ref,"},{"line":118,"text":"                'category' => (string)$ticket->category,"},{"line":119,"text":"                'is_internal' => false"},{"sep":true},{"line":126,"text":"        {"},{"line":127,"text":"            $db->rollback();"},{"line":128,"text":"            throw $e;"},{"line":129,"text":"        }"},{"line":130,"text":""},{"line":131,"text":"        (new EventDispatcher())->dispatchSafely($event, $ticket);"},{"line":132,"text":""},{"line":133,"text":"        return ["},{"line":134,"text":"            'ticket' => $ticket,"},{"line":135,"text":"            'event' => $event,"},{"line":136,"text":"            'guest_secret' => $guest ? $guest['secret'] : null"}],"present":true,"sha256":"7a84cd77761a7943495f9d3ef11269987f85265170ad6cf927a1661b8bf0a99a"},"Service/Ticket/Reply.php":{"line_count":68,"matched_context":[{"sep":true},{"line":3,"text":"namespace Gram\\Support\\Service\\Ticket;"},{"line":4,"text":""},{"line":5,"text":"use Gram\\Support\\Entity\\Ticket;"},{"line":6,"text":"use Gram\\Support\\Util\\SiteConfig;"},{"line":7,"text":"use XF\\Service\\AbstractService;"},{"line":8,"text":"use Gram\\Support\\Notification\\EventDispatcher;"},{"line":9,"text":""},{"line":10,"text":"class Reply extends AbstractService"},{"line":11,"text":"{"},{"line":12,"text":"    public function add(Ticket $ticket, string $messageText, bool $isStaff, bool $isInternal = false): array"},{"line":13,"text":"    {"},{"line":14,"text":"        $messageText = trim($messageText);"},{"line":15,"text":"        if ($messageText === '')"},{"line":16,"text":"        {"},{"line":17,"text":"            throw new \\InvalidArgumentException('GRAM_SUPPORT_EMPTY_REPLY');"},{"line":18,"text":"        }"},{"line":19,"text":"        if ($isInternal && !$isStaff)"},{"line":20,"text":"        {"},{"line":21,"text":"            throw new \\LogicException('INTERNAL_REQUIRES_STAFF');"},{"line":22,"text":"        }"},{"line":23,"text":""},{"line":24,"text":"        $visitor = \\XF::visitor();"},{"line":25,"text":"        $now = \\XF::$time;"},{"line":26,"text":"        $db = \\XF::db();"},{"sep":true},{"line":29,"text":"        try"},{"line":30,"text":"        {"},{"line":31,"text":"            $message = $this->em()->create('Gram\\Support:Message');"},{"line":32,"text":"            $message->ticket_id = (int)$ticket->ticket_id;"},{"line":33,"text":"            $message->user_id = (int)$visitor->user_id;"},{"line":34,"text":"            $message->is_staff = $isStaff ? 1 : 0;"},{"line":35,"text":"            $message->is_internal = $isInternal ? 1 : 0;"},{"line":36,"text":"            $message->message = $messageText;"},{"line":37,"text":"            $message->message_date = $now;"},{"line":38,"text":"            $message->save();"},{"line":39,"text":""},{"line":40,"text":"            $ticket->last_message_id = (int)$message->message_id;"},{"line":41,"text":"            $ticket->updated_date = $now;"},{"line":42,"text":"            $ticket->save();"},{"line":43,"text":""},{"line":44,"text":"            $event = $this->em()->create('Gram\\Support:SupportEvent');"},{"line":45,"text":"            $event->ticket_id = (int)$ticket->ticket_id;"},{"line":46,"text":"            $event->site_id = (string)$ticket->site_id;"},{"line":47,"text":"            $event->actor_user_id = (int)$visitor->user_id;"},{"line":48,"text":"            $event->event_type = $isInternal ? 'internal_note_created' : 'ticket_reply_created';"},{"line":49,"text":"            $event->event_date = $now;"},{"line":50,"text":"            $event->safe_payload_json = json_encode(["},{"line":51,"text":"                'public_ref' => (string)$ticket->public_ref,"},{"line":52,"text":"                'is_internal' => $isInternal"},{"line":53,"text":"            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);"},{"sep":true},{"line":59,"text":"        {"},{"line":60,"text":"            $db->rollback();"},{"line":61,"text":"            throw $e;"},{"line":62,"text":"        }"},{"line":63,"text":""},{"line":64,"text":"        (new EventDispatcher())->dispatchSafely($event, $ticket);"},{"line":65,"text":""},{"line":66,"text":"        return ['message' => $message, 'event' => $event];"},{"line":67,"text":"    }"},{"line":68,"text":"}"}],"present":true,"sha256":"482571a2ccf07a3c8672f34940243264a8271ef8c3cbc4fadbb46112afd36830"},"_data/templates.xml":{"line_count":781,"matched_context":[{"sep":true},{"line":1,"text":"<?xml version='1.0' encoding='utf-8'?>"},{"line":2,"text":"<templates>"},{"line":3,"text":"  <template type=\"public\" title=\"alert_user_gram_support_evt\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">&lt;xf:macro id=\"content\"&gt;"},{"line":4,"text":"\t{{ phrase('gram_support_alert_ticket_event', {"},{"line":5,"text":"\t\t'public_ref': $alert.extra_data.public_ref"},{"line":6,"text":"\t}) }}"},{"line":7,"text":"&lt;/xf:macro&gt;"},{"line":8,"text":"</template>"},{"line":9,"text":"  <template type=\"public\" title=\"gram_support.less\" version_id=\"1000100\" version_string=\"1.0.0 Alpha 3 Unified Fix 8\">.gramSupport-launcher"},{"line":10,"text":"{"},{"sep":true},{"line":490,"text":""},{"line":491,"text":"&lt;xf:form action=\"{{ link('support/create') }}\" method=\"post\" upload=\"true\" class=\"block\"&gt;"},{"line":492,"text":"\t&lt;div class=\"block-container\"&gt;"},{"line":493,"text":"\t\t&lt;div class=\"block-body\"&gt;"},{"line":494,"text":"\t\t\t&lt;xf:if is=\"!$xf.visitor.user_id\"&gt;"},{"line":495,"text":"\t\t\t\t&lt;xf:textboxrow name=\"email\" type=\"email\" required=\"required\""},{"line":496,"text":"\t\t\t\t\tlabel=\"{{ phrase('email') }}\" /&gt;"},{"line":497,"text":"\t\t\t&lt;/xf:if&gt;"},{"line":498,"text":"\t\t\t&lt;xf:selectrow name=\"category\" required=\"required\""},{"line":499,"text":"\t\t\t\tlabel=\"{{ phrase('gram_support_category') }}\"&gt;"},{"line":500,"text":"\t\t\t\t&lt;xf:option value=\"technical\"&gt;{{ phrase('gram_support_category_technical') }}&lt;/xf:option&gt;"},{"line":501,"text":"\t\t\t\t&lt;xf:option value=\"account\"&gt;{{ phrase('gram_support_category_account') }}&lt;/xf:option&gt;"},{"sep":true},{"line":507,"text":"\t\t\t&lt;xf:textarearow name=\"message\" required=\"required\" rows=\"8\""},{"line":508,"text":"\t\t\t\tlabel=\"{{ phrase('gram_support_description') }}\" /&gt;"},{"line":509,"text":"\t\t\t&lt;xf:uploadrow name=\"screenshot\" accept=\".gif,.jpeg,.jpg,.jpe,.png,.webp\""},{"line":510,"text":"\t\t\t\tlabel=\"{{ phrase('gram_support_screenshot_optional') }}\" /&gt;"},{"line":511,"text":"\t\t\t&lt;xf:if is=\"!$xf.visitor.user_id\"&gt;"},{"line":512,"text":"\t\t\t\t&lt;xf:captcharow label=\"{{ phrase('verification') }}\""},{"line":513,"text":"\t\t\t\t\thint=\"{{ phrase('required') }}\" force=\"true\" /&gt;"},{"line":514,"text":"\t\t\t&lt;/xf:if&gt;"},{"line":515,"text":"\t\t&lt;/div&gt;"},{"line":516,"text":"\t\t&lt;xf:submitrow submit=\"{{ phrase('gram_support_submit') }}\" /&gt;"},{"line":517,"text":"\t&lt;/div&gt;"},{"sep":true},{"line":548,"text":"\t\taria-label=\"{{ phrase('gram_support_open_support') }}\"&gt;"},{"line":549,"text":"\t\t{{ phrase('gram_support_open_support') }}"},{"line":550,"text":"\t&lt;/a&gt;"},{"line":551,"text":"&lt;/xf:if&gt;"},{"line":552,"text":"</template>"},{"line":553,"text":"  <template type=\"public\" title=\"gram_support_staff_queue\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">&lt;xf:title&gt;{{ phrase('gram_support_staff_queue') }}&lt;/xf:title&gt;"},{"line":554,"text":"&lt;xf:css src=\"gram_support.less\" /&gt;"},{"line":555,"text":"&lt;span class=\"js-gramSupportPwaConfig\" hidden"},{"line":556,"text":"\tdata-manifest-url=\"{{ base_url('js/gram/support/pwa/manifest.webmanifest') }}\""},{"line":557,"text":"\tdata-sw-url=\"{{ link('support/service-worker') }}\"&gt;&lt;/span&gt;"},{"line":558,"text":"&lt;xf:js addon=\"Gram/Support\" src=\"gram/support/staff-pwa.js\" min=\"1\" /&gt;"},{"line":559,"text":""},{"line":560,"text":"&lt;div class=\"block\"&gt;"},{"line":561,"text":"\t&lt;div class=\"block-container\"&gt;"},{"line":562,"text":"\t\t&lt;div class=\"block-body\"&gt;"},{"line":563,"text":"\t\t\t&lt;xf:foreach loop=\"$tickets\" value=\"$ticket\"&gt;"},{"line":564,"text":"\t\t\t\t&lt;div class=\"block-row\"&gt;"},{"line":565,"text":"\t\t\t\t\t&lt;a href=\"{{ link('support/staff/ticket', null, {'ref': $ticket.public_ref}) }}\"&gt;"},{"line":566,"text":"\t\t\t\t\t\t{{ $ticket.public_ref }} \u2014 {{ $ticket.subject }}"},{"line":567,"text":"\t\t\t\t\t&lt;/a&gt;"},{"line":568,"text":"\t\t\t\t\t&lt;span class=\"u-muted\"&gt;{{ $ticket.status }}&lt;/span&gt;"},{"line":569,"text":"\t\t\t\t&lt;/div&gt;"},{"line":570,"text":"\t\t\t&lt;/xf:foreach&gt;"},{"line":571,"text":"\t\t&lt;/div&gt;"},{"line":572,"text":"\t&lt;/div&gt;"},{"line":573,"text":"&lt;/div&gt;"},{"line":574,"text":"</template>"},{"line":575,"text":"  <template type=\"public\" title=\"gram_support_staff_service_worker\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">'use strict';"},{"line":576,"text":""},{"line":577,"text":"const OFFLINE_URL = '{{ base_url('js/gram/support/pwa/offline.html')|escape('js') }}';"},{"line":578,"text":"const STATIC_CACHE = 'gram-support-staff-static-v1';"},{"line":579,"text":""},{"line":580,"text":"self.addEventListener('install', event =&gt; {"},{"line":581,"text":"\tevent.waitUntil("},{"line":582,"text":"\t\tcaches.open(STATIC_CACHE)"},{"line":583,"text":"\t\t\t.then(cache =&gt; cache.addAll([OFFLINE_URL]))"},{"sep":true},{"line":587,"text":""},{"line":588,"text":"self.addEventListener('activate', event =&gt; {"},{"line":589,"text":"\tevent.waitUntil("},{"line":590,"text":"\t\tcaches.keys().then(keys =&gt;"},{"line":591,"text":"\t\t\tPromise.all(keys"},{"line":592,"text":"\t\t\t\t.filter(key =&gt; key.startsWith('gram-support-staff-static-') &amp;&amp; key !== STATIC_CACHE)"},{"line":593,"text":"\t\t\t\t.map(key =&gt; caches.delete(key))"},{"line":594,"text":"\t\t\t)"},{"line":595,"text":"\t\t).then(() =&gt; self.clients.claim())"},{"line":596,"text":"\t);"},{"line":597,"text":"});"},{"sep":true},{"line":607,"text":"\t\treturn;"},{"line":608,"text":"\t}"},{"line":609,"text":""},{"line":610,"text":"\t// SECURITY: all support/ticket/account/admin-like content is network-only."},{"line":611,"text":"\t// Never cache HTML/API responses that can contain ticket data, identities,"},{"line":612,"text":"\t// access state, internal notes, attachments or staff-only information."},{"line":613,"text":"\tconst sensitive ="},{"line":614,"text":"\t\turl.pathname.startsWith('/support/') ||"},{"line":615,"text":"\t\turl.pathname.startsWith('/account/') ||"},{"line":616,"text":"\t\turl.pathname.startsWith('/admin.php') ||"},{"line":617,"text":"\t\trequest.headers.get('accept')?.includes('text/html');"},{"sep":true},{"line":642,"text":"\t\t\t})"},{"line":643,"text":"\t\t)"},{"line":644,"text":"\t);"},{"line":645,"text":"});"},{"line":646,"text":"</template>"},{"line":647,"text":"  <template type=\"public\" title=\"gram_support_staff_ticket\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">&lt;xf:title&gt;{{ phrase('gram_support_staff_ticket') }} {{ $ticket.public_ref }}&lt;/xf:title&gt;"},{"line":648,"text":"&lt;xf:css src=\"gram_support.less\" /&gt;"},{"line":649,"text":"&lt;span class=\"js-gramSupportPwaConfig\" hidden"},{"line":650,"text":"\tdata-manifest-url=\"{{ base_url('js/gram/support/pwa/manifest.webmanifest') }}\""},{"line":651,"text":"\tdata-sw-url=\"{{ link('support/service-worker') }}\"&gt;&lt;/span&gt;"},{"line":652,"text":"&lt;xf:js addon=\"Gram/Support\" src=\"gram/support/staff-pwa.js\" min=\"1\" /&gt;"},{"line":653,"text":""},{"line":654,"text":"&lt;div class=\"block\"&gt;"},{"line":655,"text":"\t&lt;div class=\"block-container\"&gt;"},{"line":656,"text":"\t\t&lt;h2 class=\"block-header\"&gt;{{ $ticket.public_ref }} \u2014 {{ $ticket.subject }}&lt;/h2&gt;"},{"line":657,"text":"\t\t&lt;div class=\"block-body\"&gt;"},{"sep":true},{"line":682,"text":"\t\t\t&lt;/div&gt;"},{"line":683,"text":"\t\t&lt;/div&gt;"},{"line":684,"text":"\t&lt;/div&gt;"},{"line":685,"text":"&lt;/xf:if&gt;"},{"line":686,"text":""},{"line":687,"text":"&lt;xf:form action=\"{{ link('support/staff/reply', null, {'ref': $ticket.public_ref}) }}\" method=\"post\" class=\"block\"&gt;"},{"line":688,"text":"\t&lt;div class=\"block-container\"&gt;"},{"line":689,"text":"\t\t&lt;div class=\"block-body\"&gt;"},{"line":690,"text":"\t\t\t&lt;xf:textarearow name=\"message\" required=\"required\" rows=\"6\" label=\"{{ phrase('gram_support_reply') }}\" /&gt;"},{"line":691,"text":"\t\t\t&lt;xf:if is=\"$xf.visitor.hasPermission('gramSupport', 'internalNotes')\"&gt;"},{"line":692,"text":"\t\t\t\t&lt;xf:checkboxrow&gt;"},{"line":693,"text":"\t\t\t\t\t&lt;xf:option name=\"is_internal\" value=\"1\"&gt;{{ phrase('gram_support_internal_note') }}&lt;/xf:option&gt;"},{"line":694,"text":"\t\t\t\t&lt;/xf:checkboxrow&gt;"},{"line":695,"text":"\t\t\t&lt;/xf:if&gt;"},{"line":696,"text":"\t\t&lt;/div&gt;"},{"line":697,"text":"\t\t&lt;xf:submitrow submit=\"{{ phrase('gram_support_send') }}\" /&gt;"},{"line":698,"text":"\t&lt;/div&gt;"},{"line":699,"text":"&lt;/xf:form&gt;"},{"line":700,"text":""},{"line":701,"text":"&lt;xf:form action=\"{{ link('support/staff/update', null, {'ref': $ticket.public_ref}) }}\" method=\"post\" class=\"block\"&gt;"},{"line":702,"text":"\t&lt;div class=\"block-container\"&gt;"},{"line":703,"text":"\t\t&lt;div class=\"block-body\"&gt;"},{"line":704,"text":"\t\t\t&lt;xf:selectrow name=\"status\" value=\"{$ticket.status}\" label=\"{{ phrase('gram_support_status') }}\"&gt;"},{"line":705,"text":"\t\t\t\t&lt;xf:foreach loop=\"$statusOptions\" value=\"$status\"&gt;"},{"line":706,"text":"\t\t\t\t\t&lt;xf:option value=\"{$status}\"&gt;{$status}&lt;/xf:option&gt;"},{"sep":true},{"line":748,"text":"\t\t&lt;/div&gt;"},{"line":749,"text":"\t&lt;/div&gt;"},{"line":750,"text":"&lt;/xf:if&gt;"},{"line":751,"text":""},{"line":752,"text":"&lt;xf:if is=\"$ticket.status != 'CLOSED'\"&gt;"},{"line":753,"text":"\t&lt;xf:form action=\"{{ link('support/reply', null, {'ref': $ticket.public_ref}) }}\" method=\"post\" class=\"block\"&gt;"},{"line":754,"text":"\t\t&lt;div class=\"block-container\"&gt;"},{"line":755,"text":"\t\t\t&lt;div class=\"block-body\"&gt;"},{"line":756,"text":"\t\t\t\t&lt;xf:textarearow name=\"message\" required=\"required\" rows=\"6\""},{"line":757,"text":"\t\t\t\t\tlabel=\"{{ phrase('gram_support_reply') }}\" /&gt;"},{"line":758,"text":"\t\t\t&lt;/div&gt;"},{"line":759,"text":"\t\t\t&lt;xf:submitrow submit=\"{{ phrase('gram_support_send') }}\" /&gt;"},{"line":760,"text":"\t\t&lt;/div&gt;"},{"line":761,"text":"\t&lt;/xf:form&gt;"},{"line":762,"text":"&lt;/xf:if&gt;"},{"line":763,"text":"</template>"},{"line":764,"text":"  <template type=\"email\" title=\"gram_support_ticket_event\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">&lt;mail:subject&gt;"},{"line":765,"text":"\t{{ phrase('gram_support_email_ticket_event_subject', {'public_ref': $ticket.public_ref}) }}"},{"line":766,"text":"&lt;/mail:subject&gt;"},{"line":767,"text":""},{"line":768,"text":"{{ phrase('gram_support_email_ticket_event_body_registered', {"},{"line":769,"text":"\t'public_ref': $ticket.public_ref"},{"line":770,"text":"}) }}"},{"line":771,"text":"</template>"},{"line":772,"text":"  <template type=\"email\" title=\"gram_support_ticket_event_guest\" version_id=\"1000050\" version_string=\"1.0.0 Alpha 3 Unified Fix 3\">&lt;mail:subject&gt;"},{"line":773,"text":"\t{{ phrase('gram_support_email_ticket_event_subject', {'public_ref': $ticket.public_ref}) }}"},{"line":774,"text":"&lt;/mail:subject&gt;"},{"line":775,"text":""},{"line":776,"text":"{{ phrase('gram_support_email_ticket_event_body_guest', {"},{"line":777,"text":"\t'public_ref': $ticket.public_ref,"},{"line":778,"text":"\t'access_url': $accessUrl"},{"line":779,"text":"}) }}"},{"line":780,"text":"</template>"},{"line":781,"text":"</templates>"}],"present":true,"sha256":"1a344bd306135486d2019bf0a5cafff5eb472882f7b3433ef13af4556a4c791e"}}},"transport_rc":0,"transport_stderr_bytes":0,"transport_stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
GRAM516R1R3_NOTIFICATION_FILES=["Notification/AlertConsumer.php","Notification/DeliveryGate.php","Notification/Dispatcher.php","Notification/EmailConsumer.php","Notification/EntityDeliveryGate.php","Notification/EventDispatcher.php","Notification/Policy.php","Notification/RecipientKey.php"]
GRAM516R1R3_EMAIL_CONSUMER_FILE_PRESENT=true
GRAM516R1R3_DECISION=PASS_NOTIFY_CAPTCHA_SOURCE_EVIDENCE_CAPTURED
GRAM516R1R3_NEXT_GATE=BUILD_EXACT_TURNSTILE_AND_EMAIL_NOTIFY_CHANGE
HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SUPPORT-260918-GRAM-SUPPORT-NOTIFY-CAPTCHA-SOURCE-READONLY-516R1R3","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null}

OUTPUT_END
CHAT_OUTPUT_END
