[ELMA3] Создание собственных событий в календаре
В статье приведен пример создания собственных событий в календаре для объекта типа Справочник IDelivery. Данный справочник служит для фиксирования заказов на доставку. Справочник IDelivery содержит следующие поля:
- Базовые поля (Наименование, Дата создания, Дата изменения, Автор создания, Автор изменения);
- Дата отправки (Дата/время);
- Планируемая дата доставки (Дата/время);
- Адрес доставки (Текст);
- Посылка доставлена (Да/нет);
- Исполнитель (Пользователь, тип связи - Одиночная);
- Информировать (Пользователь, тип связи – Многие-ко-многим).
При использовании данных свойств будет доступно следующее:
- отображение всего срока события (от Даты начала до Даты окончания события);
- отображение места события (в примере за место отвечает поле Адрес доставки);
- отображение завершения события (в примере используется поле Посылка доставлена, если значение будет Да, то в календаре событие будет зачеркнутым);
- возможность информировать несколько участников (поле Информировать).
Пример отображения данных

Рис. 1. Событие в календаре для объекта

Рис. 2. Завершенное событие и место события
Методы расширения (интерфейса) ICalendarItemProvider
Точка расширения (интерфейс) ICalendarItemProvider имеет следующие методы:
/// <summary>
/// Уникальный идентификатор провайдера
/// </summary>
Guid Uid { get; }
/// <summary>
/// Список событий в календаре
/// </summary>
/// <param name="user">Пользователь</param>
/// <param name="startDate">Дата начала</param>
/// <param name="endDate">Дата окончания</param>
/// <param name="checkPermission">Проверять привилегии?</param>
/// <param name="showExpired">Отображать ли истекшие события</param>
/// <returns>Коллекцию событий в календаре</returns>
ICollection<ICalendarItem> GetItems(IUser user, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
/// <summary>
/// Список событий в календаре
/// </summary>
/// <param name="schedule">Календарь</param>
/// <param name="startDate">Дата начала</param>
/// <param name="endDate">Дата окончания</param>
/// <param name="checkPermission">Проверять привилегии?</param>
/// <param name="showExpired">Отображать ли истекшие события</param>
/// <returns>Коллекцию событий в календаре</returns>
ICollection<ICalendarItem> GetItems(ISchedule schedule, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
/// <summary>
/// Список событий в календаре
/// </summary>
/// <param name="schedules">Коллекция календарей</param>
/// <param name="startDate">Дата начала</param>
/// <param name="endDate">Дата окончания</param>
/// <param name="checkPermission">Проверять привилегии?</param>
/// <param name="showExpired">Отображать ли истекшие события</param>
/// <returns>Коллекцию событий в календаре</returns>
ICollection<ICalendarItem> GetItems(ICollection<ISchedule> schedules, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
/// <summary>
/// События которые можно запланировать несколько раз
/// </summary>
/// <param name="user">Пользователь</param>
/// <returns>Коллекцию событий в календаре</returns>
ICollection<ICalendarPlannedItem> GetRePlannedItems(IUser user);
/// <summary>
/// Не запланированные события
/// </summary>
/// <param name="user">Пользователь</param>
/// <returns>Коллекцию событий в календаре</returns>
ICollection<ICalendarPlannedItem> GetUnPlannedItems(IUser user);
/// <summary>
/// Получить пересекающиеся события сгруппированые по календарям
/// </summary>
/// <param name="schedules">Коллекция календарей</param>
/// <param name="startDate">Дата начала</param>
/// <param name="endDate">Дата окончания</param>
/// <returns>События, сгруппированные по календарям</returns>
IDictionary<ISchedule, ICollection<ICalendarItem>> CrossingItems(ICollection<ISchedule> schedules, DateTime startDate, DateTime endDate);
/// <summary>
/// Добавить событие в календарь
/// </summary>
/// <param name="user">Пользователь</param>
/// <param name="id">CalendarPlannedItem.Id</param>
/// <param name="startDate">Дата начала</param>
/// <param name="endDate">Дата окончания</param>
/// <returns>Событие календаря</returns>
ICalendarItem AddItem(IUser user, string id, DateTime startDate, DateTime endDate, bool removeOther);
/// <summary>
/// Изменить время события событие в календарь
/// </summary>
/// <param name="id">ICalendarItem.Id</param>
/// <param name="dayDelta">дельта смещения в днях</param>
/// <param name="minuteDelta">дельта смещения в минутах</param>
/// <param name="moved">смещено все событие</param>
/// <returns>Событие календаря</returns>
ICalendarItem ModifyTime(string id, int dayDelta, int minuteDelta, bool moved);
/// <summary>
/// Удалить событие
/// </summary>
/// <param name="id">ICalendarItem.Id</param>
void Remove(string id);
/// <summary>
/// Календарь является внешним (события загружаются с внешних ресурсов)
/// </summary>
bool External { get; }
Пример класса точки расширения ICalendarItemProvider
[Component]
public class CalendarItemProvider : ICalendarItemProvider
{
private readonly Guid _uid = new Guid("{03B3D20A-0807-4b20-B1FC-819833399602}");
private IDeliveryFilter GetFilter(ICollection<ISchedule> schedules, DateTime startDate, DateTime endDate)
{
return new InstanceOf<IDeliveryFilter>
{
New =
{
EndDate = new DateTimeRange { From = startDate, To = endDate }
}
}.New;
}
private IUser CurrentUser
{
get { return AuthenticationService.GetCurrentUser<IUser>(); }
}
public DeliveryManager DeliveryManager { get; set; }
public ScheduleManager ScheduleManager { get; set; }
public ISecurityService SecurityService { get; set; }
public IEntityManager<ITaskTimePlan, long> TaskTimePlanManager { get; set; }
public Guid Uid
{
get { return _uid; }
}
public ICollection<ICalendarItem> GetItems(IUser user, DateTime startDate, DateTime endDate,
bool checkPermission = true, bool showExpired = false)
{
return GetItems(ScheduleManager.GetUserSchedule(user), startDate, endDate, checkPermission, showExpired);
}
public ICollection<ICalendarItem> GetItems(ISchedule schedule, DateTime startDate, DateTime endDate,
bool checkPermission = true, bool showExpired = false)
{
return schedule.Owner != null ? GetItems(new List<ISchedule> { schedule }, startDate, endDate, checkPermission, showExpired) : new List<ICalendarItem>();
}
public ICollection<ICalendarItem> GetItems(ICollection<ISchedule> schedules, DateTime startDate,
DateTime endDate, bool checkPermission = true, bool showExpired = false)
{
if (schedules.All(s => s.Owner == null))
return new List<ICalendarItem>();
var filter = GetFilter(schedules, startDate, endDate);
IList<ICalendarItem> ret = new List<ICalendarItem>();
if (checkPermission)
{
ret = DeliveryManager.Find(filter, FetchOptions.All).Select(t => new DeliveryCalendarItem(t)).Cast<ICalendarItem>().ToList();
}
else
{
SecurityService.RunWithElevatedPrivilegies(() => ret = DeliveryManager.Find(filter, FetchOptions.All).Select(t => new DeliveryCalendarItem(t)).Cast<ICalendarItem>().ToList());
}
return ret;
}
public ICollection<ICalendarPlannedItem> GetRePlannedItems(IUser user)
{
return new List<ICalendarPlannedItem>();
}
public ICollection<ICalendarPlannedItem> GetUnPlannedItems(IUser user)
{
return new List<ICalendarPlannedItem>();
}
public IDictionary<ISchedule, ICollection<ICalendarItem>> CrossingItems(ICollection<ISchedule> schedules,
DateTime startDate, DateTime endDate)
{
if (schedules.All(s => s.Owner == null))
return new Dictionary<ISchedule, ICollection<ICalendarItem>>();
startDate = new DateTime(startDate.Ticks + 1000000000); //необходимо для того, чтобы события не пересекались, если время начала одного события = времени завершения другого
endDate = new DateTime(endDate.Ticks - 1000000000); //необходимо для того, чтобы события не пересекались, если время начала одного события = времени завершения другого
var filter = GetFilter(schedules, startDate, endDate);
var list = new List<IDelivery>();
SecurityService.RunWithElevatedPrivilegies(() => list = DeliveryManager.Find(filter, FetchOptions.All).ToList());
var ret = schedules.ToDictionary<ISchedule, ISchedule, ICollection<ICalendarItem>>(schedule => schedule, schedule =>
list.Where(e => e.Executor == schedule.Owner)
.Select(e => new DeliveryCalendarItem(e)
{
OnlyInfo = !SecurityService.CanCheckPermission(PermissionProvider.ViewTaskPermission, e) ||
(SecurityService.CanCheckPermission(PermissionProvider.ViewTaskPermission, e)
&& !SecurityService.HasPermission(PermissionProvider.ViewTaskPermission, e))
})
.Cast<ICalendarItem>()
.ToList()
);
return ret.Where(s => s.Value.Any()).ToDictionary(s => s.Key, s => s.Value);
}
public ICalendarItem AddItem(IUser user, string id, DateTime startDate, DateTime endDate, bool removeOther)
{
return null;
}
public ICalendarItem ModifyTime(string id, int dayDelta, int minuteDelta, bool moved)
{
return null;
}
public void Remove(string id)
{
}
public bool External
{
get { return false; }
}
}
Методы расширения (интерфейса) ICalendarItem
/// <summary>
/// Идентификатор обьекта
/// </summary>
string Id { get; }
/// <summary>
/// Идентификатор исходного обьекта
/// </summary>
string SourceId { get; }
/// <summary>
/// Тип исходного объекта
/// </summary>
Guid SourceTypeUid { get; }
/// <summary>
/// Дата начала
/// </summary>
DateTime StartDate { get; set; }
/// <summary>
/// Дата окончания
/// </summary>
DateTime EndDate { get; set; }
/// <summary>
/// Завершено ли событие
/// </summary>
bool Completed { get; set; }
/// <summary>
/// Выделять просроченные события
/// </summary>
bool MarkExpired { get; }
/// <summary>
/// Определяет есть ли текущий пользователь в информируемых событиях
/// </summary>
bool ToInform { get; }
/// <summary>
/// Тема события
/// </summary>
string Theme { get; set; }
/// <summary>
/// Место события
/// </summary>
string Place { get; set; }
/// <summary>
/// Кому
/// </summary>
string To { get; set; }
/// <summary>
/// Участники события
/// </summary>
IDictionary<string, string> EventUsers { get; set; }
/// <summary>
/// Описание события
/// </summary>
string Description { get; set; }
/// <summary>
/// Флаг означает можно ли копировать событие, перетаскиванием в календаре
/// </summary>
bool HasCopy { get; set; }
/// <summary>
/// Клонировать событие
/// </summary>
/// <returns></returns>
ICalendarItem Clone();
/// <summary>
/// Флаг означает что пользователь не имеет доступа к обьекту события и данные только для информации
/// </summary>
bool OnlyInfo { get; set; }
/// <summary>
/// Ид автора
/// </summary>
long CreationAuthor { get; set; }
/// <summary>
/// Событие только для показа
/// </summary>
bool ReadOnly { get; set; }
/// <summary>
/// Возвращает список комментариев
/// </summary>
/// <returns></returns>
ICollection<EleWise.ELMA.Common.Models.IComment> Comments { get; }
/// <summary>
/// Конфиденциален ли элемент календаря
/// </summary>
bool PrivateAccess { get; }
Пример класса точки расширения ICalendarItem
[Serializable]
public class DeliveryCalendarItem : ICalendarItem
{
[ScriptIgnore]
public readonly IDelivery Delivery;
public DeliveryCalendarItem(IDelivery delivery, IUser currentUser = null)
{
if (delivery == null)
throw new ArgumentNullException("delivery");
Delivery = delivery;
StartDate = delivery.StartDate;
EndDate = delivery.EndDate;
Completed = delivery.IsComplete == true;
Theme = delivery.Name;
Place = delivery != null ? delivery.Adress : "";
Description = "";
HasCopy = true;
OnlyInfo = false;
CreationAuthor = delivery.CreationAuthor.Id;
ReadOnly = false;
var users = new List<IUser> { delivery.Executor };
if (delivery.InformTo.Any())
users.AddRange(delivery.InformTo);
EventUsers = users.Distinct((a, b) => a.Id == b.Id, c => c.Id.GetHashCode()).ToDictionary(u => u.Id.ToString(), u => u.GetShortName());
}
public string Id
{
get { return Delivery.Id.ToString(); }
}
public string SourceId { get { return Delivery.Id.ToString(); } }
public Guid SourceTypeUid { get { return InterfaceActivator.UID<IDelivery>(); } }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public DateTime? LimitStartDate { get; set; }
public DateTime? LimitEndDate { get; set; }
public CalendarEventPeriod Period { get; set; }
public bool Completed { get; set; }
public bool MarkExpired
{
get { return false; }
}
public bool ToInform { get; private set; }
public string Theme { get; set; }
public string Place { get; set; }
public string To { get; set; }
public IDictionary<string, string> EventUsers { get; set; }
public string Description { get; set; }
public bool Periodical { get; private set; }
public ICalendarItem Template { get; set; }
public bool HasCopy { get; set; }
public bool OnlyInfo { get; set; }
public long CreationAuthor { get; set; }
public bool ReadOnly { get; set; }
public ICalendarItem Clone()
{
return new DeliveryCalendarItem(Delivery);
}
public ICollection<EleWise.ELMA.Common.Models.IComment> Comments
{
get { return null; }
}
public bool PrivateAccess { get { return false; } }
}
При использовании данного примера для реализации Ваших собственных событий в календаре – события будут без иконок и без ссылок. Чтобы записи в календаре имели ссылки на объекты и иконки, необходимо реализовать следующие точки расширения: IObjectIcon и IObjectLink. Данные точки расширения необходимо реализовать для объекта DeliveryCalendarItem (реализованного в данном примере).