События Discourse

В консоли ошибок не появляется. Но я записал видео для справки.
Это происходит в Chrome, Firefox и Safari. В мобильном Safari этого не происходит.

1 лайк

Твоя ссылка на видео не работает!

Извините, я действительно не всё понял. Вы правы: при клике на дату в календаре открывается модальное окно создания события, но с кнопкой «Вы хотите отказаться?». Да, действительно, есть небольшая ошибка :sweat_smile:

2 лайка

К вашему сведению, с моей стороны я добавил кнопку «+ Больше инфо» для пользователей, которые не думают кликать на заголовок, чтобы получить доступ к теме. Я использовал идею @nathank с его кнопкой «Добавить в календарь».

Разместите скрипт в компоненте темы в разделе общего /head

<script>
(() => {
    const eventInfoSelector = ".event-header .event-info";
    const eventCardSelector = ".fc-popover, .event-preview, .discourse-post-event";
    // Чистый CSS, адаптированный для тем Discourse (Светлая/Тёмная)
    const style = document.createElement("style");
    style.innerHTML = `
    .custom-topic-info-btn {
        display: flex !important;
        align-items: center;
        justify-content: center;
        width: 40%; /* Занимает всю ширину для аккуратного отображения под заголовком */
        box-sizing: border-box;
        margin: 12px 0 6px 0;
        padding: 10px 16px;
        
        /* Использование нативных переменных Discourse */
        background-color: var(--tertiary) !important; /* Акцентный цвет (например, синий) */
        color: var(--secondary) !important;          /* Контрастный цвет текста */
        
        border-radius: 8px;
        font-weight: 700;
        text-decoration: none !important;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
        transition: background-color 0.2s ease, transform 0.1s ease;
    }
    /* Эффект при наведении */
    .custom-topic-info-btn:hover {
        background-color: var(--tertiary-hover) !important;
        color: var(--secondary) !important;
        transform: translateY(-1px);
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
    }
    /* Эффект при клике */
    .custom-topic-info-btn:active {
        transform: translateY(0);
    }
    /* Стиль SVG-иконки */
    .custom-topic-info-btn .btn-icon-svg {
        margin-right: 8px;
        width: 16px;
        height: 16px;
        fill: currentColor;
    }
    `;
    document.head.appendChild(style);
    const findTopicLink = (container) => {
        if (!container) return null;
        const links = [...container.querySelectorAll('a[href*="/t/"]')];
        if (links.length) return links[0].href;
        return null;
    };
    const createTopicInfoButton = () => {
        const eventInfo = document.querySelector(eventInfoSelector);
        if (!eventInfo || eventInfo.querySelector(".custom-topic-info-btn")) return;
        const card = eventInfo.closest(eventCardSelector) || document;
        const topicUrl = findTopicLink(card);
        if (!topicUrl) return;
        const button = document.createElement("a");
        button.className = "btn custom-topic-info-btn";
        button.title = "Посмотреть полную тему";
        button.href = topicUrl;
        
        // Добавление гармоничной SVG-иконки «Инфо»
        button.innerHTML = `
            <svg class="btn-icon-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
                <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V240h-24c-13.3 0-24-10.7-24-24s10.7-24 24-24h40c13.3 0 24 10.7 24 24v120h24c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144a32 32 0 1 1 0-64 32 32 0 1 1 0 64z"/>
            </svg>
            <span class="d-button-label">Ещё инфо</span>
        `;
        if (eventInfo.firstElementChild) {
            eventInfo.insertBefore(button, eventInfo.firstElementChild.nextSibling);
        } else {
            eventInfo.appendChild(button);
        }
    };
    const observer = new MutationObserver(() => createTopicInfoButton());
    observer.observe(document.body, { childList: true, subtree: true });
    createTopicInfoButton();
})();
</script>

Вот предварительный просмотр

или альтернативный вариант

этот код

