Hi! We use Events quite a lot and one feature request that keeps popping up is the possibility to limit the amount of people being able to say the are “Going” to a certain number. Any thought on this, or maybe even a plan for implementing it?
I would like to see this feature, since we have some events limited to a specific number of attendees. Also showing on the topic the number of free spots would be awesome
I agree. These additional features mentioned above would be very helpful for our project.
What would work well with attendee number limit is a waiting list so that you can still subscribe if the event limit is reached but you will receive a message if a spot becomes available
Would you also want to limit the number of people who could sign up for the wait list? And if so, would that be a percentage of the capacity or a fixed number? Presuming a limit, you might end up closing off the wait list too, so you’d probably want additional messaging around that.
And thinking about messaging…would you want to send one once the event has started to anyone still on the wait list, thanking them for their interest and encouraging them to come to other events?
And if you’re having an in-person event where travel time might be required, all of these messages would likely need to be sent prior to the event. So perhaps you’d want to be able to set that window per event (eg, event_start_time - window_close_in_minutes)?
Hi, thank you for this nice insight, you look familiar with event organization.
I don’t think it is necessary except if anyone here has a relevant case use to propose: for me a 30 person event has a waiting list of 30 persons as well and it stops there as setting up a limit implies anticipating the number of people who are going to ‘unattend’ your event : if 30 people don’t participate because of bad weather at the last minute, it would make sens to have 30 spots free for bravest people on the waiting list.
You made me think that closing the waiting list implies 2 main situations and thus 2 similar features :
-One way being fully closing it making participants on the waiting list not able to get a free spot at all : If the event requires custom preparation for/from the participants you don’t want a spot to get attributed at the very last moment to someone on the waiting list who would come without its white tuxedo to the party
-Another way being partially closing it implying the unability to join the waiting list anymore while keeping the ability for people on it to still get a spot if one becomes avalable.
If we are talking about the case where the waiting list gets fully closed : once the event has started it is too late as you may want to let people know in advance that there is no possibility at all that they will be joining the event while thanking them for their interest as you suggested.
But it may be that it is what you suggest at the end of your message where you mention travelling time requirements?
Last point : I don’t want to hijack my feature request with another feature request but if this whole things gets setup I think it is important to let you know that people may ask in the future also for the ability for participants to join the event with external participants.
Example : on meetup.com you can create a 30 max participants event with the ability for each participant to mention that they will come with for example max 4 external participants : if a participant chooses to to come with 4 friends when the event still has 30 spots, then the remaining amount of spots available is now 25.
Sorry for the obvious maths, I just wanted to clarify this feature that might go along with the waiting list feature.
In my case the platform I worked on was for both live performance ticketing (mostly theatre) and awards voting. I was intimately involved with that for quite some time.
This is a good call out.
I’ve found managing capacity for physical venues can be difficult. I’m in Los Angeles, and you can count on anywhere from 10-30% no-shows for every performance. If the wait list is too small you may have unsold seats, too big and you could be turning away people who may never come back. And you also need to account for adequate travel time if you’re going to let those people on the wait list know they have a ticket.
On our platform, we had to account for +1s and how best to track and report that to the event producers. The event registrant was the most important to them as they were also the person who was going to cast votes on the show. If they ‘returned’ their +1s ticket, we wanted to make sure the producer could either sell that or use it to paper the house. So for us, giving a producer the ability to define when to cut off ticket sales or additions to the wait list was really important.
I agree…this isn’t really within the scope of this request. But I don’t often get to talk about those concerns anymore so I wanted to jump in since this is an area where I do have some experience.
I would not have thought of that, your way of implementing the waiting list makes perfect sens for me now.
This other example shows how each business requires different custom specificities. Finding the fitting ticket event extension in the Wordpress/Joomla extensions market takes quiet a time and may even not have you satisfied in the end as it lacks the community aspect of discourse.
A combination of discourse and an external ticket extension could work well and that is what most meetup.com organizers that I know do. The only downside is that participants must join the event on meetup/discourse but also purchase the ticket externally and sometimes they don’t purchase the tickets directly.
For me there is an open market a for a truly synchronised community event and ticket sale system.
While I wish that in the future this feature comes up along with a waiting list management, here is a temporary solution, note that you will get a second page refresh on every topic where there is an that is full, it can be bothersome for some, for me its ok
How to?
1° add the script at the bottom in admin>appearance>theme>edit>edit code>head(make sure it is placed between tags
2° Create an event that in its title contains any number with the letter ‘p’ after like 2p for two participants(any number to 500 will work), you may change what comes after the number in the script at the bottom by modifying the 2 occurences of if (location.pathname.includes(i + “p”)) { )
This will appear in the url and we can match it with the number of persons going to the event.
3° Test it, notice that there is a page refresh any time the event becomes full or when it passes from full to not full, this is necessary because discourse is a single page application.
If anyone knows how to show the registration timestamp in the participant list, some kind of waiting list could be achieved.
Enjoy !
The code :(updated 14/8 11am utc 0 because of Edge issues)
<style>
/* Smooth text fade when the label changes */
.d-button-label {
transition: opacity 0.25s ease;
}
.d-button-label.updating {
opacity: 0;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const refreshFlagKey = "p-refreshed";
const capacityRegex = /([1-9]|[1-9][0-9]|[1-4][0-9]{2}|500)p/;
let capacityCheckDone = false; // Prevent repeated checks
function getCapacityFromURL() {
const match = location.pathname.match(capacityRegex);
return match ? parseInt(match[0].replace('p', ''), 10) : null;
}
function isEventFull() {
const capacity = getCapacityFromURL();
if (!capacity) return false;
return Array.from(document.querySelectorAll('span.going'))
.some(span => parseInt(span.textContent.trim(), 10) === capacity);
}
function markFull() {
const button = document.querySelector('.btn.btn-icon-text.going-button');
const label = button ? button.querySelector('.d-button-label') : null;
if (label) {
label.classList.add('updating'); // fade out
setTimeout(() => {
label.textContent = "full";
label.classList.remove('updating'); // fade back in
}, 150);
}
if (button) {
button.style.pointerEvents = 'none';
button.style.opacity = '0.6';
}
}
function checkCapacity() {
if (capacityCheckDone) return; // Avoid infinite loops
const capacity = getCapacityFromURL();
if (!capacity) return;
let isFull = false;
document.querySelectorAll('span.going').forEach(function (span) {
if (parseInt(span.textContent.trim(), 10) === capacity) {
markFull();
isFull = true;
}
});
if (isFull) {
capacityCheckDone = true;
const notGoingBtn = document.querySelector('.btn.btn-icon-text.not-going-button');
if (notGoingBtn) {
notGoingBtn.addEventListener('click', function () {
location.reload();
});
}
const interestedBtn = document.querySelector('.btn.btn-icon-text.interested-button');
if (interestedBtn) {
interestedBtn.addEventListener('click', function () {
location.reload();
});
}
}
const goingBtn = document.querySelector('.btn.btn-icon-text.going-button');
if (goingBtn) {
goingBtn.addEventListener('click', function () {
setTimeout(function () {
const count = parseInt(document.querySelector('span.going')?.textContent.trim(), 10);
if (count === capacity) {
location.reload();
}
}, 300);
});
}
}
function observeUntilReady() {
const observer = new MutationObserver(() => {
if (document.querySelector('.btn.btn-icon-text.going-button') &&
document.querySelector('span.going')) {
observer.disconnect();
checkCapacity();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
function refreshIfNeeded() {
if (!sessionStorage.getItem(refreshFlagKey)) {
sessionStorage.setItem(refreshFlagKey, "true");
setTimeout(() => location.reload(), 100);
}
}
function patchHistoryMethod(method) {
const original = history[method];
history[method] = function () {
const result = original.apply(this, arguments);
window.dispatchEvent(new Event("locationchange"));
return result;
};
}
patchHistoryMethod("pushState");
patchHistoryMethod("replaceState");
addEventListener("popstate", () => dispatchEvent(new Event("locationchange")));
addEventListener("locationchange", function () {
if (getCapacityFromURL() && isEventFull()) {
refreshIfNeeded();
} else {
sessionStorage.removeItem(refreshFlagKey);
}
});
observeUntilReady();
// Initial check — only refresh if already full
if (isEventFull()) {
refreshIfNeeded();
}
});
</script>
I started work on this here:
Awesome, maybe in the future we will have a waiting list so that when a spot gets freed by a participant it becomes available for another, for now I am retrieving the registration time of participants with data explorer and use the “interested” button for the waiting list.