How to create a conference website with Joomla Core - Part 2
In the first part of this article I wrote about the planning around the conference website. I described the content types and the functionality we want to have. Now I will show you what happens after a speaker submits a talk.
As I wrote in the previous article the submitted talks are stored unpublished in the category “Talks”. The speakers get redirected to a “thank you” page with some information and the administrators get an e-mail informing about the new submission (this function is present in the plugin “Content - Joomla”).
In some months the team will sit together and will decide which talks are going to be part of the conference program. The accepted talks / articles will be published and the corresponding speaker / contact too. The team will inform the speakers and they will be able to edit their contact entry.
When speakers now login into the page they see the following

- Accepted talks: Articles Module showing only the title of the articles in the category talks filtered by current user

- My speaker profile: that is an own module listing the contact(s) associated with the current user. The link leads directly to the contact view which can be edited to add a photo, a description and some additional information.

The edit form for the contact has been simplified with an override similar to the edit form for content (explained in the previous article):
<?php
/**
* @package Joomla.Site
* @subpackage com_contact
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Contact\Administrator\Helper\ContactHelper;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
/** @var \Joomla\Component\Contact\Site\View\Category\HtmlView $this */
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->getDocument()->getWebAssetManager();
$wa->useScript('core');
$canDo = ContactHelper::getActions('com_contact', 'category', $this->category->id);
$canEdit = $canDo->get('core.edit');
$userId = $this->getCurrentUser()->id;
$showEditColumn = false;
if ($canEdit) {
$showEditColumn = true;
} elseif ($canDo->get('core.edit.own') && !empty($this->items)) {
foreach ($this->items as $item) {
if ($item->created_by == $userId) {
$showEditColumn = true;
break;
}
}
}
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>
<div class="com-contact-category__items">
<?php if (empty($this->items)) : ?>
<?php if ($this->params->get('show_no_contacts', 1)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_CONTACT_NO_CONTACTS'); ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="com-contact row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 gy-3" id="contactList">
<?php foreach ($this->items as $i => $item) : ?>
<?php if ($this->items[$i]->published == 0) : ?>
<div class="system-unpublished col">
<?php else : ?>
<div class="col" >
<?php endif; ?>
<?php if ($canEdit || ($canDo->get('core.edit.own') && $item->created_by === $userId)) : ?>
<div>
<?php echo HTMLHelper::_('contacticon.edit', $item, $this->params); ?>
</div>
<?php endif; ?>
<?php if ($item->published == 0) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JUNPUBLISHED'); ?>
</span>
</div>
<?php endif; ?>
<?php if ($item->publish_up && strtotime($item->publish_up) > strtotime(Factory::getDate())) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JNOTPUBLISHEDYET'); ?>
</span>
</div>
<?php endif; ?>
<?php if (!is_null($item->publish_down) && strtotime($item->publish_down) < strtotime(Factory::getDate())) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JEXPIRED'); ?>
</span>
</div>
<?php endif; ?>
<?php if ($item->published == -2) : ?>
<div>
<span class="badge bg-warning text-light">
<?php echo Text::_('JTRASHED'); ?>
</span>
</div>
<?php endif; ?>
<div class="com-contact-single card">
<?php if ($this->params->get('show_image_heading')) : ?>
<?php if ($item->image) : ?>
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $item->image,
'alt' => $this->escape($item->name),
'class' => 'contact-thumbnail rounded-top-2 mb-3',
]
); ?>
<?php else : ?>
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => 'images/design/placeholder.png',
'alt' => $this->escape($item->name),
'class' => 'contact-thumbnail rounded-top-2 mb-3',
]
); ?>
<?php endif; ?>
<?php endif; ?>
<div class="px-2 d-flex flex-column align-items-center">
<h2>
<a href="/<?php echo Route::_(RouteHelper::getContactRoute($item->slug, $item->catid, $item->language)); ?> " class="stretched-link">
<?php echo $this->escape($item->name); ?>
</a>
</h2>
<?php echo $item->event->afterDisplayTitle; ?>
<?php echo $item->event->beforeDisplayContent; ?>
<?php if ($this->params->get('show_position_headings') && !empty($item->con_position)) : ?>
<p><?php echo $item->con_position; ?></p>
<?php endif; ?>
<?php if ($this->params->get('show_country_headings') && !empty($item->country)) : ?>
<p><?php echo $item->country; ?></p>
<?php endif; ?>
<?php echo $item->event->afterDisplayContent; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($canDo->get('core.create')) : ?>
<?php echo HTMLHelper::_('contacticon.create', $this->category, $this->category->params); ?>
<?php endif; ?>
<?php if ($this->params->get('show_pagination', 2)) : ?>
<div class="com-contact-category__pagination w-100">
<?php if ($this->params->def('show_pagination_results', 1)) : ?>
<p class="com-contact-category__counter counter float-end pt-3 pe-2">
<?php echo $this->pagination->getPagesCounter(); ?>
</p>
<?php endif; ?>
<?php echo $this->pagination->getPagesLinks(); ?>
</div>
<?php endif; ?>
</div>
To avoid confusions we have installed the plugin “Restricted FS” (https://restrictedfs.dgrammatiko.dev/) that restrict the upload of images to a specific folder per user.
For the speakers we have prepared some overrides. For the category view:

Override of default_items.php
<?php
/**
* @package Joomla.Site
* @subpackage com_contact
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Contact\Administrator\Helper\ContactHelper;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
/** @var \Joomla\Component\Contact\Site\View\Category\HtmlView $this */
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->getDocument()->getWebAssetManager();
$wa->useScript('core');
$canDo = ContactHelper::getActions('com_contact', 'category', $this->category->id);
$canEdit = $canDo->get('core.edit');
$userId = $this->getCurrentUser()->id;
$showEditColumn = false;
if ($canEdit) {
$showEditColumn = true;
} elseif ($canDo->get('core.edit.own') && !empty($this->items)) {
foreach ($this->items as $item) {
if ($item->created_by == $userId) {
$showEditColumn = true;
break;
}
}
}
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>
<div class="com-contact-category__items">
<?php if (empty($this->items)) : ?>
<?php if ($this->params->get('show_no_contacts', 1)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_CONTACT_NO_CONTACTS'); ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="com-contact row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 gy-3 mb-2" id="contactList">
<?php foreach ($this->items as $i => $item) : ?>
<?php if ($this->items[$i]->published == 0) : ?>
<div class="system-unpublished col">
<?php else : ?>
<div class="col" >
<?php endif; ?>
<div class="com-contact-single card">
<?php if ($this->params->get('show_image_heading')) : ?>
<?php if ($item->image) : ?>
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $item->image,
'alt' => $this->escape($item->name),
'class' => 'contact-thumbnail rounded-top-2 mb-3',
]
); ?>
<?php else : ?>
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => 'images/design/placeholder.png',
'alt' => $this->escape($item->name),
'class' => 'contact-thumbnail rounded-top-2 mb-3',
]
); ?>
<?php endif; ?>
<?php endif; ?>
<div class="px-2 d-flex flex-column align-items-center">
<h2>
<a href="/<?php echo Route::_(RouteHelper::getContactRoute($item->slug, $item->catid, $item->language)); ?> " class="stretched-link">
<?php echo $this->escape($item->name); ?>
</a>
</h2>
<?php echo $item->event->afterDisplayTitle; ?>
<?php echo $item->event->beforeDisplayContent; ?>
<?php if ($this->params->get('show_position_headings') && !empty($item->con_position)) : ?>
<p><?php echo $item->con_position; ?></p>
<?php endif; ?>
<?php if ($this->params->get('show_country_headings') && !empty($item->country)) : ?>
<p><?php echo $item->country; ?></p>
<?php endif; ?>
<?php echo $item->event->afterDisplayContent; ?>
<?php if ($canEdit || ($canDo->get('core.edit.own') && $item->created_by === $userId)) : ?>
<div>
<?php echo HTMLHelper::_('contacticon.edit', $item, $this->params); ?>
</div>
<?php endif; ?>
<?php if ($item->published == 0) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JUNPUBLISHED'); ?>
</span>
</div>
<?php endif; ?>
<?php if ($item->publish_up && strtotime($item->publish_up) > strtotime(Factory::getDate())) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JNOTPUBLISHEDYET'); ?>
</span>
</div>
<?php endif; ?>
<?php if (!is_null($item->publish_down) && strtotime($item->publish_down) < strtotime(Factory::getDate())) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JEXPIRED'); ?>
</span>
</div>
<?php endif; ?>
<?php if ($item->published == -2) : ?>
<div>
<span class="badge bg-warning text-light">
<?php echo Text::_('JTRASHED'); ?>
</span>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($canDo->get('core.create')) : ?>
<?php echo HTMLHelper::_('contacticon.create', $this->category, $this->category->params); ?>
<?php endif; ?>
<?php if ($this->params->get('show_pagination', 2)) : ?>
<div class="com-contact-category__pagination w-100">
<?php if ($this->params->def('show_pagination_results', 1)) : ?>
<p class="com-contact-category__counter counter float-end pt-3 pe-2">
<?php echo $this->pagination->getPagesCounter(); ?>
</p>
<?php endif; ?>
<?php echo $this->pagination->getPagesLinks(); ?>
</div>
<?php endif; ?>
</div>
And for the contact:

