Повторяющиеся события

Повторяющиеся события — это удобная функция в календарных приложениях, позволяющая пользователям создавать события, которые повторяются с выбранной периодичностью. Начиная с версии 7.1, Scheduler поддерживает стандартный формат повторяющихся событий согласно RFC-5545.

В этом руководстве описано, как работать с повторяющимися событиями в Scheduler и как сохранять их в базе данных.

Вы можете ознакомиться с описанием старого формата повторяющихся событий здесь

По умолчанию повторяющиеся события в Scheduler не включены. Чтобы добавить эту возможность, необходимо активировать специальное расширение на странице — плагин recurring:

scheduler.plugins({
    recurring: true
});

После включения повторяющихся событий в интерфейсе lightbox появится дополнительный раздел, как показано ниже:

Параметры конфигурации

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

  • repeat_date — определяет формат даты, используемый в поле 'End by' (Дата окончания) внутри блока 'recurring' в lightbox.
scheduler.config.repeat_date = "%m/%d/%Y";
...
scheduler.init('scheduler_here', new Date(2019, 7, 5), "month");

Related sample:  Recurring events

Lightbox с блоком 'Recurring'

После активации расширения recurring в lightbox появляется дополнительный раздел "Repeat event". Стандартная конфигурация блока 'recurring' в lightbox выглядит так:

[    
    {name:"description", height:130, map_to:"text", type:"textarea" , focus:true},
    {name:"recurring", height:115, type:"recurring", map_to:"rec_type", 
        button:"recurring"},
    {name:"time", height:72, type:"time", map_to:"auto"}
];

Вы можете добавлять другие разделы, но обязательно оставьте разделы "recurring" и "time". Кроме того, раздел "time" всегда должен идти после "recurring".

Related sample:  Recurring events

Описание формата

Повторяющееся событие сохраняется в базе данных как одна запись, которая содержит все стандартные поля события плюс дополнительные свойства:

  1. start_date - (datetime) дата начала серии
  2. end_date - (datetime) дата окончания серии
  3. rrule - (string) определяет правило повторения
  4. duration - (number) длительность каждого экземпляра события
  5. recurring_event_id - (string|number) ID родительской серии; указывается только для изменённых или удалённых экземпляров
  6. original_start - (datetime) исходная дата изменённого экземпляра; указывается только для изменённых или удалённых экземпляров
  7. deleted - (boolean) пометка о том, что экземпляр удалён; указывается только для удалённых экземпляров

Свойство rrule соответствует формату iCalendar, описанному в RFC-5545, и определяет частоту, интервал и другие параметры повторения.

Отличия от формата iCalendar

Есть два основных отличия нашего формата от формата iCalendar:

Раздельное хранение STDATE и DTEND

В iCalendar обычно даты начала и окончания серии повторяющихся событий включаются в строку RRULE как свойства STDATE и DTEND. В нашем формате start_date и end_date хранятся как отдельные поля. Это упрощает работу с повторяющимися событиями и позволяет легко выполнять выборки по дате без необходимости парсить строку RRULE.

Пример повторяющейся серии событий, повторяющейся каждый понедельник с 1 июня 2024 по 1 декабря 2024:

{
  "id": 1,
  "text": "Weekly Team Meeting",
  "start_date": "2024-06-03 09:00:00",
  "duration": 3600,
  "end_date": "2024-12-02 10:00:00",
  "rrule": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO",
  "recurring_event_id": null,
  "original_start": null
}

Обработка исключений

Исключения — то есть изменённые или удалённые экземпляры — хранятся как отдельные записи, связанные с родительской серией. Такие записи содержат три дополнительных свойства: recurring_event_id, original_start и deleted. Они позволяют определить, какие экземпляры были изменены или удалены, и как они связаны с основной серией.

В отличие от стандартного формата iCalendar, исключения (изменённые или удалённые экземпляры) не хранятся в свойстве EXDATE строки RRULE.

Пример серии повторяющихся событий с одним изменённым и одним удалённым экземпляром:

[
  {
    "id": 1,
    "text": "Weekly Team Meeting",
    "start_date": "2024-06-03 09:00:00",
    "duration": 3600,
    "end_date": "2024-12-02 10:00:00",
    "rrule": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO",
    "recurring_event_id": null,
    "original_start": null
  },
  {
    "id": 2,
    "text": "Special Team Meeting",
    "start_date": "2024-06-10 09:00:00",
    "end_date": "2024-06-10 11:00:00",
    "rrule": null,
    "recurring_event_id": 1,
    "original_start": "2024-06-10 09:00:00"
  },
  {
    "id": 3,
    "text": "Deleted Team Meeting",
    "start_date": "2024-06-17 09:00:00",
    "end_date": "2024-06-17 10:00:00",
    "rrule": null,
    "recurring_event_id": 1,
    "original_start": "2024-06-17 09:00:00",
    "deleted": true
  }
]

