Support

Akeeba Ticket System

#43239 Ticket status: replies from invited users (and the invite notice itself) set Pending instead of Open

Posted in ‘Akeeba Ticket System for Joomla!’
This is a public ticket

Everybody will be able to see its contents. Do not include usernames, passwords or any other sensitive information.

Environment Information

Joomla! version
5.4.7
PHP version
8.1.34
Akeeba Ticket System version
5.5.2

Latest post by nicholas on Wednesday, 05 August 2026 12:05 CDT

genr8r

Hi Nicholas,

We hit this in production on ATS 5.5.2 and have a one-line patch attached. It is a direct follow-on to the invited-users mail gateway fix you shipped in 5.5.2 from our earlier ticket #42924.

The issue

In PostTable::onAfterStore(), the new ticket status is decided by comparing the poster against the ticket creator:

// Set the ticket status depending on who made this post. User: Open. Manager: Pending.
'status' => ($this->created_by == $ticket->created_by) ? 'O' : 'P',

The comment says "Manager", but the else branch actually catches anyone who is not the original requester, with no privilege check. So a reply from a user who was invited to the ticket is treated as a staff reply and the ticket is set to Pending. Separately, the automatic "user has been invited to the conversation" post is written with created_by = -1 and goes through the same expression, so simply inviting someone sets the ticket to Pending before anyone has replied.

Real-world scenario

This is how it bit us. Our clients often have more than one person involved in a ticket, so we add them as subscribers. Those subscribers then reply by email with exactly the information we asked for, which is the behaviour your 5.5.2 fix enabled. Because the reply is filed as though it came from our own team, the ticket stays Pending rather than flipping back to Open, and Pending is excluded from the views our staff work from. The customer believes the ball is in our court and is waiting. Nobody on our side is notified, and the ticket is effectively invisible.

We only found it by auditing the database directly. On our install, 28 of 73 tickets sitting in Pending were parked wrongly, and the oldest had been invisible for 195 days. Several were customers waiting on answers they had already given us the information for.

Proposed fix

Two changes in the one expression. Use a real privilege check instead of the creator comparison, and leave the status alone for system generated posts. The helper is already imported in that file and already called a few lines below in the same method for the auto assign branch, so nothing new is introduced.

'status' => ($this->created_by <= 0)
    ? $ticket->status
    : (Permissions::isManager($ticket->catid, $this->created_by) ? 'P' : 'O'),

Behaviour after the change: a staff reply still sets Pending, the ticket creator still sets Open, an invited user now sets Open instead of Pending, and the invite notice no longer changes the status at all. We verified each case against the access rules on our own install before deploying it.

Unified diff attached as a .patch file for git apply.

Alternative shapes

If you would rather keep the creator comparison and only special case the system post, that alone fixes the invite half. You may also prefer Permissions::isInvited() for an explicit creator-or-invited test rather than inverting the manager check. We are happy either way, the important part is that a non staff reply should not park the ticket.

Thanks for ATS, and for the quick turnaround on #42924. Happy to test a build if that helps.

genr8r

Small correction: the attachment did not go through, the uploader rejects a .patch file. Here is the same unified diff inline instead, against administrator/components/com_ats/src/Table/PostTable.php from 5.5.2.

--- a/administrator/components/com_ats/src/Table/PostTable.php
+++ b/administrator/components/com_ats/src/Table/PostTable.php
@@ -284,7 +284,9 @@
 		{
 			// Set the ticket status depending on who made this post. User: Open. Manager: Pending.
 			$ticketChanges = [
-				'status'      => ($this->created_by == $ticket->created_by) ? 'O' : 'P',
+				'status'      => ($this->created_by <= 0)
+					? $ticket->status
+					: (Permissions::isManager($ticket->catid, $this->created_by) ? 'P' : 'O'),
 				'modified'    => (clone Factory::getDate())->toSql(),
 				'modified_by' => $this->created_by,
 			];

I sent as a zip too if you would rather apply it with git apply.

nicholas
Akeeba Staff
Manager

What you did covers your use case, but the real fix is more convoluted. Essentially, we need to do something like this:

if (!$isSystemPost)
{
$ticketChanges['status'] = ($isManager && !$isOwner) ? 'P' : 'O';
}

With your change a system message will always set the ticket to Pending, even if the system message was something along the lines "This ticket will be automatically closed". It can get confusing.

EmailCheck also needs a bit of work to avoid duplication. I am only mentioning it since you're receiving tickets by email and you might notice your fix "not working" because it's overridden in there.

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

genr8r

Thanks Nicholas, that is cleaner. Agreed on both points: gate on the system-post flag so automated messages (the invite notice, autoclose) never move the status, and use ($isManager && !$isOwner) instead of a bare identity check. I have aligned our local override to that shape.

One observation on the EmailCheck duplication, in case it helps the refactor: in 5.5.2 the mail gateway sets the ticket status in processEmail() before it stores the reply Post, and that Post save then fires PostTable::onAfterStore(), which recomputes and saves the status again. So on our install onAfterStore() looks like the final writer for email replies, and the gateway's own status block is effectively overwritten rather than the other way around. If that holds on your side too, the simplest consolidation may be to drop the gateway's status block entirely and let onAfterStore() own the decision, though you know the surrounding code better than I do. Happy to test a build against our email-ingested tickets if useful.

nicholas
Akeeba Staff
Manager

It's a bit more complicated than that. I have already written a workaround which should cover that too.

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

genr8r
Thanks Nicholas, I appreciate your attention on this.

nicholas
Akeeba Staff
Manager

You're welcome!

I will be on vacation starting later next week and until the last week of the month. Expect a release around August 30th to September 5th according to my current release schedule prediction.

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

Support Information

Working hours: We are open Monday to Friday, 9am to 7pm Cyprus timezone (EET / EEST). Support is provided by the same developers writing the software, all of which live in Europe. You can still file tickets outside of our working hours, but we cannot respond to them until we're back at the office.

Support policy: We would like to kindly inform you that when using our support you have already agreed to the Support Policy which is part of our Terms of Service. Thank you for your understanding and for helping us help you!