logo

[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 (Событие в календаре).

Методы расширения (интерфейса) 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 (реализованного в данном примере).

Ссылки на элементы API