πŸŽ‰ A brand-new plugin store for Brilliant Directories is coming. It’s a powerful alternative to Elfsight focused on BD β€” join the waitlist today!

menu icon

How to Add Multiple Images to Events in Brilliant Directories (Step-by-Step)

Jun 16, 2025β€’Events
How to Add Multiple Images to Events in Brilliant Directories (Step-by-Step)

Introduction

Hi there! I'm happy to have you here. In this tutorial, I'll guide you step by step on how to add multiple images to the event feature.

I know β€” you might be wondering, "Why isn't this a default functionality?" Well, the short answer is because of the type of feature Brilliant Directories chose when building the event functionality.

Brilliant Directories has two types of features: ones like events, coupons, etc., which are designed to allow just one image, and others like groups, where you can upload multiple photos.

I've seen many Brilliant Directories owners run into this limitation, and that's why I'm sharing one of several ways to make it possible to have multiple photos for your events.

How it works

You know what they say β€” a video says more than a thousand words! πŸ˜…

Adding the first custom widget

This widget will handle everything related to your event images: adding new ones, viewing them, deleting, and even featuring an image so you can use it across different areas where BD displays images β€” like data tables, event streams, and in Manage My Directory where you manage your posts. The most important part? This is the image that shows up when you share the URL.

Let's get started with the first widget:

  • Visit managemydirectory and go to Toolbox > Widget Manager in the left sidebar.
  • Click the New Widget button.
  • Name the widget event-multiple-images.
  • Copy and paste the code below to add all the functionality and design.
<?php
// Set the maximum number of images that can be uploaded.
$max_images = 10;
?>
<style>
    #multi-image-uploader {
        position: relative;
        min-height: 250px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        overflow: hidden;
    }
 
    #multi-image-uploader.dragover .upload-area-container {
        background-color: #e0e0e0;
    }
 
    #multi-image-uploader .preview-area {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        padding: 10px;
    }
 
    #multi-image-uploader .slider-wrapper {
        width: 100%;
        height: 150px;
        overflow: hidden;
        position: relative;
        margin-bottom: 10px;
    }
 
    #multi-image-uploader .slider-container {
        display: flex;
        height: 100%;
        transition: transform 0.5s ease-in-out;
    }
 
    #multi-image-uploader .slide {
        min-width: 100%;
        max-width: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        padding: 0 50px; /* Space for nav buttons */
    }
 
    #multi-image-uploader .slide img {
        max-width: 100%;
        max-height: 100%;
        object-fit: contain;
    }
 
    #multi-image-uploader .slide .remove-image {
        position: absolute;
        top: 0;
        right: 40px;
        line-height: 1;
        z-index: 10;
    }
 
    #multi-image-uploader .slider-nav {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        z-index: 100;
    }
 
    #multi-image-uploader .slider-nav.prev {
        left: 10px;
    }
 
    #multi-image-uploader .slider-nav.next {
        right: 10px;
    }
 
    #multi-image-uploader .slide .feature-checkbox-wrapper {
        position: absolute;
        bottom: 5px;
        left: 50%;
        transform: translateX(-50%);
        z-index: 10;
        background-color: rgba(0, 0, 0, 0.6);
        color: white;
        padding: 3px 8px;
        border-radius: 10px;
        font-size: 12px;
        display: flex;
        align-items: center;
    }
 
    #multi-image-uploader .slide .feature-checkbox-wrapper label {
        margin: 0 0 0 5px; /* Add space between checkbox and label */
        cursor: pointer;
    }
</style>
 
<div id="multi-image-uploader" class="form-group data-upload-image well col-xs-12 col-sm-5 pull-right" style="z-index: 1;">
 
    <div class="upload-area-container text-center">
        <div class="emptyphoto nomargin">
            <div class="img-thumbnail well text-center nomargin bg-secondary center-block">
                <p class="font-lg vmargin"><i class="fa fa-picture-o fa-3x bmargin"></i><br>ADD AN IMAGE</p>
            </div>
        </div>
        <div class="filebutton tmargin" style="width:100%;overflow:hidden;">
            <label for="file-input" class="btn btn-sm bold btn-primary btn-block js-upload-trigger">
                <span class="glyphicon glyphicon-open nomargin"></span> Upload An Image
            </label>
        </div>
        <span class="help-block text-center">Recommended Size: 1200 by 640 pixels. Maximum <?php echo $max_images; ?> images.</span>
    </div>
 
    <div class="preview-area" style="display: none;">
        <div class="slider-wrapper">
            <div class="slider-container">
                <!-- Image previews will be inserted here -->
            </div>
            <button type="button" class="slider-nav prev btn btn-default btn-sm" style="display: none;">&lt;</button>
            <button type="button" class="slider-nav next btn btn-default btn-sm" style="display: none;">&gt;</button>
        </div>
        <div id="image-counter" class="text-center" style="margin: 5px 0;"></div>
         <div class="upload-more-area">
            <label for="file-input" class="btn btn-sm bold btn-secondary btn-block js-upload-trigger">
                <span class="glyphicon glyphicon-plus nomargin"></span> Add More Images
            </label>
        </div>
        <span class="help-block text-center">Recommended Size: 1200 by 640 pixels. Maximum <?php echo $max_images; ?> images.</span>
    </div>
 
    <input type="file" id="file-input" multiple accept="image/jpeg,image/png,image/gif,image/webp" style="display: none;">
    <input type="hidden" id="multi_image_paths" name="multi_image_paths" value="">
    <input type="hidden" id="post_image" name="post_image" value="">
</div>
 
