User Support
Ticket system for communicating with users
Ticket Component
The supportTicket component shows one of the signed-in user's tickets with its conversation, and lets the user reply to the ticket or close it.
| Property | Description |
|---|---|
| updatePage | The page with the Ticket Update component, used for the Edit links. |
| isPrimary | Use this page for the ticket links in mail notifications. Enable it on one page only. See Mail Links. |
Basic Usage
The page URL must include an :id parameter, which holds the ticket ID. The component only shows the tickets of the signed-in user, so restrict the page to signed-in users with the RainLab.User session component.
title = "Support Ticket"
url = "/support/ticket/:id"
layout = "default"
[session]
security = "user"
redirect = "account/login"
[supportTicket]
updatePage = "support/update"
isPrimary = 1
==
{% component "supportTicket" %}
The default markup shows the ticket details with a Close ticket button, the original message and the public replies with their attachments, and a reply form. On a closed ticket, it shows a notice in place of the reply form.
Page Variables
The ticket method of the component returns the ticket named by the :id URL parameter, or null when the signed-in user has no ticket with that ID.
{% set ticket = supportTicket.ticket %}
The ticket provides the following values.
| Variable | Description |
|---|---|
ticket.id |
The ticket ID. |
ticket.subject |
The subject. |
ticket.content |
The ticket text, in Markdown. |
ticket.content_html |
The ticket text rendered as HTML with safe Markdown. HTML that the user typed shows as plain text, so the value is safe to print with |raw. |
ticket.status |
The status, with its name and code. |
ticket.type |
The ticket type, with its name, code and description, or null. |
ticket.user |
The RainLab.User account that owns the ticket. Print the name with ticket.user.full_name. |
ticket.attachments |
The files attached to the original message. |
ticket.messages |
The public replies. Internal messages are left out. |
ticket.created_at |
When the ticket was submitted. |
ticket.closed_at |
When the ticket was closed, or null while it is open. |
ticket.url |
The address of the ticket on the updatePage page. Add ?mode=ticket to it to edit the original message. |
Each message in ticket.messages provides the following values.
| Variable | Description |
|---|---|
message.id |
The message ID. |
message.content |
The message text, in Markdown. |
message.content_html |
The message text rendered as HTML, which is safe to print with |raw. |
message.is_user_message |
True for the user's replies, false for staff replies. |
message.created_user |
The administrator who wrote a staff reply, or null when the administrator was deleted. |
message.attachments |
The files attached to the message. |
message.created_at |
When the message was posted. |
message.url |
The address of the message on the updatePage page. |
Each attachment provides its path, file_name and sizeToString values. The component also sets the updatePage page variable to the property value.
The following page shows the conversation with its own markup. The message-{id} anchor on each message is where mail links and the Ticket Update component return to.
title = "Support Ticket"
url = "/support/ticket/:id"
layout = "default"
[session]
security = "user"
redirect = "account/login"
[supportTicket]
updatePage = "support/update"
isPrimary = 1
==
{% flash %}
<p data-control="flash-message" data-type="{{ type }}" data-interval="5">
{{ message }}
</p>
{% endflash %}
{% set ticket = supportTicket.ticket %}
{% if ticket %}
<h1>
Ticket #{{ ticket.id }}: {{ ticket.subject }}
</h1>
<p>
{{ ticket.status.name }}
</p>
<div>
{{ ticket.content_html|raw }}
</div>
{% if not ticket.closed_at %}
<a href="{{ ticket.url }}?mode=ticket">
Edit
</a>
{% endif %}
{% for message in ticket.messages %}
<div id="message-{{ message.id }}">
<p>
{% if message.is_user_message %}
{{ ticket.user.full_name }}
{% else %}
{{ message.created_user ? message.created_user.full_name : __('Deleted user') }}
{% endif %}
</p>
<div>
{{ message.content_html|raw }}
</div>
{% for file in message.attachments %}
<a href="{{ file.path }}">
{{ file.file_name }} ({{ file.sizeToString }})
</a>
{% endfor %}
{% if message.is_user_message and not ticket.closed_at %}
<a href="{{ message.url }}">
Edit
</a>
{% endif %}
</div>
{% endfor %}
{% else %}
<p>
Ticket cannot be found.
</p>
{% endif %}
AJAX Handlers
The component provides the following AJAX handlers.
| Handler | Description |
|---|---|
| onSubmitMessage | Adds a reply from the user, with its attachments, and reloads the page. |
| onCloseTicket | Closes the ticket and reloads the page. |
Replying
The onSubmitMessage handler accepts the following fields.
| Field | Description |
|---|---|
content |
The reply, in Markdown. Required, and at most 50,000 characters. |
files[] |
Files to attach. See Attachments. |
After saving, the handler reloads the page, which shows the flash message The message has been posted successfully. Staff receive the new-user-message mail, and the reply does not change the ticket status.
{% if not ticket.closed_at %}
{{ form_ajax('onSubmitMessage', { files: true, flash: true, validate: true }) }}
<textarea name="content" rows="5"></textarea>
<div data-validate-for="content"></div>
<input name="files[]" type="file" multiple />
<div data-validate-for="files"></div>
<button type="submit" data-attach-loading>
Submit reply
</button>
{{ form_close() }}
{% endif %}
Closing the Ticket
The onCloseTicket handler closes the ticket and reloads the page, which shows the flash message This ticket has been closed successfully. Staff receive the closed-by-user mail. Only staff can reopen a ticket.
{% if not ticket.closed_at %}
<button
data-request="onCloseTicket"
data-request-confirm="Are you sure you want to close this ticket?"
data-request-flash>
Close ticket
</button>
{% endif %}
Mail Links
The mail sent to the user links to the ticket on the page whose supportTicket component enables isPrimary. The lookup reads the component section of each page, so write isPrimary = 1 there, as in Basic Usage.
- When more than one page enables it, the first page found is used.
- When no page enables it, the links use the first page that has the component.
- When no page has the component, the mail leaves the link out.
Staff mail links to the ticket in the admin panel instead. In PHP, the Responsiv\Support\Components\Ticket::getPrimaryPageUrl(['id' => $ticketId]) method returns the same address that the user mail links to.
Partials
The component renders the following partials.
| Partial | Description |
|---|---|
| default | Wraps the ticket in the responsiv-support element and renders the partials below, followed by the reply form or a closed notice. |
| ticket-details | The ticket title and status, and the Close ticket button. |
| ticket-content | The original message, with its Edit link and attachments. |
| ticket-messages | The public replies, with an Edit link on the user's own replies. |
| ticket-reply-panel | The reply fields and the Submit reply button. |
| flash-messages | Shared. Shows the flash messages. |
| message-attachments | Shared. The attachment list of the ticket or a message. |
To change one of them, copy it into the partials/supportTicket folder of your theme. See Overriding Partials.
Rules
- Ownership. The ticket must belong to the signed-in user. For a guest or another user,
ticketis null and both handlers refuse with Ticket cannot be found. - Internal messages. The component leaves internal messages out of
ticket.messagesbefore the ticket reaches the page, so theme markup cannot show them. - Closed tickets. The
onSubmitMessagehandler refuses a reply with This ticket has been closed. Closing a ticket that is already closed changes nothing. - Validation. The reply is required and can be at most 50,000 characters. Errors are reported under the
contentfield. - Attachments. Files follow the attachment rules, and errors are reported under the
filesfield. - Safe HTML. The
content_htmlvalues of the ticket and its messages are rendered with safe Markdown, so HTML that anyone typed shows as plain text.
Previous: Tickets Next: Ticket Submit