Событие, запланированное на 2024-06-10 09:00:00, заменено записью Special Team Meeting, а событие на 2024-06-17 09:00:00 пропущено.

Обратите внимание, что свойство rrule для изменённых или удалённых экземпляров игнорируется.

Также поля text, start_date и end_date для удалённых экземпляров не влияют на поведение Scheduler.

Редактирование/удаление отдельного экземпляра в серии

Вы можете удалить или отредактировать отдельные экземпляры в серии повторяющихся событий.

Важные рекомендации

  • Каждое изменение повторяющегося события приводит к созданию новой записи в базе данных.
  • Отдельные экземпляры связаны с основной серией через свойство recurring_event_id.
  • При редактировании экземпляра поле original_start хранит дату, на которую изначально был запланирован экземпляр, а не новую дату. Например, если экземпляр, запланированный на 27 июля 2024 в 15:00, перенесён на 30 июля 2024 в 15:00, original_start всё равно будет 27 июля 2024 в 15:00.

Логика на сервере

Помимо дополнительных полей, серверный контроллер должен реализовать следующую логику:

  • При добавлении удалённого экземпляра ответ сервера должен содержать статус "deleted".
    • Удалённый экземпляр определяется по непустому свойству deleted.
  • При изменении серии все связанные с ней изменённые и удалённые экземпляры должны быть удалены.
    • Серия определяется по непустому rrule и пустому recurring_event_id.
    • Изменённые экземпляры — это все записи, у которых recurring_event_id совпадает с id серии.
  • Если событие с непустым recurring_event_id удаляется, оно должно быть обновлено путем установки deleted=true вместо физического удаления.

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

Кастомизация блока recurring в lightbox

Начиная с версии 4.2, dhtmlxScheduler позволяет определять собственную HTML-форму для секции 'recurring' в lightbox.

Какие настройки доступны?

  1. Изменение разметки формы.
  2. Удаление ненужных элементов (например, опции ежегодного повторения и связанных полей).
  3. Установка значений по умолчанию для полей ввода (например, всегда выбирать вариант "без даты окончания" и скрывать блок для указания конца повторения).

Пример использования

Пример, в котором опции ежемесячного и ежегодного повторения удалены, а вариант "без даты окончания" выбран по умолчанию (блок конца повторения скрыт):

  1. Определите разметку вашей формы где-либо на странице (можно начать с копирования шаблона по умолчанию из 'scheduler\sources\locale\recurring\'):
    <div class="dhx_form_repeat" id="my_recurring_form">   <form>
        <div>
          <select name="repeat">
            <option value="day">Daily</option>
            <option value="week">Weekly</option>
          </select>
        </div>
        <div>
          <div style="display:none;" id="dhx_repeat_day">
            <input type="hidden" name="day_type" value="d"/>
            <input type="hidden" name="day_count" value="1" />
          </div>
          <div style="display:none;" id="dhx_repeat_week">
            Repeat every week next days:<br />
     
           <label><input type="checkbox" name="week_day" value="1" />Monday</label>
           <label><input type="checkbox" name="week_day" value="2" />Tuesday</label>
           <label><input type="checkbox" name="week_day" value="3" />Wednesday</label>
           <label><input type="checkbox" name="week_day" value="4" />Thursday</label>
           <label><input type="checkbox" name="week_day" value="5" />Friday</label>
           <label><input type="checkbox" name="week_day" value="6" />Saturday</label>
           <label><input type="checkbox" name="week_day" value="0" />Sunday</label>
           <input type="hidden" name="week_count" value="1" />
          </div>
        </div>
     
        <input type="hidden" value="no" name="end">
      </form>
    </div>
  2. Укажите параметр 'form' для секции 'recurring' с ID вашей формы:
    scheduler.config.lightbox.sections = [
        {name:"description", height:130, map_to:"text", type:"textarea" , focus:true},
        {name:"recurring", type:"recurring", map_to:"rec_type", button:"recurring", 
          form:"my_recurring_form"},    {name:"time", height:72, type:"time", map_to:"auto"}
    ];

Основные части

Стандартная HTML-структура блока повторяющихся событий (recurring block) для lightbox на разных языках находится в директории 'scheduler\sources\locale\recurring\'.
Например, для английской локализации используется файл 'scheduler\sources\locale\recurring\repeat_template_en.htm'.

Блок повторяющихся событий в lightbox обычно включает 3 группы элементов управления:

1) Элементы управления для выбора типа повторения. Эти input-элементы имеют имя 'repeat' и одно из следующих значений: 'daily', 'weekly', 'monthly', 'yearly'.
В форме должен присутствовать хотя бы один input с именем 'repeat' и каким-либо значением. Вы можете использовать radio-кнопки, выпадающие списки (select) или задать тип по умолчанию с помощью скрытого input.