<script>
(() => {
    const eventCardSelector = ".fc-popover, .event-preview, .discourse-post-event";

    // CSS для чистого и современного отображения
    const style = document.createElement("style");
    style.innerHTML = `
    .custom-topic-info-wrapper {
        margin-top: 16px;
        padding-top: 16px;
        border-top: 1px solid var(--primary-low); /* Тонкая разделительная линия */
        width: 100%;
        display: flex;
        justify-content: center;
    }

    .custom-topic-info-btn {
        display: flex !important;
        align-items: center;
        justify-content: center;
        width: 100%;
        box-sizing: border-box;
        padding: 8px 16px;
        
        /* Элегантный стиль Outline */
        background-color: transparent !important;
        color: var(--tertiary) !important;
        border: 1px solid var(--tertiary) !important;
        
        border-radius: 6px;
        font-weight: 600;
        text-decoration: none !important;
        transition: all 0.2s ease;
        cursor: pointer;
    }

    /* Эффект при наведении: кнопка заполняется цветом */
    .custom-topic-info-btn:hover {
        background-color: var(--tertiary) !important;
        color: var(--secondary) !important;
        border-color: var(--tertiary) !important;
    }

    /* Выравнивание иконки информации */
    .custom-topic-info-btn .btn-icon-svg {
        margin-right: 8px;
        width: 16px;
        height: 16px;
        fill: currentColor;
    }
    `;
    document.head.appendChild(style);

    const findTopicLink = (container) => {
        if (!container) return null;
        const links = [...container.querySelectorAll('a[href*="/t/"]')];
        if (links.length) return links[0].href;
        return null;
    };

    const createTopicInfoButton = () => {
        const cards = document.querySelectorAll(eventCardSelector);
        
        cards.forEach(card => {
            if (card.querySelector(".custom-topic-info-btn")) return;

            const topicUrl = findTopicLink(card);
            if (!topicUrl) return;

            const wrapper = document.createElement("div");
            wrapper.className = "custom-topic-info-wrapper";

            const button = document.createElement("a");
            button.className = "btn custom-topic-info-btn";
            button.title = "Просмотреть полную тему";
            button.href = topicUrl;
            
            // Иконка «i» Информация + новый текст с действием
            button.innerHTML = `
                <svg class="btn-icon-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
                    <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V240h-24c-13.3 0-24-10.7-24-24s10.7-24 24-24h40c13.3 0 24 10.7 24 24v120h24c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144a32 32 0 1 1 0-64 32 32 0 1 1 0 64z"/>
                </svg>
                <span class="d-button-label">Просмотреть тему</span>
            `;

            wrapper.appendChild(button);

            // Размещение над опциями ответа (Участвует и т.д.)
            const actionsContainer = card.querySelector(".status-and-options, .event-actions");
            const infoContainer = card.querySelector(".event-info");
            
            if (actionsContainer && actionsContainer.parentNode) {
                actionsContainer.parentNode.insertBefore(wrapper, actionsContainer);
            } else if (infoContainer) {
                infoContainer.appendChild(wrapper);
            } else {
                card.appendChild(wrapper);
            }
        });
    };

    const observer = new MutationObserver(() => createTopicInfoButton());
    observer.observe(document.body, { childList: true, subtree: true });

    createTopicInfoButton();
})();
</script>

Предварительный просмотр результата

Выбор за вами :rofl:

4 лайка

Вы действительно улучшили удобство создания события с момента последних обновлений :+1:

Но я хотел бы узнать, есть ли параметр, который запрещает создание события при клике пользователя на календарь. Иногда вместо того, чтобы кликнуть на событие, пользователь кликает рядом, и открывается модальное окно для создания события :sweat_smile:

Спасибо

2 лайка

Я знаю, что я надоедлив, но у меня есть ещё один вопрос или предложение по поводу истёкших событий. Их можно удалять автоматически, так как они больше не представляют интереса, насколько я помню, плагин «Everts» делал это автоматически.

В данный момент я делаю это вручную, поэтому, если есть способ сэкономить время :sweat_smile:

Спасибо команде

1 лайк

Существует ли способ реализовать для локации в событии поиск, как это сделано в Google Календаре или событиях Facebook: а) чтобы помочь пользователю выбрать локацию, и б) чтобы локация была кликабельной и открывалась в картографическом приложении?

2 лайка

В Locations Plugin 🌍 действительно есть подобный функционал, но он интегрирован только с альтернативным Events Plugin 📅.

Разработчик (@merefield) может быть готов интегрировать его с официальным плагином, но, полагаю, это потребует значительных усилий для внедрения и поддержки, поэтому для реализации такого проекта вам определённо потребуется спонсировать эту работу.

3 лайка

Я пытался изучить плагин Events, но застрял на

так как этот сайт уже несколько месяцев не работает.

1 лайк

Стоит подать официальный запрос на новую функцию в канал Contribute > Feature (с тегом calendar-and-events) для рассмотрения. Я могу представить, что это вполне реализуемо, и это может принести высокую ценность для многих.


позднее…
@lou действительно сделал это здесь:

3 лайка

Сообщение было объединено с существующей темой: Поддержка карты местоположения мероприятия

Я планирую экспортировать публичные события на сайт WordPress с помощью плагина, который умеет читать iCal-фид по адресу /discourse-post-event/events.ics. Пока что всё идёт хорошо.

На сайте есть несколько страниц, на которых нужно отображать только часть этих событий — на каждой странице должен быть свой набор.