<script>
document.addEventListener('DOMContentLoaded', function() {
    const maxImages = <?php echo $max_images; ?>;
    const uploader = document.getElementById('multi-image-uploader');
    const fileInput = document.getElementById('file-input');
 
    const uploadAreaContainer = uploader.querySelector('.upload-area-container');
    const previewArea = uploader.querySelector('.preview-area');
 
    const sliderWrapper = uploader.querySelector('.slider-wrapper');
    const sliderContainer = uploader.querySelector('.slider-container');
    const prevBtn = uploader.querySelector('.slider-nav.prev');
    const nextBtn = uploader.querySelector('.slider-nav.next');
 
    fileInput.addEventListener('change', () => {
        handleFiles(fileInput.files);
    });
 
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        uploader.addEventListener(eventName, preventDefaults, false);
    });
 
    function preventDefaults(e) {
        e.preventDefault();
        e.stopPropagation();
    }
 
    ['dragenter', 'dragover'].forEach(eventName => {
        uploader.addEventListener(eventName, () => uploadAreaContainer.classList.add('dragover'), false);
    });
 
    ['dragleave', 'drop'].forEach(eventName => {
        uploader.addEventListener(eventName, () => uploadAreaContainer.classList.remove('dragover'), false);
    });
 
    uploader.addEventListener('drop', (e) => {
        handleFiles(e.dataTransfer.files);
    }, false);
 
    <?php
    // Pre-populate the files array if there are existing images from the database
    $initial_files_js = '[]';
    if (!empty($post['multi_image_paths'])) {
        $image_urls = explode(',', $post['multi_image_paths']);
        $featured_url = isset($post['post_image']) ? trim($post['post_image']) : '';
        $file_objects = array();
 
        // First pass: create all file objects
        foreach ($image_urls as $url) {
            $trimmed_url = trim($url);
            if (!empty($trimmed_url)) {
                $file_objects[] = array(
                    'url' => $trimmed_url,
                    'featured' => ($trimmed_url === $featured_url)
                );
            }
        }
 
        // Find the featured image index
        $featured_index = -1;
        foreach ($file_objects as $key => $obj) {
            if ($obj['featured']) {
                $featured_index = $key;
                break;
            }
        }
 
        // If a featured image is found and it's not already first, move it to the front
        if ($featured_index > 0) {
            $featured_image = array_splice($file_objects, $featured_index, 1);
            array_unshift($file_objects, $featured_image[0]);
        }
        // If no featured image was matched, make the first one featured by default
        else if ($featured_index === -1 && !empty($file_objects)) {
            $file_objects[0]['featured'] = true;
        }
 
        if (!empty($file_objects)) {
            $initial_files_js = json_encode($file_objects);
        }
    }
    ?>
    let files = <?php echo $initial_files_js; ?>;
    let currentSlide = 0;
 
    function handleFiles(selectedFiles) {
        const currentFileCount = files.length;
        if (currentFileCount >= maxImages) {
            swal({ type: 'error', title: 'Limit Reached', text: `You can upload a maximum of ${maxImages} images.` });
            return;
        }
 
        let filesToAdd = Array.from(selectedFiles);
        let spaceLeft = maxImages - currentFileCount;
 
        if (filesToAdd.length > spaceLeft) {
            swal({
                type: 'warning',
                title: 'Selection Exceeds Limit',
                text: `Your selection of ${filesToAdd.length} images is too large. You can only add ${spaceLeft} more. Please try again.`
            });
            fileInput.value = ''; // Clear the file input
            return; // Stop the function
        }
 
        let newFiles = filesToAdd.filter(validateType);
 
        if (newFiles.length === 0) {
            fileInput.value = '';
            return; // No valid files to process
        }
 
        let formData = new FormData();
        newFiles.forEach(file => {
            formData.append('userfile[]', file);
        });
        formData.append('action', 'uploadMultipleImages');
 
        const urlTarget = `${window.location.protocol}//${window.location.hostname}/wapi/widget?request_type=GET&header_type=json&widget_name=event-multiple-images-engine`;
 
        swal({
            title: 'Uploading...',
            text: 'Please wait while we upload your image(s).',
            allowOutsideClick: false,
            onBeforeOpen: () => {
                swal.showLoading();
            }
        });
 
        $.ajax({
            url: urlTarget,
            type: "POST",
            dataType: "json",
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        }).done(function(response) {
            if (response && response.status === 'success' && response.image_urls) {
                const was_empty = files.length === 0;
                response.image_urls.forEach((url, index) => {
                    // Make the first uploaded image featured ONLY if the list was empty before
                    const is_featured = was_empty && index === 0;
                    files.push({ url: url, featured: is_featured });
                });
                updatePreview(true);
                swal({
                    type: 'success',
                    title: 'Success!',
                    text: 'Images uploaded successfully!',
                    timer: 1500,
                    showConfirmButton: false
                });
            } else {
                 swal({
                    type: 'error',
                    title: 'Oops...',
                    text: response.message || 'Something went wrong during upload!'
                });
            }
        }).fail(function(error) {
            swal({
                type: 'error',
                title: 'Oops...',
                text: 'Something went wrong uploading your images!'
            });
        });
 
        fileInput.value = '';
    }
 
    function getFilesCount() {
        return sliderContainer.querySelectorAll('.slide').length;
    }
 
    function validateType(file) {
        const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
        const isValid = validTypes.includes(file.type);
        if (!isValid) {
            alert(`File ${file.name} is not a valid image type (JPEG, PNG, GIF, WEBP).`);
        }
        return isValid;
    }
 
    function updatePreview(isNewUpload = false) {
        if (files.length > 0) {
            uploadAreaContainer.style.display = 'none';
            previewArea.style.display = 'flex';
        } else {
            uploadAreaContainer.style.display = 'block';
            previewArea.style.display = 'none';
        }
 
        const addMoreArea = previewArea.querySelector('.upload-more-area');
        if (files.length >= maxImages) {
            addMoreArea.style.display = 'none';
        } else {
            addMoreArea.style.display = 'block';
        }
 
        const imageCounter = document.getElementById('image-counter');
        imageCounter.innerHTML = `<b>${files.length} / ${maxImages}</b> images`;
 
        sliderContainer.innerHTML = '';
        files.forEach((fileData, index) => {
            const slide = document.createElement('div');
            slide.classList.add('slide');
 
            const img = document.createElement('img');
            img.src = fileData.url;
 
            const removeBtn = document.createElement('button');
            removeBtn.type = 'button';
            removeBtn.className = 'remove-image btn btn-danger btn-xs';
            removeBtn.innerHTML = '&times;';
            removeBtn.title = 'Remove image';
            removeBtn.onclick = (e) => {
                e.stopPropagation();
                removeImage(index);
            };
 
            const featureWrapper = document.createElement('div');
            featureWrapper.className = 'feature-checkbox-wrapper';
 
            const featureCheckbox = document.createElement('input');
            featureCheckbox.type = 'checkbox';
            featureCheckbox.id = `feature-check-${index}`;
            featureCheckbox.checked = fileData.featured;
            featureCheckbox.style.cursor = 'pointer';
            featureCheckbox.onchange = () => {
                setFeaturedImage(index);
            };
 
            const featureLabel = document.createElement('label');
            featureLabel.htmlFor = `feature-check-${index}`;
            featureLabel.innerText = 'Featured';
 
            featureWrapper.appendChild(featureCheckbox);
            featureWrapper.appendChild(featureLabel);
 
            slide.appendChild(img);
            slide.appendChild(removeBtn);
            slide.appendChild(featureWrapper);
            sliderContainer.appendChild(slide);
        });
 
        if (isNewUpload) {
            currentSlide = files.length - 1;
        }
 
        updateSlider();
        updateHiddenInput();
    }
 
    function removeImage(index) {
        swal({
            title: 'Deleting...',
            text: 'Please wait while the image is being removed.',
            allowOutsideClick: false,
            onBeforeOpen: () => {
                swal.showLoading();
            }
        });
 
        // Simulate deletion delay
        setTimeout(() => {
            const was_featured = files[index].featured;
            files.splice(index, 1);
            if (currentSlide >= files.length && files.length > 0) {
                currentSlide = files.length - 1;
            } else if (files.length === 0) {
                currentSlide = 0;
            }
 
            // If the removed image was featured, make the new first image featured.
            if (was_featured && files.length > 0) {
                files[0].featured = true;
            }
 
            updatePreview();
            swal({
                type: 'success',
                title: 'Removed!',
                text: 'The image has been removed.',
                timer: 1500,
                showConfirmButton: false
            });
        }, 500);
    }
 
    function setFeaturedImage(selectedIndex) {
        // Move the selected image to the front of the array
        const [selectedImage] = files.splice(selectedIndex, 1);
        files.unshift(selectedImage);
 
        // Update featured status for all images
        files.forEach((file, index) => {
            file.featured = (index === 0);
        });
 
        // Go to the first slide and update the view
        currentSlide = 0;
        updatePreview();
    }
 
    function updateHiddenInput() {
        const allUrls = files.map(f => f.url).join(',');
        $('#multi_image_paths').val(allUrls);
 
        const featuredImage = files.find(f => f.featured);
        const featuredUrl = featuredImage ? featuredImage.url : '';
        $('#post_image').val(featuredUrl);
    }
 
    function updateSlider() {
        if (files.length === 0) {
            prevBtn.style.display = 'none';
            nextBtn.style.display = 'none';
            return;
        }
 
        const slideWidth = sliderWrapper.clientWidth;
        sliderContainer.style.transform = `translateX(-${currentSlide * slideWidth}px)`;
 
        if (files.length > 1) {
            prevBtn.style.display = 'block';
            nextBtn.style.display = 'block';
        } else {
            prevBtn.style.display = 'none';
            nextBtn.style.display = 'none';
        }
    }
 
    prevBtn.addEventListener('click', () => {
        currentSlide = (currentSlide > 0) ? currentSlide - 1 : files.length - 1;
        updateSlider();
    });
 
    nextBtn.addEventListener('click', () => {
        currentSlide = (currentSlide < files.length - 1) ? currentSlide + 1 : 0;
        updateSlider();
    });
 
    window.addEventListener('resize', updateSlider);
 
    if (files.length > 0) {
        updatePreview();
    }
});
</script>
  • Finally, click Save Changes.

Adding the second custom widget

This widget is the engine behind everything β€” it takes care of saving the images to the right folder after you upload them.

  • Visit managemydirectory and go to Toolbox > Widget Manager in the left sidebar.
  • Click the New Widget button.
  • Name the widget event-multiple-images-engine.
  • Copy and paste the code below to add all the functionality.
<?php
header('Content-Type: application/json');
 
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
$response = array(
    'status' => 'error',
    'message' => 'Invalid action.'
);
 