Примеры корректного выбора типа повторения в форме:

  • Radio-кнопки:
<label><input type="radio" name="repeat" value="day" />Daily</label><br />
<label><input type="radio" name="repeat" value="week"/>Weekly</label><br />
<label><input type="radio" name="repeat" value="month" />Monthly</label><br />
<label><input type="radio" name="repeat" value="year" />Yearly</label>
  • Select-список, без опций 'Monthly' и 'Yearly':
<select name="repeat">
  <option value="day">Daily</option>
  <option value="week">Weekly</option>
</select>
  • Скрытый input (создает только серию событий "Daily"):
<input type="hidden" name="repeat" value="day" />

2) Секция для настройки параметров повторения в зависимости от выбранного типа. Например, блок для типа "Daily" выглядит так:

<div class="dhx_repeat_center">
   <div style="display:none;" id="dhx_repeat_day">
     <label>
       <input class="dhx_repeat_radio" type="radio" 
            name="day_type" value="d"/>Every
     </label>
       <input class="dhx_repeat_text" type="text" 
            name="day_count" value="1" />day<br>
     <label>
       <input class="dhx_repeat_radio" type="radio" 
            name="day_type" checked value="w"/>Every workday
     </label>
  </div>
...
</div>

Обратите внимание, что разметка, относящаяся к определенному типу повторения, может быть обернута в div с id формата "dhx_repeat_<repeat type>", например, "dhx_repeat_day".
Этот блок будет отображаться только при выборе соответствующего типа повторения.

3) Элементы управления для указания, когда завершать повторение. Input для этого имеет имя 'end'.

Возможные значения: 'no', 'date_of_end' и 'occurences_count'.

Как и для элементов 'repeat', в форме должен быть хотя бы один input с этим именем.

<div class="dhx_repeat_right">
  <label>
    <input type="radio" name="end" value="no" checked/>No end date
  </label><br />
  <label>
    <input type="radio" name="end" value="date_of_end" />After</label>
    <input type="text" name="date_of_end" />
  <br />
  <label>
    <input type="radio" name="end" value="occurences_count" />After</label>
    <input type="text" name="occurences_count" value="1" />Occurrences
</div>

Для режима 'date_of_end' дата указывается в input с именем 'date_of_end'. Аналогично, для режима 'occurences_count' количество повторений указывается в input с именем 'occurences_count'.

Вы можете удалить любой тип повторения или задать его заранее с помощью скрытого input:

<input type="hidden" name="end" value="date_of_end" />
<input type="hidden" name="date_of_end" value="01.01.2024" />

Примечания по изменению блока повторения

Перед кастомизацией блока повторения в lightbox, обратите внимание на следующие моменты:

  1. Атрибут 'name' фиксирован для всех input-элементов; input с другими именами будут проигнорированы.
  2. Атрибут 'value' фиксирован для всех input, кроме тех, которые предназначены для непосредственного ввода пользователем.
  3. Если вы предоставляете новую форму, dhtmlxScheduler не использует её напрямую, а копирует вашу HTML-структуру внутрь шаблона lightbox.
    Это значит, что любые обработчики событий или пользовательские свойства, привязанные к DOM-элементам вашей формы, не будут работать в lightbox.
    Чтобы добавить обработчики событий, их нужно указывать как inline-атрибуты HTML или навешивать при отображении формы в lightbox.

Имейте в виду, что dhtmlxScheduler не работает с вашей исходной HTML-формой, а создает её копию внутри шаблона lightbox.

Например:

  • Эта строка будет скопирована в lightbox:
<input onclick="handler()">
  • А эта — нет:
addEventListener(node, "click", function(){...})

Устаревший формат повторяющихся событий

До версии 7.1 Scheduler использовал собственный формат для повторяющихся событий. Подробнее об этом формате можно узнать здесь.

Наверх