# Events Introduction Events is a full-featured Craft CMS Plugin for event management and ticketing. Events integrates with Craft Commerce so you can easily sell tickets to your events. ## Major Features - Manage events and add optional custom fields - Events live preview and sharing - Generate tickets via events with auto generated SKU's - Manage ticket types and add optional custom fields - PDF Ticket with QR Code - Event ticket check in # Requirements ## Craft CMS Events requires Craft CMS 2.6 or higher. ## Craft Commerce Events extends und uses Craft Commerce features. The minimum version of Craft Commerce needed is 1.2. ## PHP Events requires Craft PHP 5.4 or higher. # Installation & Setup Installing Events is as simple as any other Craft plugin - just drop the `events/` folder in your `craft/plugins` directory and install. ## Installation - [Download the latest version](https://github.com/verbb/Events/archive/master.zip) of events and unzip. - Copy the `events/` directory into your `craft/plugins/` directory. - Inside the Craft control panel, navigate to *Settings → Plugins*. - Locate the row showing the Events plugin and click Install. ## Settings/License With Events installed, you will be able to acess the plugin settings. Here you can enter the license key for using Events and get access to general settings. ![](https://svenjungnickel.tinytake.com/media/58d521?filename=1500029869058_14-07-2017-12-57-49.png&sub_type=thumbnail_preview&type=attachment&width=700&height=378&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc3ODk5OF81ODIxNzI5) In the **General Settings** section you can adjust the *length of the generated ticket SKU*. Ticket SKU's are generated automatically when you add A [Ticket Type](#ticket-types) to an event and when this ticket get purchased. Per default it is set to 6 characters length. ## Setup With Events now installed, you have to setup first: - a [Ticket Type](#ticket-types) - a [Event Type](#event-types) Then you can create Events for your created event type. # Updates Refer to the [Releases](https://github.com/verbb/Events/releases) via our Github repository for all updates and changes. # Feature Tour ## Events ### Overview Go to the main section of Events via the CP sidebar menu. Because events are handled as element types, you will be presented a list similar as the entries overview. Before you can start creating events first you have to set up a [Event Type](#event-types) and a [Ticket Type](#ticket-types). ![](https://svenjungnickel.tinytake.com/media/58d808?filename=1500036440439_14-07-2017-14-47-20.png&sub_type=thumbnail_preview&type=attachment&width=700&height=483&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc3OTQ0MF81ODIyNDcy) The overview shows a list of all events. You can filter them via event types or status and can search via their title. Also supported is a quick edit of the event details via double click at the status icon next to the event title. ### Create/Edit Each field is fairly self-explanatory, but any additional information is provided below. ![](https://svenjungnickel.tinytake.com/media/58d89c?filename=1500037757420_14-07-2017-15-09-17.png&&type=attachment&&&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3) - **Title**: Set up a title for your event to easily find it in the cp panel and display at the frontend. - **Start Date**: The event start date can provided here. - **End Date**: If the event is more then a daily event, you can set a end date here. - **All day event**: If you don't want to set a specific time the event is running, you can simply set it as a all day event via the light switch button. - **Slug**: The slug is the link how the event is displayed at the frontend - **Enable**: Simply enable or disable this event for user in the frontend - **Delete**: The delete button deletes the event. Already purchased tickets for this event are still remain in the database. ![](https://svenjungnickel.tinytake.com/media/58d8bb?filename=1500038082682_14-07-2017-15-14-42.png&sub_type=thumbnail_preview&type=attachment&width=700&height=550&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc3OTUzN181ODIyNjUx) - **Total capacity**: The total capacity are calculated automatically via the quantities of all tickets. But if you want, you can set the capacity independently of the ticket quantities. Keep in mind that if you change the quanitity of one ticket, the total capacity will be recalculated. - **Ticket Type**: You can select a ticket type from a list of previous created ticket types. - **Quantity**: Set a quantity for this ticket type. - **Price**: Specify a price for this ticket type. - **Available From**: If wished, you can set a date from when this ticket type can be purchased. - **Available To**: Also possible is to set a date after that this ticket type can not be purchased anymore. - **Delete Ticket Type**: You can remove this ticket type from the event. Already purchased tickets are still remain in the database. - **Add ticket**: You can add a new ticket row where you can select ticket type, add quantities, prices and so on. ### Live Preview Events can show a live preview of you currently editing event. This is only possible, if you marked *Events of this type have their own URLs* the [Event Type](#event-types) settings and provided a *Event URL Format* as well as a *Event Template*. Read further details under the [Event Type](#event-types) section. The **Share** button allows you to share a draft version of the event. If the event is not live yet, the link contains a token which allows you to show the event detail page. Without the token the event detail page is shown as *page not found*. This ability allows you to share the event link internal to get an approval or similar before you publish the event. ## Event Types ### Overview Here you can find a table list of all created event types. First step(s) to start is to create your first event type you can categories events for. ![](https://svenjungnickel.tinytake.com/media/58da14?filename=1500040819354_14-07-2017-16-00-18.png&sub_type=thumbnail_preview&type=attachment&width=700&height=287&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc3OTcyN181ODIyOTk2) The **Delete** icon deletes already created event types. Created events for this event type will also be deleted. Already purchased tickets still remain in the database. ### Create/Edit Each field is fairly self-explanatory, but any additional information is provided below. ![](https://svenjungnickel.tinytake.com/media/58daa5?filename=1500041906854_14-07-2017-16-18-25.png&sub_type=thumbnail_preview&type=attachment&width=700&height=510&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc3OTgxOV81ODIzMTQx) - **Name**: What this event type will be called in the CP. - **Handle**: How you’ll refer to this event type in the templates. If you ticked **Events of this type have their own URLs**, the following fields appear: - **Event URL Format**: What the event URLs should look like. You can include tags that output event properties, such as such as {slug} or {publishDate|date("Y")}. - **Event Template**: The template to use when a event’s URL is requested. If you ticked it up, you should create a template file and insert the path. You can find a template example in the [Templating](#templating) section. ### Event Fields Event Fields are provided to use custom fields for events. You can find more details under the [Event Custom Fields](#events1) section. ![](https://svenjungnickel.tinytake.com/media/591706?filename=1500307246115_17-07-2017-18-00-55.png&sub_type=thumbnail_preview&type=attachment&width=700&height=650&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc4NjcyNF81ODM4NTk4) ## Ticket Types ### Overview Here you can find a table list of all created ticket types. First step(s) to start is to create your first ticket type you can create tickets for. ![](https://svenjungnickel.tinytake.com/media/591771?filename=1500308060473_17-07-2017-18-14-31.png&sub_type=thumbnail_preview&type=attachment&width=700&height=330&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc4Njc3OV81ODM4NzA1) The **Delete** icon deletes already created ticket types. Created tickets for this ticket type will also be deleted. Already purchased tickets still remain in the database. ### Create/Edit Each field is fairly self-explanatory, but any additional information is provided below. ![](https://svenjungnickel.tinytake.com/media/59178c?filename=1500308426444_17-07-2017-18-20-36.png&sub_type=thumbnail_preview&type=attachment&width=688&height=700&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc4Njc5NF81ODM4NzMy) - **Name**: What this event type will be called in the CP. - **Handle**: How you’ll refer to this event type in the templates. - **Tax Category**: Tickets created and purchased for this ticket type will be categorised in the tax categorie you select here. If you ticked **Tickets of this type have their own URLs**, the following fields appear: - **Ticket URL Format**: What the ticket URLs should look like. You can include tags that output event properties, such as such as {slug} or {publishDate|date("Y")}. - **Ticket Template**: The template to use when a ticket’s URL is requested. If you ticked it up, you should create a template file and insert the path. You can find a template example in the [Templating](#templating) section. ### Ticket Fields Ticket Fields are provided to use custom fields for tickets. You can find more details under the [Ticket Custom Fields](#tickets) section. ![](https://svenjungnickel.tinytake.com/media/5917a1?filename=1500308752879_17-07-2017-18-26-03.png&sub_type=thumbnail_preview&type=attachment&width=700&height=516&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc4NjgwNF81ODM4NzUz) ## PDF Ticket Events supports a PDF version of purchased tickets. They can be printed by the customer to use them at the entrance. This PDF feature is using the Craft CMS integated `Dompdf()` function. ### Template File The printed ticket appearance is handled by a *html template*. You need to create a new template file somewhere in you template directory. A template example is provided in the [Templating](#templating) section under [PDF Ticket](#pdf-ticket1). ### Dynamic Route Add a new dynamic route in *Settings -> Routes*: - **URL**: As example `shop/events/ticket/<tag>/<number>` - `<tag>`: This is the order id where the ticket was purchased. - `<number>`: This is the line item id of the ticket type which was purchased. - **Template**: Put in the path of the previous created template file. As example: `shop/events/_ticket`. You can leave off the html ending. ### Download Link Last but not least you have to provide a download button oder link. You can put it in your `costumer/order.html` file for instance. Don't forget to use the link you pasted in your dynamic route. ``` <a target="_blank" href="/shop/events/ticket/{{ order.id }}">Download PDF Tickets</a> ``` The link has to point to the route you just created. You can also display the download link for every single line item within you line item loop: ``` {% if item.purchasable.elementType == 'Events_Ticket' %} <a target="_blank" href="/shop/events/ticket/{{ order.id }}/{{ item.id }}">Download PDF Tickets</a> {% endif %} ``` ### QR Code Events provides a QR code generation feature. Because URL's are most times super long and not very beautiful to print we decided to use a QR code instead. The generated QR code contains a link to the [Ticket Check In](#ticket-check-in) controller with the the ticket SKU. ## Ticket Check In Events is providing an easy ticket check in. The check in controller validates the ticket sku and checks if the ticket was already checked in to avoid multiple usage. ### Route The route to the check in controller is following: ``` actions/events/ticket/checkin?sku=<sku> ``` ### Parameter - `<sku>`: This is the ticket SKU which gets generated automatically at the purchase of the ticket. This SKU is unique. ### Return The controller returns a json response. On an **error** the response contains a simple error message. On **success** the response contains following: - *success*: Contains the string "Ticket checked in.". - *checkedInDate*: The check in date in the [DATE_ATOM](http://php.net/manual/en/class.datetime.php#datetime.constants.atom) format. # Templating ## Event list You can display a list of all events via the following template snippet: ``` {% for event in craft.events.events.find() %} ``` Events are element types. Hence they are selectable via the [Craft Element Criteria Model](https://craftcms.com/docs/templating/elementcriteriamodel). For instance you can set a limit vouchers via `limit()`. ``` {% for event in craft.events.events.limit(5).find() %} ``` Another way to adjust your list is to specify a event type via `type()`. ``` {% for event in craft.events.events.type('festival').find() %} ``` And of course, you can combine multiple criterias. ``` {% for event in craft.events.events.type('festival').limit(5).find() %} ``` ## Ticket list Tickets are also handled as ticket types. Hence you can select them similar as events via criterias. To get all tickets for a specified event you need to add the event id. ``` {% for ticket in craft.events.tickets.eventId(event.id) %} ``` ### Available Tickets Because tickets can have a `Available From` and `Available To` parameter you need to be aware of this. Events provides a simple helper function to get all available tickets. The event id also needed here again. ``` {% for ticket in craft.events.availableTickets(event.id) %} ``` ### Purchased Tickets Purchased tickets are stored in a extra table called *purchased tickets*. You can get this tickets via `craft.events.purchasedTickets()`. You can give this function up to 2 parameters: - array $attributes: list of attribute values (indexed by attribute names) that the active records should match. - array $options: query condition or criteria. #### Event Capacity Events have a maximum capacity. The capacity are calculated by the summary of the quanitites of all created event tickets. To check for the maximum event capacity you can paste in a event id as an argument. ``` {% set purchasedEventTickets = craft.events.purchasedTickets({ eventId: event.id }) | length %} {% if purchasedEventTickets < event.capacity %} // Your ticket list here... {% endif %} ``` #### Ticket Quantity Every ticket has a maximum quantity. To check for the maximum quantity you can paste in a ticket id as an argument, similar as events. ``` {% set purchasedTickets = craft.events.purchasedTickets({ ticketId: ticket.id }) | length %} {% if purchasedTickets < ticket.quantity %} //... {% endif %} ``` #### Simple Ticket selection A simple ticket selection list can look like following snippet: ``` {# check for event limit #} {% set purchasedEventTickets = craft.events.purchasedTickets({ eventId: event.id }) | length %} {% if purchasedEventTickets < event.capacity %} <form method="POST"> <input type="hidden" name="action" value="commerce/cart/updateCart"> <input type="hidden" name="redirect" value="shop/cart"> <input type="hidden" name="qty" value="1"> {{ getCsrfInput() }} <select name="purchasableId" class="purchasableId"> {%- for ticket in craft.events.availableTickets(event.id) -%} {# check for ticket limits #} {% set purchasedTickets = craft.events.purchasedTickets({ ticketId: ticket.id }) | length %} {% if purchasedTickets < ticket.quantity %} <option value="{{ ticket.purchasableId }}"> {{ ticket.getTicketTypeName() }} - {{ ticket.price|commerceCurrency(cart.currency) }} </option> {%- endif -%} {%- endfor -%} </select> <button type="submit">{{ "Add to cart"|t }}</button> </form> {% else %} <strong>Sold out</strong> {% endif %} ``` #### Multiple Ticket Selection Events is providing a multi ticket selection list. To use it you need to call the custom frontend controller `events/cart/add`. A complete integration can look like following snippet: ``` <form method="POST"> {# custom frontend controller #} <input type="hidden" name="action" value="events/cart/add"> <input type="hidden" name="redirect" value="shop/cart"> {{ getCsrfInput() }} <table width="100%" border="0" cellpadding="0" cellspacing="0"> {% for ticket in craft.events.availableTickets(event.id) %} <tr> <td>{{ ticket.getTicketTypeName() }}</td> <td>{{ ticket.price | commerceCurrency(cart.currency) }}</td> <td align="right" nowrap="nowrap"> <input type="hidden" name="event" value="{{ event.id }}"> {# check for ticket limits #} {% set purchasedTickets = craft.events.purchasedTickets({ ticketId: ticket.id }) | length %} {% set availableTickets = ticket.quantity - purchasedTickets %} {% if availableTickets > 0 %} <div class="field dropdown"> <div class="input"> <select name="item[{{ ticket.id }}][qty]" class="ticket_table_select"> {% set maxDropdown = (availableTickets > 10) ? 10 : availableTickets %} {% for i in 0..maxDropdown %} <option value="{{ i }}">{{ i }}</option> {% endfor %} </select> </div> </div> {% else %} <strong>Sold out</strong> {% endif %} </td> </tr> {% endfor %} </table> <input type="submit" value="{{ "Add to cart"|t }}" class="button"/> </form> ``` ### Displaying Tickets After The Purchase Purchased tickets can be displayed after the purchase in the `costumer/order.html` or `_pdf/order.html` file within the line item loop. Use following template snippet: ``` {% if item.purchasable.elementType == 'Events_Ticket' %} {% set purchasedTickets = craft.events.purchasedTickets({ lineItemId: item.id }) %} {% if purchasedTickets %} {% for purchasedTicket in purchasedTickets %} SKU: {{ purchasedTicket.ticketSku }}<br /> {% endfor %} {% endif %} {% endif %} ``` ## PDF Ticket To generate a pdf ticket you need to create a extra template view. You can use simple html tags, css styling and even twig tags. ### Display Tickets Displaying a list of all purchased tickets for an order just use: ``` craft.events.purchasedTickets({ orderId: tag) ``` *<tag>* is used as order id, which we defined previously in our route setup. If you did add *<number>* into your route setup, you can add `number` as lineItemId: ``` craft.events.purchasedTickets({ orderId: tag, lineItemId: number }) ``` ### PDF Settings - compress: Compress the pdf file. Default is `false` - true - false - orientation: The orientation of the pdf - 'portrait' - 'landscape' - size: The size of the pdf - 'letter' - cacheDirectory: The directory to cache the pdf file. - filename: The filename for the chached pdf file. ### QR Code To replace a ugly URL with a beautiful QR Code, simply use: ``` <img src="{{ ticket.getQR() }}" /> ``` ### Example File Following template snippet can help you creating a appropiated template file: ``` {% set html %} <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <style> html { margin-top:0.2in !important; margin-left:0.2in !important; } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; line-height:1.4em; font-weight:bold; } .ticket { width:8in; height:2.7in; background-size:cover; background-repeat:no-repeat; position:relative; margin-bottom: 0.2in; } #event-info { display:inline-block; position:absolute; left:0.9in; top:0.12in; width:4.7in; } .label { color:#768690; display:block; text-transform:uppercase; } .value { display:block; color:#121212; text-transform:uppercase; overflow:hidden; font-size:16px; } #title { height:0.4in; } #stub-info { display:block; position:absolute; top:0.06in; left:6in; width:1.9in; text-align:center; } #purchased-on { display:inline-block; color:#fff; text-transform:uppercase; font-size:9px; text-align:center; width:100%; position:relative; } #qrcode { position:relative; width: 70%; height: auto; margin-top: 0.3in; margin-left: -1.9in; } #ticket-num { display:block; text-transform:uppercase; text-align:center; width:100%; position:relative; top: 0; left: 0; font-weight:bold; font-size: 12px; } #attendee-info { text-align:left; font-size:10px; position:relative; top:0.18in; line-height: 1.6em; } #attendee-info .value { font-size:10px; } </style> </head> <body> {% set orderId = tag %} {% for ticket in craft.events.purchasedTickets({ orderId: orderId, lineItemId: number }) %} <div class="ticket"> <div id="event-info"> <span class="label">EVENT</span> <span id="title" class="value">{{ ticket.getEvent().title }}</span> <span class="label">DATE AND TIME</span> <span class="value">{{ ticket.getEvent().startDate | date("M j, Y \\a\\t g:i A") }}</span> <span class="label">to</span> <span class="value">{{ ticket.getEvent().endDate | date("M j, Y \\a\\t g:i A") }}</span> </div> <div id="stub-info"> <span id="purchased-on">Purchased on {{ ticket.getOrder().dateOrdered | date("M j, Y \\a\\t g:i A") }}</span> <img id="qrcode" src="{{ ticket.getQR() }}" /> <span id="ticket-num" class="value">#{{ ticket.ticketSku }}</span> <div id="attendee-info"> <span class="label">1 {{ ticket.getTicketTypeName() }} Pass</span> {% if ticket.getOrder().customer.user %} <span id="name" class="value">{{ ticket.getOrder().customer.user.name }}</span> {% else %} {% set address = ticket.getOrder().customer.addresses[0] %} <span id="name" class="value">{{ address.firstName }} {{ address.lastName }}</span> {% endif %} <span id="email" class="value">{{ ticket.getOrder().customer.email }}</span> </div> </div> </div> {% endfor %} </body> </html> {% endset %} {% set settings = { compress: true, orientation: 'portrait', size: 'letter', cacheDirectory: 'cache/tickets', filename: 'JAF2015-' ~ orderId ~ '.pdf' } %} {% set pdf = craft.events.pdfFromHtml(html, settings) %} {{ pdf.output }} ``` # Custom fields Events provides custom fields. You can assign them for [Events](#events1) and [Tickets](#tickets). You can access this custom fields easily as every other custom field in Craft. For instance, an assigned image asset field to an event can be displayed by following template code: ``` <img src="{{ voucher.customFieldImage.first().getUrl() }}" /> ``` In this case `customFieldImage` is the handle of the custom field you did assign. ## Events You can assign custom fields to events via *Event Types*. ![](https://svenjungnickel.tinytake.com/media/591706?filename=1500307246115_17-07-2017-18-00-55.png&sub_type=thumbnail_preview&type=attachment&width=700&height=650&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc4NjcyNF81ODM4NTk4) Once you assigned custom fields to a event type, they appear on the *Event* edit page and can filled with content. Every single tab you assigned in the field layout appears as it own tab with the associated fields. ![](https://svenjungnickel.tinytake.com/media/5943e5?filename=1500453018492_19-07-2017-10-30-18.png&&type=attachment&&&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3) ## Tickets You can assign custom fields to tickets via *Ticket Types*. ![](https://svenjungnickel.tinytake.com/media/59443b?filename=1500453884160_19-07-2017-10-44-44.png&sub_type=thumbnail_preview&type=attachment&width=700&height=625&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3&salt=MTc5MjQ1Nl81ODUwMTcx) Once you assigned custom fields to a ticket type, they appear on the *Event* edit page in the ticket tab under the ticket settings. To display the ticket settings simply click on the gearwheel icon. All assigned custom fields appear under the *Available From/To* fields. ![](https://svenjungnickel.tinytake.com/media/594935?filename=1500465101739_19-07-2017-13-51-39.png&&type=attachment&&&_felix_session_id=241ceac8aaa6d209c3f1c693c9f99aa3) Because tab handling is not supported here, just assign all custom fields into the same tab in the ticket type field layout editor. **Notice** After you selected a ticket type, assigned custom fields won't appear. First save the event and reopen the ticket settings, then custom fields will be displayed. # Support ## Slack Get in touch via the [Craft Community Slack](https://buildwithcraft.com/community#slack) and be sure to post in the `#help` channel and mention our handle (`@crawf`). This is the most preferred method. ## GitHub If you've found a bug, or would like to make a feature request, head to the [GitHub Repo](https://github.com/verbb/Events/issues) and file an issue. Pull requests are also most welcome! ## Twitter Get our attention on Twitter by using the `#craftcms` hashtag and mentioning `@verbb` ## Stack Exchange Ask a question via the [Craft Stack Exchange](http://craftcms.stackexchange.com) and tag your question with `plugin-events`. ## Email Any feedback, comments, questions or suggestions please email us at `support at verbb.io`.