if ($action === 'uploadMultipleImages') {
 
    if (!empty($_FILES['userfile']['name'][0])) {
        $uploadFolder = 'images/events';
 
        // Create the directory if it does not exist
        if (!file_exists($uploadFolder)) {
            mkdir($uploadFolder, 0777, true);
        }
 
        $uploaded_urls = array();
        $files = reArrayFiles($_FILES['userfile']);
 
        foreach ($files as $file) {
            if ($file['error'] === UPLOAD_ERR_OK) {
                $temp_name = $file["tmp_name"];
 
                // Sanitize filename and create a unique name
                $nameSeed = rand(1, 1000000);
                $cleanName = strtolower(preg_replace('/[^a-zA-Z0-9-_\.]/', '', basename($file["name"])));
                $extension = pathinfo($cleanName, PATHINFO_EXTENSION);
                $baseName = pathinfo($cleanName, PATHINFO_FILENAME);
 
                $newName = $baseName . '-' . $nameSeed . '.' . $extension;
                $targetPath = $uploadFolder . '/' . $newName;
 
                if (move_uploaded_file($temp_name, $targetPath)) {
                    $uploaded_urls[] = '/' . $targetPath;
                }
            }
        }
 
        if (!empty($uploaded_urls)) {
            $response = array(
                'status' => 'success',
                'image_urls' => $uploaded_urls
            );
        } else {
            $response['message'] = 'No files were uploaded successfully.';
        }
 
    } else {
        $response['message'] = 'No files were sent.';
    }
}
 
echo json_encode($response);
 
function reArrayFiles(&$file_post) {
    $file_ary = array();
    $file_count = count($file_post['name']);
    $file_keys = array_keys($file_post);
 
    for ($i = 0; $i < $file_count; $i++) {
        foreach ($file_keys as $key) {
            $file_ary[$i][$key] = $file_post[$key][$i];
        }
    }
 
    return $file_ary;
}
 
?>
  • Finally, click Save Changes.

Creating the custom folder

I decided to use a custom folder instead of the default ones. Why? Because this way you can easily manage and view the images yourself.

  • Visit managemydirectory and go to Content > Media Manager.
  • Click the icon to add a new folder.
Media Manager
  • Name the folder events and click OK.
Media Manager
  • The folder will be there.
Media Manager

Adding a new column to the data_posts table

There are many ways to implement this in the database, but for the purpose of this tutorial I'll keep it simple. All the image paths will be saved in the same column as text, separated by commas.

  • Visit managemydirectory and go to Developer Hub > MySQL Database.
  • In the sidebar, click on the database that ends with directory.
Media Manager
  • Click on the SQL tab at the top.
Media Manager
  • Copy and paste the code below to add the new column multi_image_paths, then click GO.
ALTER TABLE `data_posts` ADD `multi_image_paths` TEXT NOT NULL;
Media Manager
  • You should see a success message like this:
Media Manager

Editing the Events Form

We need to replace the widget used in the form to manage the event post image.

  • Visit managemydirectory and go to Toolbox > Form Manager.
  • Search for Event Form.
  • Depending on whether you have already customized the form, click Edit. If it's still the default form, click Actions > Customize.
  • Find the default widget. It's usually the second field, with the field type Custom HTML and the Field Label set to [widget=Bootstrap Theme - Account - Feature Single Photo Upload].
  • We need to disable this widget. Click the icon and turn off all the display settings.
Media Manager
  • Now let's add a new field. Click Add Field +.
Media Manager
  • Set the field type to Custom HTML, the Field Label to [widget=event-multiple-images], and the Database Variable to event_multiple_images. All display settings should be off except for Input View.
Media Manager
  • Finally, click Save Changes.

Editing the Search Results Event Page

Now let's edit the code that displays the image on the search results page for events. The idea is to show a slider if there's more than one image, and also include a fallback for events that don't use the new implementation. This way, we cover both scenarios.

  • Visit managemydirectory and go to My Content > Edit Post Settings.
  • Search for the Events Feature and click Edit.
  • Click on the Search Results Design tab, then click Click to View/Edit Code.

Here we'll need to update the Page Header Code and Page Loop Code.

πŸ’‘

Note: I'd love to tell you exactly which lines to replace, but since these files can vary a lot (and there's often a lot of code), I hope you're working with the default setup to make this easier.

  • Code for Page Header Code:
[widget=Bootstrap Theme - Grid View Toggle]
<hr>
<style>
.loop-slider-wrapper {
    position: relative;
    overflow: hidden;
    background-color: #f8f8f8; /* Light background for consistency */
    width: 100%;
    aspect-ratio: 16 / 9; /* Enforce a consistent 16:9 aspect ratio */
}
.loop-slider-container {
    display: flex;
    transition: transform 0.5s ease-in-out;
    height: 100%; /* Make the container as tall as the wrapper */
}
.loop-slide {
    min-width: 100%;
    box-sizing: border-box;
    height: 100%;
}
.loop-slide a { /* The anchor tag needs to fill the slide */
    display: block;
    width: 100%;
    height: 100%;
}
.loop-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* This will fill the area, cropping if necessary */
    max-width: 100%;
}
.loop-slider-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background-color: rgba(0,0,0,0.5);
    color: white;
    border: none;
    cursor: pointer;
    padding: 0;
    z-index: 10;
    border-radius: 50%;
    height: 30px;
    width: 30px;
    line-height: 30px;
    text-align: center;
    font-size: 16px;
}
.loop-slider-nav.prev {
    left: 10px;
}
.loop-slider-nav.next {
    right: 10px;
}
</style>
<script>
// Use a flag to ensure this script runs only once, even if the file is included multiple times.
if (!window.eventsSliderInitialized) {
    window.eventsSliderInitialized = true;
    document.addEventListener('DOMContentLoaded', function () {
        const sliders = document.querySelectorAll('.events-loop-slider');
 
        sliders.forEach(slider => {
            const container = slider.querySelector('.loop-slider-container');
            const slides = slider.querySelectorAll('.loop-slide');
            const prevBtn = slider.querySelector('.loop-slider-nav.prev');
            const nextBtn = slider.querySelector('.loop-slider-nav.next');
            let currentSlide = 0;
            const totalSlides = slides.length;
 
            if (totalSlides <= 1) {
                if(prevBtn) prevBtn.style.display = 'none';
                if(nextBtn) nextBtn.style.display = 'none';
                return;
            }
 
            function updateSliderPosition() {
                const slideWidth = slider.querySelector('.loop-slider-wrapper').clientWidth;
                container.style.transform = `translateX(-${currentSlide * slideWidth}px)`;
            }
 
            prevBtn.addEventListener('click', () => {
                currentSlide = (currentSlide > 0) ? currentSlide - 1 : totalSlides - 1;
                updateSliderPosition();
            });
 
            nextBtn.addEventListener('click', () => {
                currentSlide = (currentSlide < totalSlides - 1) ? currentSlide + 1 : 0;
                updateSliderPosition();
            });
 
            // A single resize listener for all sliders for better performance
            window.addEventListener('resize', updateSliderPosition);
 
            // Set initial position
            updateSliderPosition();
        });
    });
}
</script>
<div class="grid-container">
  • Code for Page Loop Code:
<?php
$post = getMetaData("data_posts", $post['post_id'], $post, $w);
$subscription = getSubscription($user['subscription_id'],$w);
$post["label"] = $label;
$postFeaturedClass = ($post['sticky_post'] &&
    ($post['sticky_post_expiration_date'] == '0000-00-00' || $post['sticky_post_expiration_date'] >= date('Y-m-d')))
    ? ' featured-post featured-post-' . $dc['data_filename']
    : '';