Я надеялся, что смогу пометить события тегами, связанными с каждой страницей, чтобы эти теги отображались в строке CATEGORIES файла iCal и позволяли фильтровать события по месту назначения. Однако этого не происходит.

Я готов использовать параметр ?tag=[tag] в URL и задавать разные адреса для разных страниц, хотя, судя по всему, такой функции в плагине нет.

Может быть, я что-то упускаю?

Нет, вы ничего не упускаете (насколько мне известно).

Возможность фильтровать .ics-ленту календаря по категориям и/или тегам была бы отличным запросом на Contribute > Feature!

1 лайк

В итоге я решил написать свой собственный фильтр тегов, но, кажется, я понял, что «просроченные» события не загружаются через /discourse-post-event/events.ics. Также я не вижу никакого способа управлять тем, когда событие становится «просроченным». Возможно, мне придётся воспользоваться запросом в Data Explorer вместо использования /…/events.ics

[хотя, кажется, я нашёл лучший способ сделать это, используя /discourse-post-event/event]

1 лайк

Привет! Ищу отзывы по поводу интеграции событий с аудио и прямыми трансляциями. См. последний ответ здесь → https://meta.discourse.org/t/discourse-desktop-mac-app/406912/10?u=nicolsdennis

Удавалось ли кому-нибудь внедрить VDO.Ninja в прямые трансляции мероприятий? Я вижу интеграцию с Zoom и думаю, что, возможно, можно как-то её обойти и использовать VDO.Ninja вместо Zoom :thinking:

3 сообщения были выделены в новую тему: Вопросы о интеграции Zoom Livestream

Маршруты «Предстоящие события» в календаре/событиях Discourse, по-видимому, не включают метаданные Open Graph и Twitter Cards в первоначальный HTML-ответ, сгенерированный на стороне сервера.

Это воспроизводится на размещённом сайте Discourse, где обычные страницы корректно выводят социальные метаданные, а изображение Open Graph для сайта настроено верно.

Пример затронутого маршрута:

https://www.mbacommons.com/upcoming-events/month/2026/8/1

Шаги воспроизведения

  1. Настройте корректное изображение Open Graph для сайта.
  2. Убедитесь, что на главной странице или в обычной теме в исходном HTML присутствуют стандартные метаданные Open Graph.
  3. Откройте /upcoming-events/month/2026/8/1.
  4. Проверьте исходный HTML (через view-source:) или первоначальный ответ сервера, а не DOM после выполнения JavaScript.

Фактический результат

Маршрут «Предстоящие события» возвращает HTTP 200, но в исходном HTML отсутствуют:

  • og:image
  • og:title
  • og:description
  • og:url
  • twitter:card
  • twitter:image

Я также протестировал с помощью LinkedInBot/1.0; результат идентичен.

Аудит исходного HTML на этом же сайте в продакшене показал:

  • главная страница: 11 тегов социальных метаданных
  • обычная тема: 14
  • Предстоящие события: 0

Главная страница и обычные темы корректно используют настроенное изображение Open Graph для сайта, поэтому сама конфигурация брендинга на уровне сайта работает.

Ожидаемый результат

Маршруты «Предстоящие события» должны выводить видимые для краулеров социальные метаданные, сгенерированные на стороне сервера, включая как минимум:

  • og:title
  • og:description
  • og:url
  • og:image, с использованием настроенного изображения Open Graph для сайта в качестве резервного варианта
  • соответствующие метаданные Twitter Cards
    n
    Это должно присутствовать в первоначальном HTML, потому что LinkedIn и подобные социальные краулеры не могут полагаться на клиентский JavaScript тем.

В настоящее время LinkedIn Post Inspector сообщает:

Добавьте тег og:image на страницу, чтобы управлять изображением контента в LinkedIn.

Глобальная инъекция через head_tag в теме не кажется подходящим решением, поскольку на обычных страницах метаданные уже корректны, и это может привести к дублированию или конфликту тегов. Похоже, что требуется обработка на стороне сервера с учётом конкретного маршрута для «Предстоящих событий» или использование стандартного пути рендеринга социальных метаданных в Discourse.

2 лайка

Peertube Live с календарём и событиями Discourse

Этим летом (2025) это работало нормально с Peertube, предоставляя интерфейс чата в теме (используя URL видео /embed/xxx для отображения трансляции и учитывая задержку ~30 секунд из-за P2P в Peertube) при использовании тега #livestream.

Но сегодня я попробовал снова, и чат, похоже, не встраивается в макет, как это было раньше (по крайней мере, насколько я помню). Кроме того, на интерфейсе события нет кнопки «Livestream». Это нормально?

2 лайка

Вы видели недавно обновлённые инструкции? Мне кажется, интерфейс немного изменился, когда всё это было объединено в этот плагин.

1 лайк