Creating custom events in calendar

The article describes how to create custom events in the calendar for the object type IDelivery. This object is used to register orders for delivery. The IDelivery object contains the following fields:

  1. Basic fields (Name, Date Created, Date Modified, Author, Change Creator);
  2. Dispatch date (date / time);
  3. Expected delivery date (date / time);
  4. Delivery address (text);
  5. Shipment delivered (Yes / No);
  6. Executor (user, connection type - single);
  7. Informed User (User, connection type - many-to-many).

These properties allow:

  1. Displaying the entire event period (from the start date to the end date of the event);
  2. Displaying the place (the "Delivery Address" field in this example);
  3. Displaying the end of the event (the "Shipment delivered" field in this example. If the field value is "Yes", the event will be crossed out in the calendar);
  4. Informing several participants (the "Informed User" field).

Example of data display

calendar event

Fig. 1 An event in the calendar

calendar event 2

Fig. 2 The place of the event that has already taken place.

Extension methods (interface) ICalendarItemProvider

The extension point (interface) ICalendarItemProvider has the following methods:

/// <summary>
/// Provider’s UID
/// </summary>
Guid Uid { get; }
 
/// <summary>
/// List of events in the calendar
/// </summary>
/// <param name="user">User</param>
/// <param name="startDate">Start Date</param>
/// <param name="endDate">End Date</param>
/// <param name="checkPermission"> Whether to check permissions?</param>
/// <param name="showExpired"> Whether to show past events </param>
/// <returns> Collection of events in the calendar </returns>
ICollection<ICalendarItem> GetItems(IUser user, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
 
/// <summary>
/// List of events in the calendar
/// </summary>
/// <param name="schedule">Calendar</param>
/// <param name="startDate"> Start Date </param>
/// <param name="endDate"> End Date </param>
/// <param name="checkPermission"> Whether to check permissions?</param>
/// <param name="showExpired"> Whether to show past events </param>
/// <returns> Collection of events in the calendar </returns>
ICollection<ICalendarItem> GetItems(ISchedule schedule, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
 
/// <summary>
/// List of events in the calendar
/// </summary>
/// <param name="schedules">Collection of calendars</param>
/// <param name="startDate">Start Date</param>
/// <param name="endDate">End Date</param>
/// <param name="checkPermission">Whether to check permissions?</param>
/// <param name="showExpired">Whether to show past events</param>
/// <returns>Collection of events in the calendar</returns>
ICollection<ICalendarItem> GetItems(ICollection<ISchedule> schedules, DateTime startDate, DateTime endDate, bool checkPermission = true, bool showExpired = false);
 
/// <summary>
/// Events that can be rescheduled several times
/// </summary>
/// <param name="user">User</param>
/// <returns>Collection of events in the calendar</returns>
ICollection<ICalendarPlannedItem> GetRePlannedItems(IUser user);
 
/// <summary>
/// Unplanned events
/// </summary>
/// <param name="user">User</param>
/// <returns>Collection of events in the calendar</returns>
ICollection<ICalendarPlannedItem> GetUnPlannedItems(IUser user);
 
 
/// <summary>
/// Get overlapping events grouped by calendars 
/// <summary>
/// <param name="schedules">Collection of calendars</param>
/// <param name="startDate">Start Date</param>
/// <param name="endDate">End Date</param>
/// <returns> Events grouped by calendars </returns>
IDictionary<ISchedule, ICollection<ICalendarItem>> CrossingItems(ICollection<ISchedule> schedules, DateTime startDate, DateTime endDate);
 
 
/// <summary>
/// Add event to calendar
/// </summary>
/// <param name="user">User</param>
/// <param name="id">CalendarPlannedItem.Id</param>
/// <param name="startDate">Start Date</param>
/// <param name="endDate">End Date</param>
/// <returns>Calendar´s event</returns>
ICalendarItem AddItem(IUser user, string id, DateTime startDate, DateTime endDate, bool removeOther);
 
/// <summary>
/// Change the time of the event in the calendar
/// </summary>
/// <param name="id">ICalendarItem.Id</param>
/// <param name="dayDelta"> Day delta </param>
/// <param name="minuteDelta">Minute delta</param>
/// <param name="moved">The event is rescheduled</param>
/// <returns>Calendar´s event </returns>
ICalendarItem ModifyTime(string id, int dayDelta, int minuteDelta, bool moved);
 
/// <summary>
/// Remove event
/// </summary>
/// <param name="id">ICalendarItem.Id</param>
void Remove(string id);
 
/// <summary>
/// This is an external calendar (events are loaded from external resources)
/// </summary>
bool External { get; }

Example of the extension point class 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); // ensure that events do not overlap, if the start date of an event = the end date of another event
  endDate = new DateTime(endDate.Ticks - 1000000000); // ensure that events do not overlap, if the start date of an event = the end date of another event
  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; }
 }
}
Note
To implement this extension point it is required to implement the ICalendarItem interface (an event in the calendar).

Extension methods (interface) ICalendarItem

/// <summary>
/// Object Identifier
/// </summary>
string Id { get; }
 
/// <summary>
///Identifier of the source object
/// </summary>
string SourceId { get; }
 
/// <summary>
/// Type of the source object
/// </summary>
Guid SourceTypeUid { get; }
 
/// <summary>
/// Start Date
/// </summary>
DateTime StartDate { get; set; }
 
/// <summary>
/// End Date
/// </summary>
DateTime EndDate { get; set; }
         
/// <summary>
/// Whether the event has taken place 
/// </summary>
bool Completed { get; set; }
 
/// <summary>
/// Highlight overdue events
/// </summary>
bool MarkExpired { get; }
         
/// <summary>
/// Determines whether the current user participates in the events 
/// </summary>
bool ToInform { get; }
 
/// <summary>
/// Event subject
/// </summary>
string Theme { get; set; }
 
/// <summary>
/// Event place
/// </summary>
string Place { get; set; }
 
/// <summary>
/// To whom
/// </summary>
string To { get; set; }
 
/// <summary>
/// Participants of the event
/// </summary>
IDictionary<string, string> EventUsers { get; set; }
 
/// <summary>
/// Event Description
/// </summary>
string Description { get; set; }
 
/// <summary>
/// The flag indicates whether it is possible to copy an event in the calendar by dragging and dropping
/// </summary>
bool HasCopy { get; set; }
 
/// <summary>
/// Clone event
/// </summary>
/// <returns></returns>
ICalendarItem Clone();
 
/// <summary>
/// The flag means that the user does not have access to the event object; data is for information only
/// </summary>
bool OnlyInfo { get; set; }
 
/// <summary>
/// Author ID
/// </summary>
long CreationAuthor { get; set; }
 
/// <summary>
/// Read Only Event
/// </summary>
bool ReadOnly { get; set; }
 
/// <summary>
/// Returns a list of comments
/// </summary>
/// <returns></returns>
ICollection<EleWise.ELMA.Common.Models.IComment> Comments { get; }
 
/// <summary>
/// Whether a calendar item is confidential
/// </summary>
bool PrivateAccess { get; }

Example of the extension point class 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; } }
}
Note
Please note that if you use this code to implement custom calendar events, the events will have no icons and no links. To add links to objects and icons, you must implement the following extension points: IObjectIcon and IObjectLink. These extension points need to be implemented for the DeliveryCalendarItem object (implemented in this example).

Links to API elements

ICalendarItem
ICalendarItemProvider
IObjectLink
IObjectIcon