?>
    <div class="search_result row-fluid member-level-<?php echo $user['subscription_id'] . $postFeaturedClass?>">
        <div class="grid_element">
 
            <div class="col-xs-8 col-sm-10 nolpad font-sm bmargin posted_meta_data">
            <span>
            %%%posted_on%%%
            <?php echo transformDate($post['post_live_date'],"QB")?>
            </span>
                <?php
                if ($subscription['searchable'] != 0) { ?>
                    <span class="posted-by inline-block">
                %%%by_label%%%
                <a class="bold" href="/<?php echo $user['filename']; ?>" title="%%%posted_by_membership_features%%% <?php echo $user['full_name']; ?>">
                    <?php echo $user['full_name']; ?>
                </a>
            </span>
                    <?php
                } ?>
            </div>
 
            <div class="col-xs-4 col-sm-2 norpad favorite_button">
                <?php
                $addonFavorites = getAddOnInfo("add_to_favorites","a8ad175dd81204563b3a9fc3ebcd5354");
                if (isset($addonFavorites['status']) && $addonFavorites['status'] === 'success') {
                    echo '<span class="postItem" data-userid="'.$post['user_id'].'" data-datatype="'.$post['data_type'].'" data-dataid="'.$post['data_id'].'" data-postid="'.$post['post_id'].'"></span>';
                    echo widget($addonFavorites['widget'],"",$w['website_id'],$w);
                }
                addonController::showWidget('post_comments','88a1bbf8d5d259c20c2d7f6c7651f672'); ?>
            </div>
 
            <div class="clearfix"></div>
 
            <?php
            // 1. Prepare image array from multi_image_paths or post_image
            $image_urls = array();
            if (!empty($post['multi_image_paths'])) {
                // Use the multi_image_paths field if available
                $image_urls = explode(',', $post['multi_image_paths']);
            } else if (!empty($post['post_image'])) {
                // Fallback to the single post_image
                $image_urls[] = $post['post_image'];
            }
            $has_image = !empty($image_urls);
            ?>
 
            <?php if ($has_image) { ?>
            <div class="img_section col-sm-4 nopad sm-bmargin">
 
                <?php
                // 2. PROMO BANNER (kept from original)
                if ($post['post_promo'] != "") { ?>
                    <div class="btn-sm bg-primary font-lg bold no-radius-bottom">
                        <?php
                        $fixedValue = websiteSettingsController::fixPriceValue(
                            $post['post_promo'],
                            brilliantDirectories::getCurrencySymbol(),
                            brilliantDirectories::getCurrencySuffix(),
                            brilliantDirectories::getCurrencyThousandsDivider()
                        );
                        if($fixedValue != ""){
                            echo displayPrice($fixedValue);
                        }else{
                            echo $post['post_promo'];
                        }
                        ?>
                        <div class="clearfix"></div>
                    </div>
                <?php } ?>
 
                <?php
                // 3. SLIDER FOR MULTIPLE IMAGES
                if (count($image_urls) > 1) {
                    $slider_id = "slider-" . $post['post_id'];
                    ?>
                    <div id="<?php echo $slider_id; ?>" class="events-loop-slider">
                        <div class="loop-slider-wrapper">
                            <div class="loop-slider-container">
                                <?php foreach ($image_urls as $img_url) { ?>
                                    <div class="loop-slide">
                                        <a title="<?php echo $post['post_title']; ?>" href="/<?php echo $post['post_filename']; ?>">
                                           <img class="search_result_image center-block" alt="<?php echo (!empty($post['post_alt'])?$post['post_alt']:$post['post_title'])?>" title="<?php echo $post['post_title']; ?>" src="<?php echo trim($img_url); ?>"/>
                                        </a>
                                    </div>
                                <?php } ?>
                            </div>
                        </div>
                        <button type="button" class="loop-slider-nav prev">&lt;</button>
                        <button type="button" class="loop-slider-nav next">&gt;</button>
                    </div>
                    <?php
                // 4. SINGLE IMAGE
                } else {
                    ?>
                    <a title="<?php echo $post['post_title']; ?>" href="/<?php echo $post['post_filename']; ?>">
                        <div class="alert-secondary btn-block text-center">
                            <img class="search_result_image center-block" alt="<?php echo (!empty($post['post_alt'])?$post['post_alt']:$post['post_title'])?>" title="<?php echo $post['post_title']; ?>" src="<?php echo trim($image_urls[0]); ?>"/>
                        </div>
                    </a>
                    <?php
                }
                ?>
            </div>
            <?php } ?>
 
            <div class="mid_section xs-nopad <?php if ($has_image) { ?>col-sm-8<?php } else { ?>col-sm-12<?php } ?> ">
 
                <a class="h3 bold bmargin center-block" title="<?php echo $post['post_title']; ?>" href="/<?php echo $post['post_filename']; ?>">
                    <?php echo $post['post_title']; ?>
                </a>
 
                <div class="clearfix"></div>
 
                <?php if ($post['post_start_date'] != "") {
                    //get the add-on
                    $recurringEventAddOn = getAddOnInfo("recurring_events", "e621b224abe9646e9c2cea1bcf80dbae");
                    if ( ( $dc['enable_recurring_events_addon'] == 1 || !isset($dc['enable_recurring_events_addon']) ) && isset($recurringEventAddOn['status']) && $recurringEventAddOn['status'] == 'success') {
                        echo widget($recurringEventAddOn['widget'], "", $w['website_id'], $w);
                    } else {
                        try{
                            $event = new event($post);
                        }catch (Exception $e) {
                            echo 'Caught exception: ',  $e->getMessage(), "\n";
                        }
                        ?>
                        <div class="post-location-snippet bmargin">
                    <span class="inline-block">
                        <i class="fa fa-calendar"></i>
                        <b><?php echo $event->startDateObject->format($w['default_date_format']); ?></b>
                        <?php
                        $post['start_time'] = preg_replace('/: /', ':', $post['start_time']);
                        echo str_replace(array('AM','PM'),array($label['time_am_label'],$label['time_pm_label']),$post['start_time']); ?>
                    </span>
                        </div>
                        <?php
                    }
                } if ($post['post_content'] != "") { ?>
                    <p class="bpad xs-nomargin xs-nopad">
                        <?php echo limitWords(preg_replace('#<[^>]+>#', ' ', $post['post_content']),115)?>...
                        <a class="inline-block bold" title="<?php echo $post['post_title']; ?>" href="/<?php echo $post['post_filename']; ?>">
                            %%%view_more_label%%%
                        </a>
                    </p>
                    <?php
                } ?>
 
                <div class="clearfix"></div>
 
                <div class="hidden-xs row-fluid bpad">
                    <a title="<?php echo $post['post_title']; ?>" class="btn btn-success col-sm-5 view-details rmargin" href="/<?php echo $post['post_filename']; ?>">
                        %%%results_view_details_link%%%
                    </a><?php addonController::showWidget('star_ratings_for_posts','609d748eaa051578e8ae2e3c8f848d9a');?>
                    <div class="clearfix"></div>
 
                </div>
 
                <div class="clearfix"></div>
 
                <?php
                if ($post['post_location'] != "" || $post['post_venue'] !="") { ?>
                    <div class="post-location-snippet tmargin">
                        <i class="fa fa-map-marker text-danger"></i>
                        <b><?php echo $post['post_venue']; ?></b>
                        <span class="inline-block hidden-xs font-sm">
					<?php echo $post['post_location']; ?>
				</span>
                    </div>
                    <?php
                } ?>
 
            </div>
        </div>
    </div>
    <div class="clearfix"></div>
    <hr>
<?php
if ($post['lat'] != '' && $post['lon'] != '') {
    $_ENV['post'] = $post;
    echo widget("Bootstrap Theme - Google Pins Locations", "", $w['website_id'], $w);
} ?>
  • Finally, click Save Changes.

Editing the Event Details Page

Let's add a carousel to the event details page to display multiple images. I've also added a validation, so if there's only one image, multiple images, or if the event is using the old image system, it will show the appropriate image (the one saved previously).

  • On the same page, click on the Detail Page Design tab, then click Click to View/Edit Code.
  • Copy and paste the code below to add all the functionality and design:
<?php
echo widget("Bootstrap Theme - Detail Page - Schema Markup - Event Post Type");
echo widget("Bootstrap Theme - Display - Posted By Snippet");
?>
 
