forked from wintercms/winter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of Calendar functionality using FullCalendar v4
Need to rebuild on Snowboard / (maybe Vue) with FullCalendar v6.
- Loading branch information
1 parent
090562a
commit 08794be
Showing
103 changed files
with
22,740 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
<?php | ||
|
||
namespace Backend\Behaviors; | ||
|
||
use ApplicationException; | ||
use Backend\Classes\ControllerBehavior; | ||
use Backend\Widgets\Calendar as CalendarWidget; | ||
use Backend\Widgets\Filter as FilterWidget; | ||
use Backend\Widgets\Toolbar as ToolbarWidget; | ||
use Lang; | ||
use stdClass; | ||
use Winter\Storm\Support\Str; | ||
use Winter\Storm\Database\Model; | ||
|
||
/** | ||
* Adds features for working with backend records through a Calendar interface. | ||
* | ||
* This behavior is implemented in the controller like so: | ||
* | ||
* public $implement = [ | ||
* \Backend\Behaviors\CalendarController::class, | ||
* ]; | ||
* | ||
* public $calendarConfig = 'config_calendar.yaml'; | ||
* | ||
* The `$calendarConfig` property makes reference to the calendar configuration | ||
* values as either a YAML file, located in the controller view directory, | ||
* or directly as a PHP array. | ||
* | ||
* @package winter\wn-backend-module | ||
* @author Luke Towers | ||
*/ | ||
class CalendarController extends ControllerBehavior | ||
{ | ||
protected ?ToolbarWidget $toolbarWidget = null; | ||
protected ?FilterWidget $filterWidget = null; | ||
protected ?CalendarWidget $calendarWidget = null; | ||
|
||
/** | ||
* The initialized model used by the behavior. | ||
*/ | ||
protected Model $model; | ||
|
||
/** | ||
* The primary calendar alias to use, default 'calendar' | ||
*/ | ||
protected string $primaryDefinition = 'calendar'; | ||
|
||
/** | ||
* Configuration values that must exist when applying the primary config file. | ||
* - modelClass: Class name for the model | ||
* - searchList: list field definitions for the search widget | ||
*/ | ||
protected array $requiredConfig = ['modelClass', 'searchList']; | ||
|
||
/** | ||
* Behavior constructor | ||
*/ | ||
public function __construct(\Backend\Classes\Controller $controller) | ||
{ | ||
parent::__construct($controller); | ||
|
||
// Build the configuration | ||
$this->config = $this->makeConfig($controller->calendarConfig, $this->requiredConfig); | ||
$this->config->modelClass = Str::normalizeClassName($this->config->modelClass); | ||
} | ||
|
||
/** | ||
* Calendar Controller action | ||
*/ | ||
public function calendar(): void | ||
{ | ||
$this->controller->pageTitle = $this->controller->pageTitle ? : Lang::get($this->getConfig( | ||
'title', | ||
'luketowers.calendarwidget::lang.behaviors.calendar.title' | ||
)); | ||
$this->controller->bodyClass = 'slim-container'; | ||
$this->makeCalendar(); | ||
} | ||
|
||
/** | ||
* Creates the Calendar widget used by this behavior | ||
*/ | ||
public function makeCalendar(): CalendarWidget | ||
{ | ||
$model = $this->controller->calendarCreateModelObject(); | ||
|
||
$config = $this->config; | ||
$config->model = $model; | ||
$config->alias = $this->primaryDefinition; | ||
|
||
// Initialize the Calendar widget | ||
$widget = $this->makeWidget(CalendarWidget::class, $config); | ||
$widget->model = $model; | ||
$widget->bindToController(); | ||
$this->calendarWidget = $widget; | ||
|
||
// Initialize the Toolbar & Filter widgets | ||
$this->initToolbar($config, $widget); | ||
$this->initFilter($config, $widget); | ||
|
||
return $widget; | ||
} | ||
|
||
/** | ||
* Prepare the Toolbar widget if necessary | ||
*/ | ||
protected function initToolbar(stdClass $config, CalendarWidget $widget): void | ||
{ | ||
if (empty($config->toolbar)) { | ||
return; | ||
} | ||
|
||
// Prepare the config and intialize the Toolbar widget | ||
$toolbarConfig = $this->makeConfig($config->toolbar); | ||
$toolbarConfig->alias = $widget->alias . 'Toolbar'; | ||
$toolbarWidget = $this->makeWidget(ToolbarWidget::class, $toolbarConfig); | ||
$toolbarWidget->bindToController(); | ||
$toolbarWidget->cssClasses[] = 'list-header'; | ||
|
||
/* | ||
* Link the Search widget to the Calendar widget | ||
*/ | ||
if ($searchWidget = $toolbarWidget->getSearchWidget()) { | ||
$searchWidget->bindEvent('search.submit', function () use ($widget, $searchWidget) { | ||
$widget->setSearchTerm($searchWidget->getActiveTerm()); | ||
return $widget->onRefresh(); | ||
}); | ||
|
||
$widget->setSearchOptions([ | ||
'mode' => $searchWidget->mode, | ||
'scope' => $searchWidget->scope, | ||
]); | ||
|
||
// Find predefined search term | ||
$widget->setSearchTerm($searchWidget->getActiveTerm()); | ||
} | ||
|
||
$this->toolbarWidget = $toolbarWidget; | ||
} | ||
|
||
/** | ||
* Prepare the Filter widget if necessary | ||
*/ | ||
protected function initFilter(stdClass $config, CalendarWidget $widget): void | ||
{ | ||
if (empty($config->filter)) { | ||
return; | ||
} | ||
|
||
$widget->cssClasses[] = 'list-flush'; | ||
|
||
// Prepare the config and intialize the Toolbar widget | ||
$filterConfig = $this->makeConfig($config->filter); | ||
$filterConfig->alias = $widget->alias . 'Filter'; | ||
$filterWidget = $this->makeWidget(FilterWidget::class, $filterConfig); | ||
$filterWidget->bindToController(); | ||
|
||
/* | ||
* Filter the Calendar when the scopes are changed | ||
*/ | ||
$filterWidget->bindEvent('filter.update', function () use ($widget, $filterWidget) { | ||
return $widget->onFilter(); | ||
}); | ||
|
||
// Apply predefined filter values | ||
$widget->addFilter([$filterWidget, 'applyAllScopesToQuery']); | ||
$this->filterWidget = $filterWidget; | ||
$widget->filterWidget = $this->filterWidget; | ||
|
||
} | ||
|
||
/** | ||
* Creates a new instance of a calendar model. This logic can be changed by overriding it in the controller. | ||
*/ | ||
public function calendarCreateModelObject(): Model | ||
{ | ||
$class = $this->config->modelClass; | ||
return new $class; | ||
} | ||
|
||
/** | ||
* Render the calendar widget | ||
* | ||
* @throws ApplicationException if the calendar widget has not been initialized | ||
*/ | ||
public function calendarRender($options = []): string | ||
{ | ||
if (empty($this->calendarWidget)) { | ||
throw new ApplicationException(Lang::get('backend::lang.calendar.behavior_not_ready')); | ||
} | ||
|
||
if (!empty($options['readOnly']) || !empty($options['disabled'])){ | ||
$this->calendarWidget->previewMode = true; | ||
} | ||
|
||
if (isset($options['preview'])) { | ||
$this->calendarWidget->previewMode = $options['preview']; | ||
} | ||
|
||
return $this->calendarMakePartial('container', [ | ||
'toolbar' => $this->toolbarWidget, | ||
'filter' => $this->filterWidget, | ||
'calendar' => $this->calendarWidget, | ||
]); | ||
} | ||
|
||
/** | ||
* Render the requested partial, providing opportunity for the controller to take over | ||
*/ | ||
public function calendarMakePartial(string $partial, array $params = []): string | ||
{ | ||
$contents = $this->controller->makePartial('calendar_' . $partial, $params, false); | ||
if (!$contents) { | ||
$contents = $this->makePartial($partial, $params); | ||
} | ||
return $contents; | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
modules/backend/behaviors/calendarcontroller/docs/example.config_calendar.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# =================================== | ||
# Calendar Behavior Config | ||
# =================================== | ||
|
||
# Model to use for getting the records to display on the calendar | ||
modelClass: Author\Plugin\Models\Event | ||
|
||
# Search columns | ||
# Used for configuration of additional columns to search by | ||
searchList: $/author/plugin/models/event/columns.yaml | ||
|
||
# Record URL | ||
recordUrl: author/plugins/events/update/:event_id | ||
|
||
|
||
# Record on click | ||
# @see custom.calendar.js sample | ||
# data is a plain object with the following properties: | ||
# startDate: is a JS Date Object | ||
# endDate: is a JS Date Object, may be null | ||
# event: A standard JavaScript object that FullCalendar uses to store information about a calendar event, including id, title, start, end | ||
# eventEl: The HTML element for this event | ||
# recordOnClick: $.wn.eventController.onEventClick(:data, :startDate, :endDate, :event, :eventEl) | ||
|
||
# Triggered when the user clicks on a date or a time | ||
# data is a plain object with the following properties | ||
# date: is the a JS Date Object for the clicked day/time. | ||
# dateStr: An ISO8601 string representation of the date | ||
# allDay: true or false | ||
# dayEl: An HTML element that represents the whole-day that was clicked on. | ||
# event: The native JavaScript event with low-level information such as click coordinates. | ||
# view: The current view @see https://fullcalendar.io/docs/v4/view-object | ||
onClickDate: $.wn.availabilitySlotController.onClickDate(:data, :date, :dateStr, :allDay, :dayEl, :event, :view) | ||
|
||
# The property to use as the title displayed on the calendar | ||
recordTitle: name | ||
|
||
# The property to use as the start time for the record | ||
recordStart: start_time | ||
|
||
# The property to use as the end time for the record | ||
recordEnd: end_time | ||
|
||
# The property to use as the background color displayed on the record, , '' = the default background color in the calendar.less | ||
recordColor: event_color | ||
|
||
# The property to use as the content of the tooltip for the record | ||
recordTooltip: [recordTitle] | ||
|
||
# Available display modes to be supported in this instance | ||
availableDisplayModes: [month, week, day, list] | ||
|
||
# Flag for whether calendar is read only or editable | ||
previewMode: true | ||
|
||
# load one month of records at a time, ensure they stay loaded between month pages | ||
|
||
# Toolbar widget configuration | ||
toolbar: | ||
# Partial for toolbar buttons | ||
buttons: calendar_toolbar | ||
|
||
# Search widget configuration | ||
search: | ||
prompt: backend::lang.list.search_prompt | ||
filter: calendar_filter.yaml | ||
|
||
# when filter gets applied, clear the client's cache of events, essentially start them over | ||
# if they had just loaded this page / month with the current filters applied |
14 changes: 14 additions & 0 deletions
14
modules/backend/behaviors/calendarcontroller/docs/example.custom.calendar.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Sample for config_calendar.yaml -> recordOnClick | ||
+ function ($) { | ||
"use strict"; | ||
|
||
var EventController = function () { | ||
|
||
this.onEventClick = function (data, startDate, endDate, event, eventEl) { | ||
alert('eventID = '+ event.id); | ||
} | ||
|
||
} | ||
$.wn.eventController = new EventController; | ||
|
||
}(window.jQuery); |
9 changes: 9 additions & 0 deletions
9
modules/backend/behaviors/calendarcontroller/partials/_container.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php if ($toolbar) : ?> | ||
<?= $toolbar->render() ?> | ||
<?php endif ?> | ||
|
||
<?php if ($filter) : ?> | ||
<?= $filter->render() ?> | ||
<?php endif ?> | ||
|
||
<?= $calendar->render() ?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.