Override of default.php:
<?php
/**
* @package Joomla.Site
* @subpackage com_contact
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
/** @var \Joomla\Component\Contact\Site\View\Contact\HtmlView $this */
$tparams = $this->item->params;
$canDo = ContentHelper::getActions('com_contact', 'category', $this->item->catid);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by === $this->getCurrentUser()->id);
$htag = $tparams->get('show_page_heading') ? 'h2' : 'h1';
$htag2 = ($tparams->get('show_page_heading') && $tparams->get('show_name')) ? 'h3' : 'h2';
$icon = $this->params->get('contact_icons') == 0;
?>
<div class="com-contact contact">
<?php if ($tparams->get('show_page_heading')) : ?>
<h1>
<?php echo $this->escape($tparams->get('page_heading')); ?>
</h1>
<?php endif; ?>
<?php if ($this->item->name && $tparams->get('show_name')) : ?>
<div class="page-header">
<<?php echo $htag; ?>>
<?php if ($this->item->published == 0) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<span class="contact-name"><?php echo $this->item->name; ?></span>
</<?php echo $htag; ?>>
</div>
<?php endif; ?>
<?php if ($canEdit) : ?>
<div class="icons">
<div class="float-end">
<div>
<?php echo HTMLHelper::_('contacticon.edit', $this->item, $tparams); ?>
</div>
</div>
</div>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($tparams->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<div class="com-contact__tags">
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
</div>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if ($this->params->get('show_info', 1)) : ?>
<div class="row mb-5">
<?php if ($this->item->image && $tparams->get('show_image')) : ?>
<div class="col-sm-4">
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $this->item->image,
'alt' => $this->item->name,
'class' => 'rounded-2',
]
); ?>
</div>
<?php endif; ?>
<div class="col-sm-8">
<p><?php echo $this->item->country; ?></p>
<?php if ($this->item->con_position && $tparams->get('show_position')) : ?>
<p><?php echo $this->item->con_position; ?></p>
<?php endif; ?>
<?php if ($this->item->misc && $tparams->get('show_misc')) : ?>
<?php echo $this->item->misc; ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ($tparams->get('show_articles') && $this->item->user_id && $this->item->articles) : ?>
<?php echo '<' . $htag2 . '>' . Text::_('JWC_TALKS') . '</' . $htag2 . '>'; ?>
<?php echo $this->loadTemplate('articles'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_profile') && $this->item->user_id && PluginHelper::isEnabled('user', 'profile')) : ?>
<?php echo '<' . $htag2 . '>' . Text::_('COM_CONTACT_PROFILE') . '</' . $htag2 . '>'; ?>
<?php echo $this->loadTemplate('profile'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_user_custom_fields') && $this->contactUser) : ?>
<?php echo $this->loadTemplate('user_custom_fields'); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>
The talks (articles) from the speaker are listed below the profile. That is possible setting the parameter “User Articles” to “Show” in the menu item:

The title “My talk(s) is a language override:

The program of the conference will also be created using Joomla Core. How? That will be the topic of my next article ;-)
Don’t forget to visit the conference website: https://conference.joomla.org/. Think about joining the international Joomla Community in October and if you have something special to talk about, please submit your session!
Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project
By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/

Comments 2
Hi Viviana,
Thanks for the first two articles in this series. As I'm involved in an academic non-profit organisation over in Canada and maintain their website (I'm currently migrating their website from v3 to v6), and that organisation not only holds an annual conference but also produces an annual journal, I am especially interested in this use of Joomla. I guess this type of site could also be adapted and used for submissions a peer-reviewed academic journal as well (or at least the initial proposal of abstract submission).
As our organisation holds its annual conference each November, and our journal is also published at the same time, I think it's too late to set up a conference website on these lines for the 2026 conference, but one question I do have, if I may... these are the first two of how many sections for the full article? Could you please let us know? I'd be happy to build a similar site along your lines (giving you full credit, of course), and then 'sell' the finished site to our Board for their views.
I guess, in addition, that as our 'new' version site uses Gantry 5 and Joomla 6, it would still work within that framework?
Thanks again for the interesting articles. I look forward to building a version of your conference website in our test area
Peter Robinson, Society for the Study of Egyptian Antiquities, Canada
Hi Peter, nice to hear my articles are useful The series will include at leat one more article about creating the schedule / programme for the conference, probably next month.
You can a use a similar system combined with workflows to create a peer-reviewed academic journal (we are working on a relaunch of this Magazine doing that - we will probably write about that or give a presentation somewhere).
About Gantry, I think it should work. You use Joomla core for the content and function and Gantry to create the visual part.
If you have specific questions, you can contact me per e-mail or join the Joomla Community at Mattermost.