<div id="post-content">
 
	<div class="row">
 
		<div class="col-md-12 vmargin">
 
			<h1 class="bold h2">
				<?php echo $post['post_title']; ?>
			</h1>
 
			<div class="clearfix"></div>
 
			<?php
			if ($post['post_location'] != "" || $post['post_venue'] !="") { ?>
			<i class="fa fa-map-marker text-danger"></i>
			<b><?php echo $post['post_venue']; ?></b>
			<span class="inline-block font-sm">
				<?php echo $post['post_location']; ?>
			</span>
			<?php
			} ?>
 
			<div class="clearfix"></div>
 
		</div>
 
	</div>
 
	<?php if ($post['post_promo'] != "") { ?>
	<div class="btn-sm fpad bg-primary no-radius-bottom">
		<span class="h4 nomargin bold">
			<?php
            $fixedValue = websiteSettingsController::fixPriceValue(
                $post['post_promo'],
                brilliantDirectories::getCurrencySymbol(),
                brilliantDirectories::getCurrencySuffix(),
                brilliantDirectories::getCurrencyThousandsDivider()
            );
            if($fixedValue != ""){
            	echo displayPrice($fixedValue);
            }else{
            	echo $post['post_promo'];
            }
			?>
		</span>
		<div class="clearfix"></div>
	</div>
	<?php
	} ?>
 
	<div class="well text-center nobmargin no-radius-bottom event-date-info">
 
		<?php
		//check for recurring events
		$recurringEventAddOn = getAddOnInfo("recurring_events","78775e2e310f6e64c751a601169dd413");
		if( ( $dc['enable_recurring_events_addon'] == 1 || !isset($dc['enable_recurring_events_addon']) ) && isset($recurringEventAddOn['status']) && $recurringEventAddOn['status'] == 'success'){
			echo widget($recurringEventAddOn['widget'],"",$w['website_id'],$w);
		} else { ?>
		<div class="row">
			<div class="col-sm-6 xs-bmargin">
				<?php
	if ($post['post_start_date'] != "") { ?>
				<b>
					%%%start_date_membership_feature_details%%%
				</b>
				<br>
				<?php echo transformDate($post['post_start_date'],"QBTIME");
										} ?>
			</div>
			<div class="col-sm-6">
				<?php
	if ($post['post_expire_date'] != "") { ?>
				<b>
					%%%end_date_membership_feature%%%
				</b>
				<br>
				<?php echo transformDate($post['post_expire_date'],"QBTIME");
										 } ?>
			</div>
		</div>
		<?php } ?>
 
	</div>
 
	<?php
	// 1. Prepare image array from multi_image_paths or post_image
	$image_urls = array();
	if (!empty($post['multi_image_paths'])) {
		$image_urls = explode(',', $post['multi_image_paths']);
	} else if (!empty($post['post_image'])) {
		$image_urls[] = $post['post_image'];
	}
 
	// 2. Check if we have any images
	if (!empty($image_urls)) {
		// 3. If only one image, display it simply
		if (count($image_urls) <= 1) {
	?>
		<div class="alert-secondary btn-block text-center img-rounded no-radius-top">
			<img class="center-block img-rounded no-radius-top" alt="<?php echo (!empty($post['post_alt'])?$post['post_alt']:$post['post_title'])?>" title="<?php echo $post['post_title']; ?>" src="<?php echo trim($image_urls[0]); ?>" />
			<div class="clearfix"></div>
		</div>
	<?php
		}
		// 4. If multiple images, implement the custom Swiper.js slider
		else {
			$gallery_main_id = 'event-swiper-main-' . $post['post_id'];
			$gallery_thumbs_id = 'event-swiper-thumbs-' . $post['post_id'];
	?>
			<!-- Swiper.js CDN links - loaded only when there are multiple images -->
			<link rel="stylesheet" href="https://unpkg.com/swiper@8/swiper-bundle.min.css" />
			<script src="https://unpkg.com/swiper@8/swiper-bundle.min.js"></script>
 
			<style>
				/* Styles for the main gallery */
				.event-gallery-main {
					width: 100%;
					aspect-ratio: 16 / 9;
					background-color: #f8f8f8;
				}
				.event-gallery-main .swiper-slide img {
					display: block;
					width: 100%;
					height: 100%;
					object-fit: cover;
				}
				.event-gallery-main .swiper-button-next,
				.event-gallery-main .swiper-button-prev {
					color: #ffffff;
					background-color: rgba(0, 0, 0, 0.8);
					border-radius: 3px;
					width: 40px;
					height: 40px;
					transition: background-color 0.3s ease;
					margin-top: -20px;
				}
				.event-gallery-main .swiper-button-next::after,
				.event-gallery-main .swiper-button-prev::after {
					font-size: 20px;
				}
				.event-gallery-main .swiper-button-next:hover,
				.event-gallery-main .swiper-button-prev:hover {
					background-color: #000000;
				}
 
				/* Styles for the thumbnail gallery */
				.event-gallery-thumbs {
					height: 80px;
					box-sizing: border-box;
					padding: 10px 0;
					position: relative; /* Required for arrow positioning */
				}
				.event-gallery-thumbs .swiper-wrapper {
					justify-content: center;
				}
				.event-gallery-thumbs .swiper-slide {
					width: 90px; /* Made thumbnails smaller */
					height: 100%;
					opacity: 0.5;
					transition: opacity 0.3s ease;
					cursor: pointer;
				}
				.event-gallery-thumbs .swiper-slide-thumb-active {
					opacity: 1;
				}
				.event-gallery-thumbs .swiper-slide img {
					display: block;
					width: 100%;
					height: 100%;
					object-fit: cover;
				}
 
				/* Styles for thumbnail navigation arrows */
				.event-gallery-thumbs .swiper-button-next,
				.event-gallery-thumbs .swiper-button-prev {
					top: 50%;
					width: 30px;
					height: 30px;
					margin-top: -15px;
					background-color: rgba(0,0,0,.5);
					color: #fff;
					border-radius: 50%;
				}
				.event-gallery-thumbs .swiper-button-next:hover,
				.event-gallery-thumbs .swiper-button-prev:hover {
					background-color: rgba(0,0,0,.8);
				}
				.event-gallery-thumbs .swiper-button-next::after,
				.event-gallery-thumbs .swiper-button-prev::after {
					font-size: 16px;
				}
			</style>
 
			<!-- Main Swiper -->
			<div id="<?php echo $gallery_main_id; ?>" style="margin-bottom:10px;" class="swiper event-gallery-main">
				<div class="swiper-wrapper">
					<?php foreach ($image_urls as $img_url) { ?>
						<div class="swiper-slide">
							<img src="<?php echo trim($img_url); ?>" />
						</div>
					<?php } ?>
				</div>
				<div class="swiper-button-next"></div>
				<div class="swiper-button-prev"></div>
			</div>
 
			<!-- Thumbs Swiper -->
			<div id="<?php echo $gallery_thumbs_id; ?>" class="swiper event-gallery-thumbs">
				<div class="swiper-wrapper">
					<?php foreach ($image_urls as $img_url) { ?>
						<div class="swiper-slide">
							<img src="<?php echo trim($img_url); ?>" />
						</div>
					<?php } ?>
				</div>
				<div class="swiper-button-next"></div>
				<div class="swiper-button-prev"></div>
			</div>
 
			<script>
				document.addEventListener('DOMContentLoaded', function () {
					const galleryThumbs = new Swiper('#<?php echo $gallery_thumbs_id; ?>', {
						spaceBetween: 10,
						slidesPerView: 'auto',
						freeMode: true,
						watchSlidesProgress: true,
						navigation: {
							nextEl: '#<?php echo $gallery_thumbs_id; ?> .swiper-button-next',
							prevEl: '#<?php echo $gallery_thumbs_id; ?> .swiper-button-prev',
						},
					});
					const galleryMain = new Swiper('#<?php echo $gallery_main_id; ?>', {
						loop: true,
						effect: 'fade',
						spaceBetween: 10,
						navigation: {
							nextEl: '#<?php echo $gallery_main_id; ?> .swiper-button-next',
							prevEl: '#<?php echo $gallery_main_id; ?> .swiper-button-prev',
						},
						thumbs: {
							swiper: galleryThumbs,
						},
					});
				});
			</script>
	<?php
		}
	}
	?>
 
	<div class="clearfix"></div>
 
	<div class="row">
		<?php
		if ($subscription['receive_messages'] != 1 && $user['active'] == 2) { ?>
		<div class="<?php if ($post['post_url']!="") { ?>col-sm-6<?php } else { ?>col-sm-12<?php } ?> vmargin">
			<a data-toggle="modal" data-target="#contactModal" class="btn btn-success btn-lg btn-block bold">
				%%%contact_member_label%%%
			</a>
			[widget=Bootstrap Theme - Contact Member Modal]
		</div>
		<?php } if ($post['post_url']!="") { ?>
		<div class="<?php if ($subscription['receive_messages'] != 1 && $user['active'] == 2) { ?>col-sm-6<?php } else { ?>col-sm-12<?php } ?> vmargin">
			<a class="btn btn-primary btn-lg btn-block bold" title="<?php echo $post['post_title']; ?>" <?php if ($subscription['nofollow_links'] =="1") { ?>rel="nofollow"<?php } ?> href="<?php if (strpos($post['post_url'],'http') !== FALSE) { ?><?php echo $post['post_url']?><?php } else { ?>//<?php echo $post['post_url'];?><?php } ?>" target="_blank">
				%%%more_details_membership_feature%%%
			</a>
		</div>
		<?php
		} ?>
		<div class="clearfix"></div>
	</div>
 
	<div class="well">
 
		<?php if ($post['post_content_clean'] !="") {
			echo '<div class="the-post-description">' . $post['post_content_clean'] .'<div class="clearfix"></div></div>';
		} ?>
 
		<div class="clearfix"></div>
 
		<?php if ($tags != "") {
			if ($post['post_content_clean'] !="") { echo '<hr class=tmargin>'; }?>
		<div class="tags">
			<?php echo $tags;?>
		</div>
		<?php
		} ?>
 
		<div class="clearfix"></div>
	</div>
	<div class="clearfix"></div>
