%PDF- %PDF-
Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/calendar/docs/ |
Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/calendar/docs/interface.md |
# Calendar interface v1.0 This guide describes how to implement the calendar interface in order to inject events into the calendar module. All interface classes reside within the `interface` directory of the calendar module. Calendar interface implementations should reside within the `integration/calendar` directory of your module. > Note: Calendar v1.0 switched from an array type interface to real class level interfaces. The old array type interface is still >supported but deprecated. ## Calendar item types A calendar item type is used to provide some meta data of your custom event type as for example: - `title`: A translatable title - `description`: Short translatable description of your item type - `default color` (optional): A default color used for this item type, which can be overwritten in the calendar module config - `icon` (optional): Icon related to this event type e.g. `fa-calendar` Add your own custom calendar item type by implementing the `humhub\modules\calendar\interfaces\CalendarTypeIF` as follows: **CustomItemType.php:** ```php use humhub\modules\calendar\interfaces\CalendarTypeIF; class CustomItemType implements CalendarTypeIF { const ITEM_TYPE = 'customEvent'; public function getKey() { static::ITEM_TYPE; } public function getDefaultColor() { return '#ffffff'; } public function getTitle() { return Yii::t('MymoduleModule.integration', 'CustomEvent'); } public function getDescription() { return Yii::t('MymoduleModule.integration', 'A custom calendar event'); } public function getIcon() { return 'fa-calendar-o'; } } ``` Configure an event listener for the `getItemTypes` of `humhub\modules\calendar\interfaces\CalendarService`: **config.php**: ```php return [ 'id' => 'mymodule', 'class' => 'mymodule\Module', 'namespace' => 'mymodule', 'events' => [ //... ['class' => 'humhub\modules\calendar\interfaces\CalendarService', 'event' => 'getItemTypes', 'callback' => ['mymodule\Events', 'onGetCalendarItemTypes']], ], ]; ``` > Note: Define the class name as string to prevent a strict dependency to the calendar module. **Event.php:** ```php public static function onGetCalendarItemTypes(CalendarItemTypesEvent $event) { $contentContainer = $event->contentContainer; if(!$contentContainer || $contentContainer->isModuleEnabled('mymodule')) { $event->addType(CustomItemType::ITEM_TYPE, new CustomItemType()); } } ``` Your custom item type should now be listed within the `Other calendars` section of your global and space calendar module settings (in case the module is enabled). > Note: Don't forget to check if your module is enabled on the given `$event->contentContainer`. If no `contentContainer` is given it's meant to be a global search for all available calendar item types. ## Calendar Events Model Custom event models have to implement the `humhub\modules\calendar\interfaces\CalendarEventIF`. In most cases you'll want to implement an `ActiveRecord` based event model, or even better a `ContentActiveRecord` based model. The following database fields should be used for your event model: - `uid`: An event [uid](https://www.kanzaki.com/docs/ical/uid.html) id which will be assigned automatically by the calendar interface in case `EditableEventIF` is implemented. - `start_datetime`: the start date time - `end_datetime`: end date time > Note: In case you want to keep the dependency of your module to the calendar module optional, you should not directly >implement the `CalendarEventIF` on the model class and instead implement a integration adapter class within your `integration/calendar` >directory. ### CalendarEventIF implementation The following example shows a calendar integration by means of a `ContentActiveRecord`, with optional dependency to the calendar module. First implement your custom `ContentActiveRecord` class: **mymodule/models/CustomEvent.php**: ```php namespace mymodule/models; class CustomEvent extends ConentActiveRecord { // Model logic of your module } ``` Then implement the integration adapter class: **mymodule/integration/calendar/CustomCalendarEvent.php**: ``` namespace mymodule/integration/calendar; class CustomCalendarEvent extends CustomEvent implements CalendarEventIF { // Implement all missing CalendarEventIF functions not alrady defined in your base model class public static function find() { return new ActiveQueryContent(static::class); } public static function getObjectModel() { return CustomEvent::class; } } ``` > Note: the `getObjectModel()` needs to be overwritten in order to force the content relation to the original `CustomEvent` class in the content table instead of `CustomCalendarEvent`. > Note: the `find()` function needs to be overwritten in order to stay compatible with HumHub version < 1.4, if your modules min-version is >= 1.4 you can omit this. Here is a short description of the interfac functions: - `getUid()`: The event uid as described above, when implementing `EditableEventIF`, this uid will be assigned automatically if no custom uid was assigned. - `getType()`: returns an instance of the related calendar type - `isAllDay()`: weather or not this event is an all day event - `getStartDateTime()`: start DateTime object of this event - `getEndDateTime()`: end DateTime object of this event - `getTimezone()`: string the timezone string of this event - `getEndTimezone()`: can be used in case the end date timezone differs from the start timezone, otherwise can be null - `getUrl()`: An url to the detail-view of this event, this will be used in the upcoming event snippet and in the calendar view - `getTitle()`: The event title - `getDescription()`: The event description - `getLastModified()`: The last modified DateTime used for ICal export e.g. `new DateTime($this->content->updated_at);` - `getColor()`: (optional) A color used within the calendar view and sidebar snippet, if null is returned the default color will be used - `getSequence()`: (optional) The event [revision sequence](https://www.kanzaki.com/docs/ical/sequence.html) - `getLocation()`: (optional) an event location string - `getBadge()`: (optional) a `humhub\widgets\Label` or string used in the sidebar snippet - `getCalendarOptions()`: (optional) additional event configuration > **Optional** functions can return null if not supported. ### AbstractCalendarQuery implementation The calendar module will trigger the `findItems` event of `humhub\modules\calendar\interfaces\CalendarService` to fetch all events from external modules. The event may contain different filters and the search range. In order to ease the implementation of filtering your events, you should extend the `humhub\modules\calendar\interfaces\event\AbstractCalendarQuery` class. The following example shows the most basic implementation of a custom event query: **mymodule/integration/calendar/CustomCalendarEventQuery**: ```php class CustomCalendarEventQuery extends AbstractCalendarQuery { protected static $recordClass = CustomCalendarEvent::class; } ``` The previous example implies that our `CustomCalendarEvent` model uses the default database fields for `start_datetime` and `end_datetime` and the default database datetime format `Y-m-d H:i:s`. The `AbstractCalendarQuery:dateQueryType` is used to define the behaviour of the date query, by setting one of the following values: - `AbstractCalendarQuery:DATE_QUERY_TYPE_TIME` (default): Will assume all dates are timezone relevant and the default date format is `Y-m-d H:i:s`. - `AbstractCalendarQuery:DATE_QUERY_TYPE_DATE`: Will assume all dates are all day events without timezone translations the default date format is `Y-m-d`. - `AbstractCalendarQuery:DATE_QUERY_TYPE_MIXED`: Should be used in case all day events and time relevant events are mixed, the query will only consider timezone offset differences between user and the system when an `all_day` database flag is not set. Beside the `dateQueryType` the following fields can be overwritten in order to change the default behavior: - `recordClass`: Required in order to set your `ActiveRecord` class used for the query - `allDayField`: Defines the database field name of your models all day flag. This is only required when using `DATE_QUERY_TYPE_MIXED` (default is `all_day`) - `startField`: The name of your start date field (default `start_datetime`) - `endField`: The name of your end date field (default `end_datetime`) - `dateFormat`: The date format of start and end fields see above In case your model extends `ContentActiveRecord` the query class provides a default implementation for the following filter: - `filterDashboard()`: this filter is used for the dashboard upcoming events snippets, by default this filter will make use of the `USER_RELATED_SCOPE_SPACE` and `USER_RELATED_SCOPE_OWN_PROFILE` - `filterGuests()`: used for guest users which are not able to use other filters - `filterUserRelated()`: used for user related queries e.g: 'Only content from following spaces' (see `ActiveQueryContent::userRelated`) - `filterContentContainer()`: used to filter content of a specific ContentContainer (Space/User) - `filterReadable()`: only include content readable by the current user - `filterMine()`: only include items created by me - `setupDateCriteria()`: responsible for the date interval filter Some filter can be implemented manually if supported by your model: - `filterIsParticipant()`: in case the item type supports an own participation logic, this filter is used to only include items in which the current logged in user participates (optional) In case a filter is not supported, the respective filter function should throw a `FilterNotSupportedException`, which by default is the case for all filters except the date criteria filter when a non `ContentActiveRecord` is used as base model. > Note: Guest users are not able to use other filters than the `filterGuests` The following example shows the implementation of a more complex AbstractCalendarQuery with custom start and end date field name , custom date format and custom participation filter. **MeetingCalendarQuery example:** ```php class MeetingCalendarQuery extends AbstractCalendarQuery { protected static $recordClass = Meeting::class; public $startField = 'start_date'; public $endField = 'end_date'; /** * @inheritdoc */ public function filterIsParticipant() { $this->_query->leftJoin('meeting_participant', 'meeting.id=meeting_participant.meeting_id AND meeting_participant.user_id=:userId', [':userId' => $this->_user->id]); $this->_query->andWhere('meeting_participant.id IS NOT NULL'); } } ``` ### Inject calendar entries In order to inject our events to the calendar we need to listen to the `findItems` event of `humhub\modules\calendar\interfaces\CalendarService` as follows: **config.php**: ```php return [ 'events' => [ ['class' => 'humhub\modules\calendar\interfaces\CalendarService', 'event' => 'findItems', 'callback' => ['mymodule\Events', 'onFindCalendarItems']], ], ]; ``` **Event.php:** ```php public static function onFindCalendarItems(CalendarItemsEvent $event) { $contentContainer = $event->contentContainer; if(!$contentContainer || $contentContainer->isModuleEnabled('mymodule')) { $event->addItems(static::ITEM_TYPE_KEY, CustomCalendarEventQuery::findForEvent($event)); } } ``` ### Implementation of EditableEventIF The `humhub\modules\calendar\interfaces\event\EditableEventIF` extends the `CalendarEventIF` and can be implemented in order to support auto `uid` generation when saving or fetching a model. The interface extends the base calendar interface with the following functions: - `setUid($uid)`: Set the uid of this event, in case no manual uid generation was done - `save()`: Should persist all data set by this or sub interfaces. - `setSequence($sequence)`: (optional) Should set the sequence counter field if supported by your event type **Example:** ``` class CustomCalendarEvent extends CustomEvent implements EditableEventIF { // Implementation of other CalendarEventIF functions... public function setUid($uid) { $this->uid = $uid; } public function setSequence($sequence) { $this-sequence = $sequence; } public function saveEvent() { return $this->save(); } public static function find() { return new ActiveQueryContent(static::class); } } ``` ### Implementation of FullCalendarEventIF The `humhub\modules\calendar\interfaces\fullcalendar\FullCalendarEventIF` can be used to change the event behavior in the calendar view or set additional [Fullcalendar options](https://fullcalendar.io/docs/event-object) The following functions are available to change the event behavior: - `isUpdatable()`: enables the drag/drop and resize feature of fullcalendar for this event. Here you should make sure the current logged in user is allowed to edit the underlying model e.g. `$this->content->canEdit()` - `updateTime()`: should update and persist the start and end DateTime of this event. - `getCalendarViewUrl()`: here you can overwrite the url returned by `getUrl()`, usually used to provide a modal view instead of a detail view. If null is returned the `getUrl()` is used and `redirect` view mode is used. - `getCalendarViewMode()`: defines the way the event is opened after being clicked in the calendar view. - `modal`: should be used for modal based views - `redirect`: is the default and should be used for a detail view opened as full page - `getFullCalendarOptions()`: see [Fullcalendar options](https://fullcalendar.io/docs/event-model) In case you want to skip the default drag/drop and resize update mechanism and implement a own one, you can set a `updateUrl` option within `getFullCalendarOptions()`. In case you need to refresh the whole calendar view after drag/drop and resize you can set the `refreshAfterUpdate` option within `getFullCalendarOptions()`. This is may required if updating your event affects other events as well. **Example:** ``` class CustomCalendarEvent extends CustomEvent implements FullCalendarEventIF { // Implementation of other CalendarEventIF functions... public function isUpdatable() { return $this->content->canEdit(); } public function updateTime(DateTime $start, DateTime $end) { $this->start_datetime = CalendarUtils::toDBDateFormat($start); $this->end_datetime = CalendarUtils::toDBDateFormat($end); return $this->save(); } public function getCalendarViewUrl() { return $this->conent->container->createUrl('/mymodule/calendar/view-modal', ['id' => $this-id]); } public function getCalendarViewMode() { return static::VIEW_MODE_MODAL; } public function getFullCalendarOptions() { return [ 'rendering' => 'background'; ] } } ``` ### Recurrent events A recurrent event consist of a root event and multiple recurrent instances. The root event serves as template for all recurrent instances and is not part of a calendar query result itself. The first instance of a recurring event has the same start/end date as the root event unless it has been updated. The recurrent event interface provided by the calendar module supports: - The creation of `rrules` by means of the `humhub\modules\calendar\interfaces\recurrence\RecurrenceFormModel` and `humhub\modules\calendar\interfaces\recurrence\widgets\RecurrenceFormWidget` - The filtering and expansion of recurring events by means of `humhub\modules\calendar\interfaces\recurrenc\AbstractRecurrenceQuery` - Editing of recurrent events either by - Editing all events - Splitting an recurrent event into two seperate recurrent events - Edit single instances which serve as exceptional events - Deleting recurrence instances with automatic `exdate` management - Deleting a recurrence root with all recurrence instances ### Implementation of RecurrentEventIF The `humhub\modules\calendar\interfaces\recurrence\RecurrentEventIF` can be used in order to support recurrent events. Your recurrent event model needs to support the following fields: - `id` the event id - `sequnece` the event id - `start_datetime` as datetime field defining the start of the event - `end_datetime` as datetime field defining the start of the event - `rrule` a [rrule](https://www.kanzaki.com/docs/ical/rrule.html) string - `exdate` a string field containing comma seperated [exdates](https://www.kanzaki.com/docs/ical/exdate.html) - `parent_event_id` the id of the recurring root event A recurring event and instances have to follow the following urles: - `rrule` is set on both recurrent instances and root events in order to detect it as recurrent - `paren_event_id` is not set on non recurrent events and not set on the root event itself - `exdate` is only set on the root event The interface requires the following additional functions: - `getId()`: returns the id of your model - `getRecurrenceRootId()`: returns the id of the root event - `getRrule()`: returns the [rrule](https://www.kanzaki.com/docs/ical/rrule.html) in case the event is recurrent - `setRrule()`: used to set the [rrule](https://www.kanzaki.com/docs/ical/rrule.html) of an event - `getRecurrenceId()`: returns the [recurrence id](https://www.kanzaki.com/docs/ical/recurrenceId.html) of this event - `setRecurrenceId()`: sets the [recurrence id](https://www.kanzaki.com/docs/ical/recurrenceId.html) of this event - `getExdate()`: returns the [https://www.kanzaki.com/docs/ical/exdate.html](https://www.kanzaki.com/docs/ical/exdate.html) string of a root event - `setExdate()`: sets the [https://www.kanzaki.com/docs/ical/exdate.html](https://www.kanzaki.com/docs/ical/exdate.html) string of a root event - `createRecurrence()`: is used to create an event instance for a given start and end (without persisting it!) - `syncEventData()`: should copy all necessary data of a given root event into this instance - `getRecurrenceQuery()`: should return an instance of your `AbstractRecurrenceQuery` - `delete()`: deletes a model from the database **Example** ``` class CustomCalendarEvent extends CustomEvent implements RecurrentEventIF { private $query; public function init() { parent::init(); $this->query = new CustomCalendarEventRecurrenceQuery(['event' => $this]); } // Implementation of other CalendarEventIF functions... public function getId() { return $this->id; } public function getRecurrenceRootId() { return $this->parent_event_id; } public function getRrule() { return $this->rrule; } public function setRrule($rrule) { $this->rrule = $rrule; } public function getRecurrenceId() { return $this->recurrence_id; } public function setRecurrenceId($recurrenceId) { $this->recurrence_id = "recurrence_id; } public function getExdate() { return $this->exdate; } public function setExdate($exdate) { $this->exdate = $exdate; } public function createRecurrence($start, $end) { $instance = new self($this->content->container, $this->content->visibility); $instance->start_datetime = $start; $instance->end_datetime = $end; // Turn off notifications and wall entry creation $instance->silentContentCreation = true; $instance->content->stream_channel = null; return $instance; } public function syncEventData($root, $original = null) { $this->content->created_by = $root->content->created_by; $this->content->visibility = $root->content->visibility; // Only align description if we did not already overwrite it for this event if (!$original || empty($this->description) || $original->description === $this->description) { $this->description = $root->description; } if (!$original || empty($this->participant_info) || $original->participant_info === $this->participant_info) { $this->participant_info = $root->participant_info; } $this->title = $root->title; $this->time_zone = $root->time_zone; $this->all_day = $root->all_day; } public function getRecurrenceQuery() { return $this->query; } } ``` > Note: `delete()` is already implemented by `ActiveRecord`. ### Implementation of AbstractRecurrenceQuery In case of a recurrent event model your query class need to extend `humhub\modules\calendar\interfaces\recurrence\AbstractRecurrenceQuery` instead of the `AbstractCalendarQuery` which allows you to overwrite the following fields in addition to the fields defined in `AbstractCalendarQuery`. - `idField`: the field name of your record `id` field (default `id`) - `rruleField`: the field name of your `rrule` field (default `rrule`) - `sequenceField`: the field name of your `sequence` field (default `sequence`) - `recurrenceIdField`: the field name of your `recurrence_id` field (default `recurrence_id`) When following the database field naming recommendation a recurrence query class can look like: ``` class CustomCalendarEventRecurrenceQuery extends AbstractRecurrenceQuery { public static $recordClass = CustomCalendarEvent::class; } ``` ### CalendarEventReminderIF The `humhub\modules\calendar\interfaces\reminder\CalendarEventReminderIF` is used to support setting reminders for an event. This interface is currently only supported by `ContentActiveRecord` based events and extends the `CalendarEventIF` by: - `getContentRecord()`: should return the related `Content` instance - `getReminderUserQuery()`: should return an `ActiveQueryUser` filtering users to receive the reminder ``` class CustomCalendarEvent extends CustomEvent implements CalendarEventReminderIF { // Implementation of other CalendarEventIF functions... public function getContentRecord() { return $this->content; } public function getReminderUserQuery() { return $this->findParticipantUsers(); } } ``` When implementing an optional dependency to the calendar module as in the previous examples your base model `CustomEvent` needs to implement a `getCalendarEvent()` function which returns a `CustomCalendarEvent`. This is required to determine a `CalendarEventIF` from a given `Content` instance. ``` public function getCalendarEvent() { return new CustomCalendarEvent($this->attributes); } ```