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, ticket is null and both handlers refuse with Ticket cannot be found.
  • Internal messages. The component leaves internal messages out of ticket.messages before the ticket reaches the page, so theme markup cannot show them.
  • Closed tickets. The onSubmitMessage handler 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 content field.
  • Attachments. Files follow the attachment rules, and errors are reported under the files field.
  • Safe HTML. The content_html values of the ticket and its messages are rendered with safe Markdown, so HTML that anyone typed shows as plain text.