</div>
  • Finally, click Save Changes.

OPTIONAL

If you use the streaming events section on the homepage, we need to edit the widget so it works with the new image implementation. Here I didn't add the slider β€” it simply shows the featured image or the default image that events had before this implementation.

  • Visit managemydirectory and go to Toolbox > Widget Manager in the left sidebar.
  • Search for the widget Bootstrap Theme - Display - Recent Events.
  • Click on Actions > Customize.
  • Copy and paste the code below to add all the functionality and design:
<?php
/*
 * Widget Name:  Bootstrap Theme - Display - Recent Events
 * Short Description: Enables the Streaming of Particular Membership Feature Posts.
 */
global $postExtra,$post,$postLocation,$sqlWhereParameters,$dc;
 
$eventFeatureUsersMeta = bd_controller::users_meta(WEBSITE_DB)->get('1','is_event_feature');
 
if($eventFeatureUsersMeta !== false){//we try to get is_event_feature = 1
    $eventFeatureUsersMeta = (is_array($eventFeatureUsersMeta))?$eventFeatureUsersMeta[0]:$eventFeatureUsersMeta;
    $dc = bd_controller::data_categories(WEBSITE_DB)->get($eventFeatureUsersMeta->database_id,'data_id');
    bd_controller::data_categories(WEBSITE_DB)->loadProperties($dc);
    $dc = (array)clone bd_controller::data_categories(WEBSITE_DB);
}else{//we try to get form_name = event_fields
    $dataCategoriesResult = bd_controller::data_categories(WEBSITE_DB)->get('event_fields','form_name');
    if($dataCategoriesResult !== false){
        $dc = (is_array($dataCategoriesResult))?$dataCategoriesResult[0]:$dataCategoriesResult;
        bd_controller::data_categories(WEBSITE_DB)->loadProperties($dc);
        $dc = (array)clone bd_controller::data_categories(WEBSITE_DB);
    }
}
 
$featureUrl = $wa['custom_192']; // Use to Set the name of the Membership Feature this Streaming Widget will use. Can be controlled in the Design Settings.
$maxItems = $wa['custom_169']; // Maximum amount of streaming items shown. Default is 4. Can be controlled in the Design Settings.
$hidePostsWithoutPhotos = $wa['custom_181']; // This will show or hide posts if they do not have photos. Default is true. Can be controlled in the Design Settings.
 
if ($wa['post_events_hideEmptyStream'] == "1" || $wa['post_events_hideEmptyStream'] == "") {
    $hideEmptyStream = true; // This hides the feature if there are no posts to show on it. Default is true. Can be controlled in the Design Settings.
} else {
    $hideEmptyStream = false;
}
if ($wa['post_events_onlyActiveMembers'] == "1" || $wa['post_events_onlyActiveMembers'] == ""){
    $onlyActiveMembers = true; // Show or Hide results from Active Members Only. Default is true.
} else {
    $onlyActiveMembers = false;
}
if ($wa['post_events_searchablePostsOnly'] == "0" || $wa['post_events_searchablePostsOnly'] == ""){
    $searchablePostsOnly = true; // Show posts even if they are not searchable. Useful for Membership Levels that do not want to show in the search results but want to show their posts in the streaming widgets, similar to how an admin account would work.
} else {
    $searchablePostsOnly = false;
}
if($wa['streaming_view_more_class'] != ''){
    $viewAllClass = $wa['streaming_view_more_class'];
} else {
    $viewAllClass = 'btn-info';
}
 
if($wa['streaming_read_more_class'] != ''){
    $readMoreClass = $wa['streaming_read_more_class'];
} else {
    $readMoreClass = 'btn-success';
}
$sortingOrder = 'dp.post_start_date ASC'; // Sorting Order in which results will be shown. Values supported are DESC (Descending) & ASC (Ascending). Default is ASC.
 
//order logic for recurring events
$isRamdon               = false;
$recurringEventsOrder   = 'default';
 
if(isset($wa['custom_285'])){
    $sortingOrder   = ($wa['custom_285'] != 'RAND()')?"dp.post_start_date ".$wa['custom_285']:$wa['custom_285'];
 
    switch ($wa['custom_285']) {
        case 'RAND()':
            $isRamdon = true;
            break;
 
        case 'ASC':
            $recurringEventsOrder = 'publish-asc';
            $sortingOrder = "dp.post_live_date ASC";
            break;
 
        case 'DESC':
            $recurringEventsOrder = 'publish-desc';
            $sortingOrder = "dp.post_live_date DESC";
            break;
 
        case 'date-start-asc':
            $recurringEventsOrder = 'date-start-asc';
            $sortingOrder = "dp.post_start_date ASC";
            break;
 
        case 'date-start-desc':
            $recurringEventsOrder = 'date-start-desc';
            $sortingOrder = "dp.post_start_date DESC";
            break;
 
        case "date-end-desc":
            $recurringEventsOrder = $wa['custom_285'];
            $sortingOrder = "dp.post_expire_date DESC";
            break;
        case "date-end-asc":
            $recurringEventsOrder = $wa['custom_285'];
            $sortingOrder = "dp.post_expire_date ASC";
            break;
 
        default:
            $recurringEventsOrder = $wa['custom_285'];
            $sortingOrder = $wa['custom_285'];
            break;
    }
 
    if(!addonController::isAddonActive('recurring_events')){
 
        if (strpos($sortingOrder,"_date ASC") !== false){
            $sortingOrder = str_replace("_date ASC","_date DESC", $sortingOrder);
        } else {
            $sortingOrder = str_replace("_date DESC","_date ASC", $sortingOrder);
        }
    }
}
$onlySubscriptionIds = $wa['post_events_onlySubscriptionIds'];
$recurringEventAddOnSecurityWidget = getAddOnInfo("recurring_events","7e166d959bd900cb0d9df212166e1f4d");
 
if (empty($featureUrl)) {
    $featureUrl = 'events';
}
 
if (empty($maxItems)) {
    $maxItems = 4;
}
 
if(is_numeric($featureUrl)){
    $where = "data_id = ".$featureUrl;
}else{
    $where = "data_filename LIKE '".$featureUrl."'";
}
 
if ($wa['custom_305'] == "2"){
    $columnWidth = "col-md-6";
} else if ($wa['custom_305'] == "1") {
    $columnWidth = "col-md-4";
} else {
    $columnWidth = "col-md-3";
}
 
// Query that grabs information from the Membership Feature selected
$membershipFeaturesSQL = mysql($w['database'], "SELECT
        data_id, data_filename
    FROM
        data_categories
    WHERE
        ".$where."
    AND
        data_active = '1'
    ORDER BY
        data_id
    LIMIT
        1");
$features = mysql_fetch_assoc($membershipFeaturesSQL);
 
$sqlSelectParameters = array(
    "dp.post_id",
    "dp.post_title",
    "dp.post_content",
    "dp.post_image",
    "dp.post_filename",
    "dp.post_updated",
    "dp.post_start_date",
    "dp.post_expire_date",
    "dp.user_id",
    "CASE WHEN length(dp.post_expire_date) = 8 THEN CONCAT(dp.post_expire_date, '000000') ELSE dp.post_expire_date END AS expire_date",
    "CASE WHEN length(dp.post_start_date) = 8 THEN CONCAT(dp.post_start_date, '000000') ELSE dp.post_start_date END AS start_date"
);
 
if( ($dc['form_name'] == "event_fields" || $dc['is_event_feature'] == 1) && ( ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1) || !isset($dc['enable_recurring_events_addon']) ) && (isset($recurringEventAddOnSecurityWidget['status']) && $recurringEventAddOnSecurityWidget['status'] == 'success' && ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1) ) ){
    $sqlSelectParameters[] = "IF(dp.post_expire_date='',IF(dp.recurring_type > 0,'',dp.post_start_date),dp.post_expire_date) as post_expire_date_having";
}else{
    $sqlSelectParameters[] = "IF(dp.post_expire_date='',dp.post_start_date,dp.post_expire_date) as post_expire_date_having";
}
 
