User Support

Ticket system for communicating with users

Ticket Update Component

The supportTicketUpdate component lets the signed-in user edit their original ticket or one of their own replies, including its attachments.

Property Description
ticketPage The page with the Ticket component to return to after saving.

Basic Usage

The page URL must include an :id parameter. The mode query parameter sets what the ID refers to.

  • With ?mode=ticket, the page edits the original ticket, and :id is the ticket ID.
  • Without it, or with any other value, the page edits a reply, and :id is the message ID.

The Ticket component builds both links, as ticket.url with ?mode=ticket added and as message.url. The component only edits the records of the signed-in user, so restrict the page to signed-in users with the RainLab.User session component.

title = "Update Message"
url = "/support/update/:id"
layout = "default"

[session]
security = "user"
redirect = "account/login"

[supportTicketUpdate]
ticketPage = "support/ticket"
==
{% component "supportTicketUpdate" %}

The default form shows the subject in ticket mode, the message, a Minor update checkbox, the current attachments with a Delete button on each, and a field for new attachments.

Page Variables

The component object provides the following values.

Variable Description
supportTicketUpdate.model The ticket in ticket mode, or the message in message mode. It is null when the signed-in user cannot edit the record.
supportTicketUpdate.returnUrl The address of the ticket on the ticketPage page, pointing at the message in message mode.

The record provides its content and attachments, and in ticket mode its subject. In message mode, model.ticket is the ticket that the message belongs to.

The component also sets these page variables.

Variable Description
updateMode The edit mode, which is ticket or message.
ticketPage The ticketPage property value.

The following page edits the record with its own markup. It posts the mode field and keeps the attachment list in the partialUpdateAttachments element, which the delete buttons update.

title = "Update Message"
url = "/support/update/:id"
layout = "default"

[session]
security = "user"
redirect = "account/login"

[supportTicketUpdate]
ticketPage = "support/ticket"
==
{% set model = supportTicketUpdate.model %}
{% set ticket = model and updateMode == 'message' ? model.ticket : model %}

{% if not model %}
    <p>
        Message cannot be found.
    </p>
{% elseif ticket.closed_at %}
    <p>
        This ticket has been closed.
    </p>
{% else %}
    {{ form_ajax('onUpdateMessage', { files: true, flash: true, validate: true }) }}
        <input type="hidden" name="mode" value="{{ updateMode }}" />

        {% if updateMode == 'ticket' %}
            <input name="subject" type="text" maxlength="255" value="{{ model.subject }}" />
            <div data-validate-for="subject"></div>
        {% endif %}

        <textarea name="content" rows="8">{{ model.content }}</textarea>
        <div data-validate-for="content"></div>

        <input type="hidden" name="minor_update" value="0" />
        <label>
            <input name="minor_update" type="checkbox" value="1" checked />
            Minor update
        </label>

        <div id="partialUpdateAttachments">
            {% partial 'supportTicketUpdate::update-attachments' attachments=model.attachments %}
        </div>

        <input name="files[]" type="file" multiple />
        <div data-validate-for="files"></div>

        <button type="submit" data-attach-loading>
            Save
        </button>
        <a href="{{ supportTicketUpdate.returnUrl }}">
            Cancel
        </a>
    {{ form_close() }}
{% endif %}

AJAX Handlers

The component provides the following AJAX handlers.

Handler Description
onUpdateMessage Validates and saves the edited record with its attachments, then returns to the ticket page.
onDeleteAttachment Marks an attachment for removal when the form is saved, and refreshes the attachment list.

Saving Changes

The onUpdateMessage handler accepts the following fields.

Field Description
mode The edit mode. Post it with the form, as the default form does, so the handler edits the same record as the page.
subject The subject, in ticket mode only. Required, and at most 255 characters.
content The text, in Markdown. Required, and at most 50,000 characters.
minor_update When posted as 0, staff receive the ticket-message-updated mail. When it is 1 or missing, the edit counts as minor and sends no mail.
files[] Files to attach. See Attachments.
redirect Optional. A page name, or a path on this site, that replaces the ticketPage property.

The form's session key, which form_ajax() adds as _session_key, links the new attachments and the removals to the save. After saving, the handler sets the flash message The message has been updated successfully. and redirects to the returnUrl address.

To let the user choose whether staff are notified, post a hidden 0 value before the checkbox, as the default form does, so that unticking the box sends 0.

<input type="hidden" name="minor_update" value="0" />
<label>
    <input name="minor_update" type="checkbox" value="1" checked />
    Minor update
</label>

See Staff Recipients for who receives the mail.

Deleting Attachments

The onDeleteAttachment handler accepts the following fields.

Field Description
file_id The ID of the attachment to remove.
_session_key The session key of the edit form. Required.

The handler marks the file for removal with the session key, and the file is removed when the form is saved. Leaving the page without saving keeps the file. The handler then sets the attachments page variable to the remaining attachments, and showDelete to true, for the update-attachments partial.

Place the button inside the edit form, so that the request sends the form's session key and mode.

{% for attachment in attachments %}
    <a href="{{ attachment.path }}">
        {{ attachment.file_name }}
    </a>
    <button
        type="button"
        data-request="onDeleteAttachment"
        data-request-data="{ file_id: {{ attachment.id }} }"
        data-request-update="{ '@update-attachments': '#partialUpdateAttachments' }"
        data-request-flash>
        Delete
    </button>
{% endfor %}

Partials

The component renders the following partials.

Partial Description
default Wraps the edit form in the responsiv-support element, or shows a notice when the record cannot be found or its ticket is closed.
update-attachments The current attachments, each with a Delete button.
flash-messages Shared. Shows the flash messages.

To change one of them, copy it into the partials/supportTicketUpdate folder of your theme. See Overriding Partials.

Rules

  • Signed-in users. The onUpdateMessage handler refuses a guest with You should be logged in.
  • Ownership. In ticket mode, the ticket must belong to the signed-in user. In message mode, the message must be one of the user's own replies on their ticket, so staff replies and internal messages cannot be edited. Otherwise model is null and both handlers refuse with Message cannot be found.
  • Closed tickets. Both handlers refuse to change a closed ticket, or a reply on one, with This ticket has been closed.
  • Ticket type. Only the subject and content are saved. A posted ticket type is ignored.
  • Validation. Errors are reported under the subject and content fields.
  • Attachments. New files follow the attachment rules, where attachmentMaxFiles counts the files added in one save, and errors are reported under the files field. The onDeleteAttachment handler only removes files of the record being edited.