if ($wa['post_events_onePostperMember'] == "1") {
    if ($wa['custom_285'] == "" || $wa['custom_285'] == "DESC"){
        $tempTableOrder = $sortingOrder.", post_live_date DESC";
    } else if ($wa['custom_285'] == "ASC"){
        $tempTableOrder = $sortingOrder.", post_live_date ASC";
    } else {
        $tempTableOrder = "RAND()";
    }
    mysql(brilliantDirectories::getDatabaseConfiguration('database'), "CREATE TEMPORARY TABLE temp_data_posts SELECT * FROM data_posts as dp WHERE dp.data_id = '".$features['data_id']."' AND (post_expire_date >= NOW() OR post_expire_date = '') ORDER BY ".$tempTableOrder."");
    $sqlTablesParameters = array(
        "`temp_data_posts` AS dp",
        "`users_data` AS ud",
        "`subscription_types` AS st"
    );
} else {
    $sqlTablesParameters = array(
        "`data_posts` AS dp",
        "`users_data` AS ud",
        "`subscription_types` AS st"
    );
}
$sqlWhereParameters = array(
    "dp.user_id = ud.user_id",
    "ud.subscription_id = st.subscription_id",
    "dp.data_id = '".$features['data_id']."'",
    "dp.post_title != ''",
    "dp.post_status = '1'"
);
if (!empty($onlySubscriptionIds) && $onlySubscriptionIds != " ") {
    array_push($sqlWhereParameters,"ud.subscription_id IN (".$onlySubscriptionIds.")");
}
 
$sqlGroupByParameters = array();
if ($wa['post_events_onePostperMember'] == "1") {
    array_push($sqlGroupByParameters,"user_id");
}
 
if(!isset($dc['show_expire_post']) || $dc['show_expire_post'] == 0){
    $sqlHavingParameters = array(
        "((post_expire_date_having >= '".date('YmdHis')."' OR post_expire_date_having >= '".date('Ymd')."' OR post_expire_date_having = '') AND start_date > 0)"
    );
}
 
$sqlOrderByParameters = array(
    $sortingOrder
);
$sqlLimitParameters = array(
    $maxItems
);
 
global $finalDate,$finalDate2;
 
$dateObjectNow = createDateTimeObject();
 
//we get today date
$finalDate      = $dateObjectNow->format('Ymd');
//by default we need to get maximun end date (end of the days? +1000 years)
$in1000yearsTimeStamp = $dateObjectNow->getTimestamp()+31536000000;
 
$dateObjectNow->setTimestamp($in1000yearsTimeStamp);
 
$finalDate2 = $dateObjectNow->format('Ymd');
 
if(!isset($dc['show_expire_post']) || $dc['show_expire_post'] == 0){
    if( ($dc['form_name'] == "event_fields" || $dc['is_event_feature'] == 1) && ( ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1) || !isset($dc['enable_recurring_events_addon']) ) && (isset($recurringEventAddOnSecurityWidget['status']) && $recurringEventAddOnSecurityWidget['status'] == 'success' && ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1) ) ){
 
        $sqlSelectParameters[] = "dp.recurring_type";
 
        //we removed the limit
        $sqlLimitParameters = array();
 
        //we get the security widget to add new where parameters
        $recurringEventQueryAddOn = getAddOnInfo("recurring_events","dc8a4cdcf23bb242c347c7e6bf121db4");
 
        echo widget($recurringEventQueryAddOn['widget'],"",$w['website_id'],$w);
 
    }else{
        $sqlWhereParameters[] = "(dp.post_start_date <= '".trim($finalDate)."000000' OR (dp.post_start_date <='".trim($finalDate)."000000' AND dp.post_expire_date <= '".trim($finalDate2)."240000') OR dp.post_start_date <= '".trim($finalDate2)."240000')";
    }
}
 
if ($onlyActiveMembers) {
    $sqlWhereParameters[] = "ud.active = '2'";
}
if ($hidePostsWithoutPhotos) {
    $sqlWhereParameters[] = "dp.post_image != ''";
}
if ($searchablePostsOnly == true) {
    $sqlWhereParameters[] = "st.searchable = '1'";
}
$sqlWhereParameters[] = "st.data_settings LIKE '%".$features[data_id]."%'";
 
$sql = "";
/* -------------------------- Code That Constructs the SQL statement ------------------------------------- */
if (count($sqlSelectParameters) > 0) {
    $sql .= "SELECT ";
    $sql .= implode(", ",$sqlSelectParameters);
}
if (count($sqlTablesParameters) > 0) {
    $sql .= " FROM ";
    $sql .= implode(", ",$sqlTablesParameters);
}
if (count($sqlWhereParameters) > 0) {
    $sql .= " WHERE ";
    $sql .= implode(" AND ",$sqlWhereParameters);
}
if (count($sqlGroupByParameters) > 0) {
    $sql .= " GROUP BY ";
    $sql .= implode(", ",$sqlGroupByParameters);
}
if (count($sqlHavingParameters) > 0) {
    $sql .= " HAVING ";
    $sql .= implode(" AND ",$sqlHavingParameters);
}
if (count($sqlOrderByParameters) > 0) {
    $sql .= " ORDER BY ";
    $sql .= implode(", ",$sqlOrderByParameters);
}
if (count($sqlLimitParameters) > 0) {
    $sql .= " LIMIT ";
    $sql .= implode(", ",$sqlLimitParameters);
}
/* -------------------------- END Code That Constructs the SQL statement ------------------------------------- */
 
if ($_GET['devmode']) {
    echo $sql;
}
$featureResults = mysql($w['database'], $sql);
$featureNum = mysql_num_rows($featureResults);
$showFeature = true;
 
if ($hideEmptyStream == true) {
 
    if ($featureNum > 0) {
        $showFeature = true;
    } else {
        $showFeature = false;
    }
}
 
$postArray = array();
$objectsArray = array();
 
if ($wa['post_events_onePostperMember'] == "1") {
    mysql(brilliantDirectories::getDatabaseConfiguration('database'), "DROP TABLE temp_data_posts");
}
 
//if we are doing events and we have recurring events
if(($dc['form_name'] == "event_fields" || $dc['is_event_feature'] == 1) && ( (isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1) || !isset($dc['enable_recurring_events_addon']) ) && (isset($recurringEventAddOnSecurityWidget['status']) && $recurringEventAddOnSecurityWidget['status'] == 'success' && ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 1)) ){
 
    $indexArray = array();
 
    //pre index the mysql result set to shuffle it if need it
    while ($post = mysql_fetch_assoc($featureResults)) {
        $indexArray[] = $post;
    }
 
    //if the order is ramdon we shuffle it
    if($isRamdon === true){
        shuffle($indexArray);
    }
 
 
    foreach ($indexArray as $post){
        $post               = getMetaData("data_posts",$post['post_id'],$post,$w);
        $event              = new event($post);
        $isEventDateInRange = $event->isEventDateInRange($finalDate,$finalDate2);
        $isEventTimeExpire  = $event->isEventTimeExpire();
        $currentTimeStamp   = $event->currentDateObject->getTimestamp();
        $startTimeStamp     = $event->closestDate->getTimestamp();
 
        //this for events in date range and the event hasnt start
        if($isEventDateInRange && $currentTimeStamp < $startTimeStamp){
            $objectsArray[] = $event;
        }else if($isEventDateInRange && $currentTimeStamp >= $startTimeStamp && $isEventTimeExpire === false){//this is for events in date range and the event already started and the time hasnt expired
            $objectsArray[] = $event;
        }else{ //debug purspose
            //debugDev::output($event);
            if($_GET['devmode'] == 1){
                echo $post['post_title'].' is expired<br>';
            }
        }
    }
 
    //is not ramdon we order it by the order selected.
    if($isRamdon === false){
 
        event::sortEvents($objectsArray,$recurringEventsOrder);
    }
 
 
    require_once($w['flocation'].'/dev_refactor_oop_3.0/classes/paginator.php');
    $paginatorObject = new paginator($maxItems,1,$objectsArray);
 
    //we re index the post array with the new order
 
    $objectsArray = array_slice($objectsArray, $paginatorObject->getStart(),$paginatorObject->getRecordsPerPage());
 
    foreach ($objectsArray as $event) {
        $postArray[] = $event->post;
    }
 
}else{
    while ($post = mysql_fetch_assoc($featureResults)) {
        $post = getMetaData("data_posts", $post['post_id'], $post, $w);
        $postArray[] = $post;
    }
}
 
$titleStyleColor = "";
if($wa['recent_events_tColor'] != ""){
    $titleStyleColor = 'style="color: '.$wa['recent_events_tColor'].'"';
}
 
if ($showFeature == true) { ?>
    <div class="clearfix"></div>
    <div class="content-container">
        <div class="clearfix"></div>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <?php if ($wa['events_show_view_all'] != '0'){ ?>
                        <a href="/<?php echo $features['data_filename'];?>" class="view-all-btn-desktop hidden-xs btn <?php echo $viewAllClass ?>">
                            %%%view_all_label%%%
                        </a>
                    <?php } ?>
                    <h2 class="nomargin sm-text-center streaming-title" <?php echo $titleStyleColor ?>>
                        <?php echo $wa['custom_123'];?>
                    </h2>
                    <hr>
                </div>
				<div class="clearfix"></div>
                <div class="row">
                    <div class="col-md-12 slickEvents">
                        <?php
                        foreach ($postArray as $post ) {
 
                            //if end date is empty is one time event
                            if(empty($post['post_expire_date'])){
                                $post['post_expire_date'] = $post['post_start_date'];
                            }
 
                            if(strlen((string)$post['post_expire_date']) == 8){
                                $post['post_expire_date'] .= "000000";
                            }
 
                            if(strlen((string)$post['post_start_date']) == 8){
                                $post['post_start_date'] .= "000000";
                            }
 
                            //we now check if the event expired
                            if(
                                ($dc['form_name'] == "event_fields" || $dc['is_event_feature'] == 1) &&
                                ( ( isset($dc['enable_recurring_events_addon']) && $dc['enable_recurring_events_addon'] == 0 ) || addonController::isAddonActive('recurring_events') !== true) &&
                                $post['post_expire_date'] <= date('YmdHis') && (!isset($dc['show_expire_post']) || $dc['show_expire_post'] == 0)
                            ){
                                if($_GET['devmode'] == 1){
                                    echo $post['post_title'].' is expired and theres not recurring event addon active<br>';
                                }
                                continue;
                            }
 
                            $postExtra = getMetaData("data_posts",$post['post_id'],$post,$w);
                            foreach ($post as $key => $value) {
                                $post[$key] = stripslashes($value);
                            }
                            $post['post_title'] = stripslashes($post['post_title']);
                            if (strlen($post['post_title']) > 40 ) {
                                $postTitle = limitName($post['post_title'],40);
                            } else {
                                $postTitle = $post['post_title'];
                            }
 
                            if (isset($postExtra['post_location']) && strlen($postExtra['post_location']) > 50 ) {
                                $postLocation = limitName($postExtra['post_location'],50);
                            } else if(isset($postExtra['post_location'])){
                                $postLocation = $postExtra['post_location'];
                            }else{
                                $postLocation = "";
                            }
 
                            if ($post['post_image'] != "") {
                                $thumbnailImage = $post['post_image'];
                                $postimage = "background-image:url(' ".$thumbnailImage." ')";
                            } else {
                                if($w['default_post_image'] != ''){
                                    $thumbnailImage = $w['default_post_image'];
                                    $postimage = "background-image:url(' ".$thumbnailImage." ')";
                                } else {
                                    $thumbnailImage = "/images/image-placeholder.jpg";
                                    $postimage = "background-image:url(' ".$thumbnailImage." ')";
                                }
                            } ?>
                            <div class="col-sm-6 <?php echo $columnWidth; ?> text-center bmargin">
                            <?php if (!empty($w['lazy_load_images'])) { ?>
                                <div class="pic lazyloader" data-src="<?php echo $thumbnailImage; ?>">
                            <?php } else { ?>
                                <div class="pic" style="<?php echo $postimage; ?>">
                            <?php } ?>
                                    <span class="pic-caption bottom-to-top" onclick>
                                        <h3 class="pic-title">
                                            <?php echo $postTitle;?>
                                        </h3>
                                        <?php
                                        if(($dc['form_name'] == "event_fields" || $dc['is_event_feature'] == 1) && ( $dc['enable_recurring_events_addon'] == 1 || !isset($dc['enable_recurring_events_addon']) ) && (isset($recurringEventAddOnSecurityWidget['status']) && $recurringEventAddOnSecurityWidget['status'] == 'success' && $dc['enable_recurring_events_addon'] == 1)){
                                            echo widget($recurringEventAddOnSecurityWidget['widget'],"",$w['website_id'],$w);
                                        }else{
                                            ?>
                                            <p>
                                            %%%start_date_membership_feature_details%%%:
                                            <?php echo transformDate($post['post_start_date'],"QB");?>
                                            <br>
                                            <?php
                                            if ($postExtra['post_location'] != "") {
                                                echo '<i class="fa fa-map-marker"></i> '.$postLocation;
                                            } ?>
                                        </p>
                                        <?php }?>
                                        <a href="/<?php echo $post['post_filename'];?>" class="btn <?php echo $readMoreClass?> fpad-lg vpad view-more">
                                            %%%results_view_details_link%%%
                                        </a>
                                    </span>
                                    <a aria-label="<?php echo strip_tags($Label['results_view_details_link']);?>" href="/<?php echo $post['post_filename'];?>" class="homepage-link-element <?php if ($wa['streaming_info_display'] == "on_hover") { ?>hidden-xs<?php } ?>"></a>
                                </div>
                            </div>
                        <?php } ?>
                    </div>
                </div>
                <div class="clearfix"></div>
                <?php if ($wa['events_show_view_all'] != '0'){ ?>
                    <div class="col-md-6">
                        <a href="<?php echo $features['data_filename'];?>" class="btn btn-lg <?php echo $viewAllClass ?> btn-block visible-xs-block">%%%view_all_label%%%</a>
                    </div>
                    <div class="clearfix"></div>
                <?php } ?>
            </div>
        </div>
    </div>
    <?php
    global $featureSliderEnabled, $featureMaxPerRow, $featureSliderClass, $postsCount;
    $postsCount = $featureNum;
    $featureSliderEnabled = $wa['events_posts_slider'];
    $featureMaxPerRow = $wa['custom_305'];
    $featureSliderClass = '.slickEvents';
    addonController::showWidget('post_carousel_slider','1a19675a36d28232077972bbdb6bb7fe');
} ?>
  • Finally, click Save Changes.

QA

Wow, that was a lot of work so far! Now let's get to the fun part and make sure everything is working perfectly.

Checklist:

  • βœ… Upload images in a new post.
  • βœ… Upload images in an existing post.
  • βœ… Check the search results page.
  • βœ… Check the detail page for a new post, an old post with multiple images, and an old post without new images.
  • βœ… Check the streaming events on the homepage.
  • βœ… Verify that you can see the images on the page where users manage their events.
  • βœ… Check managemydirectory where you manage your posts.
  • βœ… Check how the event looks when you share it on social media using OpenGraph

CONCLUSION

Congratulations! πŸŽ‰ You've now implemented multiple images for your events, giving your directory a more professional look and helping your members showcase their events in a better way.

I hope this has been helpful for both you and your users. If you run into any issues, feel free to let me know in the comments of this tutorial on my Facebook page β€” I'll be adding a comment section here soon.

I truly hope you enjoyed this tutorial as much as I enjoyed creating it. I put a lot of heart into it, and I'd love it if you considered signing up! πŸ™Œ

Brilliant Directories iconFacebook iconLinkedin IconYoutube Icon

Β© 2025 Alex Cruz - All rights reserved.