You are here

FullCalendar.php in FullCalendar 8.4

File

src/Plugin/fullcalendar/type/FullCalendar.php
View source
<?php

namespace Drupal\fullcalendar\Plugin\fullcalendar\type;

use Drupal\Core\Datetime\DateHelper;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\fullcalendar\Plugin\FullcalendarBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @FullcalendarOption(
 *   id = "fullcalendar",
 *   module = "fullcalendar",
 *   js = TRUE,
 *   weight = "-20"
 * )
 */
class FullCalendar extends FullcalendarBase implements ContainerFactoryPluginInterface {
  use OptionsFormHelperTrait;
  const FC_DOCS_URL = 'http://fullcalendar.io/docs';
  const COMMA_REPLACEMENT = '_COMMA_';

  /**
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->moduleHandler = $module_handler;
    $this->languageManager = $language_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container
      ->get('module_handler'), $container
      ->get('language_manager'));
  }

  /**
   * {@inheritdoc}
   */
  public function defineOptions() {
    return $this
      ->getDefaultOptions();
  }

  /**
   * {@inheritdoc}
   */
  public function process(&$settings) {
    static $fc_dom_id = 1;
    if (empty($this->style->view->dom_id)) {
      $this->style->view->dom_id = 'fc-' . $fc_dom_id++;
    }
    $options = $this->style->options;

    // We no longer need ccustom fields.
    unset($options['fields']);
    $settings += $options + [
      'view_name' => $this->style->view->storage
        ->id(),
      'view_display' => $this->style->view->current_display,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {

    /** @var \Drupal\fullcalendar\Plugin\views\style\FullCalendar $style_plugin */
    $style_plugin = $this->style;
    $form['intro'] = [
      '#markup' => $this
        ->t('Fullcalendar defaults have been provided where appropriate. See the "more info" links for the documentation of settings.'),
    ];
    $form['google'] = $this
      ->getFieldsetElement($this
      ->t('Google Calendar Settings'), $this
      ->t('Display events from a public Google Calendar you have configured. @more-info', [
      '@more-info' => Link::fromTextAndUrl($this
        ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/google-calendar', [
        'attributes' => [
          'target' => '_blank',
        ],
      ]))
        ->toString(),
    ]));
    $form['google']['googleCalendarApiKey'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Google Calendar API key'),
      '#default_value' => $style_plugin->options['google']['googleCalendarApiKey'],
      '#size' => '60',
      '#fieldset' => 'google',
    ];
    $form['google']['googleCalendarId'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Google Calendar ID(s)'),
      '#description' => $this
        ->t('You can specify multiple, comma-separated Google Calendar IDs'),
      '#default_value' => $style_plugin->options['google']['googleCalendarId'],
      '#size' => '60',
      '#fieldset' => 'google',
    ];

    // Toolbar settings.
    $form['toolbar'] = $this
      ->getFieldsetElement($this
      ->t('Toolbar'));
    $form['header'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Header'),
      '#description' => $this
        ->t("Defines the buttons and title at the top of the calendar. Enter comma-separated key:value pairs for object properties e.g. left: 'title', center: '', right: 'today prev,next' @more-info", [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/header', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['header'],
      '#size' => '40',
      '#fieldset' => 'toolbar',
    ];
    $form['footer'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Footer'),
      '#description' => $this
        ->t('Defines the controls at the bottom of the calendar. These settings accept the same exact values as the header option. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/footer', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['footer'],
      '#size' => '40',
      '#fieldset' => 'toolbar',
    ];
    $form['titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['titleFormat'], 'toolbar');
    $form['titleRangeSeparator'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Title range separator'),
      '#description' => $this
        ->t('Determines the separator text when formatting the date range in the toolbar title. Default: \\u2013 (en dash)'),
      '#default_value' => $style_plugin->options['titleRangeSeparator'],
      '#prefix' => '<div class="views-left-50">',
      '#suffix' => '</div>',
      '#size' => '40',
      '#fieldset' => 'toolbar',
    ];
    $form['buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['buttonText'], 'toolbar');
    $form['buttonIcons'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Button icons'),
      '#description' => $this
        ->t("Icons that will be displayed in buttons of the header/footer. Enter comma-separated key:value pairs for object properties e.g. prev:'left-single-arrow', next:'right-single-arrow', prevYear:'left-double-arrow', nextYear:'right-double-arrow'  @more-info", [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/buttonIcons', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['buttonIcons'],
      '#prefix' => '<div class="views-left-50">',
      '#suffix' => '</div>',
      '#size' => '40',
      '#fieldset' => 'toolbar',
    ];

    // FC Views.
    $form['views'] = $this
      ->getFieldsetElement($this
      ->t('Views'), $this
      ->t('Select the Fullcalendar views to enable on this calendar.'));
    $form['month_view'] = [
      '#type' => 'checkbox',
      '#title' => t('Month View'),
      '#default_value' => $style_plugin->options['month_view'],
      '#data_type' => 'bool',
      '#fieldset' => 'views',
      '#prefix' => '<div class="views-left-25">',
      '#suffix' => '</div>',
    ];
    $form['timegrid_view'] = [
      '#type' => 'checkbox',
      '#title' => t('TimeGrid View'),
      '#default_value' => $style_plugin->options['timegrid_view'],
      '#data_type' => 'bool',
      '#fieldset' => 'views',
      '#prefix' => '<div class="views-left-25">',
      '#suffix' => '</div>',
    ];
    $form['list_view'] = [
      '#type' => 'checkbox',
      '#title' => t('List View'),
      '#default_value' => $style_plugin->options['list_view'],
      '#data_type' => 'bool',
      '#fieldset' => 'views',
      '#prefix' => '<div class="views-left-25">',
      '#suffix' => '</div>',
    ];
    $form['daygrid_view'] = [
      '#type' => 'checkbox',
      '#title' => t('DayGrid View'),
      '#default_value' => $style_plugin->options['daygrid_view'],
      '#data_type' => 'bool',
      '#fieldset' => 'views',
      '#prefix' => '<div class="views-left-25">',
      '#suffix' => '</div>',
    ];

    // View.
    $form['view_settings'] = $this
      ->getFieldsetElement($this
      ->t('View settings'), '', FALSE, '', [
      'visible' => [
        ':input[name="style_options[month_view]"]' => [
          'checked' => TRUE,
        ],
      ],
    ]);

    // Month View.
    $form['month_view_settings'] = $this
      ->getFieldsetElement($this
      ->t('Month View settings'), '', FALSE, 'view_settings', [
      'visible' => [
        ':input[name="style_options[month_view]"]' => [
          'checked' => TRUE,
        ],
      ],
    ]);
    $form['month_view_settings']['fixedWeekCount'] = [
      '#type' => 'checkbox',
      '#title' => t('Number of weeks'),
      '#description' => $this
        ->t('Determines the number of weeks displayed in a month view (true). If true, the calendar will always be 6 weeks tall. If false, the calendar will have either 4, 5, or 6 weeks, depending on the month.'),
      '#default_value' => $style_plugin->options['month_view_settings']['fixedWeekCount'],
      '#data_type' => 'bool',
      '#fieldset' => 'month_view_settings',
      '#prefix' => '<div class="views-left-50">',
      '#suffix' => '</div>',
    ];
    $form['month_view_settings']['showNonCurrentDates'] = [
      '#type' => 'checkbox',
      '#title' => t('Number of weeks'),
      '#description' => $this
        ->t('In month view, whether dates in the previous or next month should be rendered at all. (true). Days that are disabled will not render events.'),
      '#default_value' => $style_plugin->options['month_view_settings']['showNonCurrentDates'],
      '#data_type' => 'bool',
      '#fieldset' => 'month_view_settings',
      '#prefix' => '<div class="views-left-50">',
      '#suffix' => '</div>',
    ];

    // TimeGrid View
    $form['timegrid_view_settings'] = $this
      ->getFieldsetElement($this
      ->t('TimeGrid View settings'), '', FALSE, 'view_settings', [
      'visible' => [
        ':input[name="style_options[timegrid_view]"]' => [
          'checked' => TRUE,
        ],
      ],
    ]);
    $form['timegrid_view_settings']['allDaySlot'] = [
      '#type' => 'checkbox',
      '#title' => t('Display "all-day" slot'),
      '#description' => $this
        ->t('Determines the number of weeks displayed in a month view (true). When hidden with false, all-day events will not be displayed in TimeGrid views.'),
      '#default_value' => $style_plugin->options['timegrid_view_settings']['allDaySlot'],
      '#data_type' => 'bool',
      '#fieldset' => 'timegrid_view_settings',
    ];
    $form['timegrid_view_settings']['allDayText'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('"All-day" text'),
      '#description' => $this
        ->t('The text titling the “all-day” slot at the top of the calendar (default: all-day).'),
      '#default_value' => $style_plugin->options['timegrid_view_settings']['allDayText'],
      '#size' => '40',
      '#fieldset' => 'timegrid_view_settings',
    ];
    $form['timegrid_view_settings']['slotEventOverlap'] = [
      '#type' => 'checkbox',
      '#title' => t('Determines if timed events in TimeGrid view should visually overlap.'),
      '#description' => $this
        ->t('When set to true (the default), events will overlap each other.'),
      '#default_value' => $style_plugin->options['timegrid_view_settings']['slotEventOverlap'],
      '#data_type' => 'bool',
      '#fieldset' => 'timegrid_view_settings',
    ];
    $form['timegrid_view_settings']['timeGridEventMinHeight'] = [
      '#type' => 'textfield',
      '#title' => t('Guaranteed minimum height.'),
      '#description' => $this
        ->t('Guarantees that events within the TimeGrid views will be a minimum height. An integer pixel value can be specified to force all TimeGrid view events to be at least the given pixel height. (default: null). If not specified (the default), all events will have a height determined by their start and end times.'),
      '#default_value' => $style_plugin->options['timegrid_view_settings']['timeGridEventMinHeight'],
      '#size' => '40',
      '#fieldset' => 'timegrid_view_settings',
    ];

    // List View
    $form['list_view_settings'] = $this
      ->getFieldsetElement($this
      ->t('List View settings'), '', FALSE, 'view_settings', [
      'visible' => [
        ':input[name="style_options[list_view]"]' => [
          'checked' => TRUE,
        ],
      ],
    ]);
    $form['list_view_settings']['listDayFormat'] = [
      '#type' => 'textfield',
      '#title' => t('Day format (left)'),
      '#description' => $this
        ->t('A @more-info that affects the text on the left side of the day headings in list view. If false is specified, no text is displayed.', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('Date Formatter'), Url::fromUri(self::FC_DOCS_URL . '/listDayFormat', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['list_view_settings']['listDayFormat'],
      '#size' => '40',
      '#fieldset' => 'list_view_settings',
    ];
    $form['list_view_settings']['listDayAltFormat'] = [
      '#type' => 'textfield',
      '#title' => t('Day format (right)'),
      '#description' => $this
        ->t('A @more-info that affects the text on the right side of the day headings in list view. If false is specified, no text is displayed.', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('Date Formatter'), Url::fromUri(self::FC_DOCS_URL . '/listDayFormat', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['list_view_settings']['listDayAltFormat'],
      '#size' => '40',
      '#fieldset' => 'list_view_settings',
    ];
    $form['list_view_settings']['noEventsMessage'] = [
      '#type' => 'textfield',
      '#title' => t('No events message'),
      '#description' => $this
        ->t('The text that is displayed in the middle of list view, alerting the user that there are no events within the given range.'),
      '#default_value' => $style_plugin->options['list_view_settings']['noEventsMessage'],
      '#size' => '40',
      '#fieldset' => 'list_view_settings',
    ];
    $form['views_options'] = $this
      ->getFieldsetElement($this
      ->t('View-Specific Options'), $this
      ->t('Options that apply only to specific calendar views, provided as options objects in the views option, keyed by the name of the view. @more-info', [
      '@more-info' => Link::fromTextAndUrl($this
        ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/view-specific-options', [
        'attributes' => [
          'target' => '_blank',
        ],
      ]))
        ->toString(),
    ]));
    $form['views_year'] = $this
      ->getFieldsetElement($this
      ->t('Year'), $this
      ->t('Options that apply only to Year views'), FALSE, 'views_options');
    $form['views_year']['listYear_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_year']['listYear_buttonText'], 'views_year', $this
      ->t('Button text (Year - List)'));
    $form['views_year']['listYear_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_year']['listYear_titleFormat'], 'views_year', $this
      ->t('Title format (Year - List)'));
    $form['views_month'] = $this
      ->getFieldsetElement($this
      ->t('Month'), $this
      ->t('Options that apply only to Month views'), FALSE, 'views_options');
    $form['views_month']['listMonth_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_month']['listMonth_buttonText'], 'views_month', $this
      ->t('Button text (Month - List)'));
    $form['views_month']['listMonth_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_month']['listMonth_titleFormat'], 'views_month', $this
      ->t('Title format (Month - List)'));
    $form['views_month']['dayGridMonth_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_month']['dayGridMonth_buttonText'], 'views_month', $this
      ->t('Button text (Month - Day Grid)'));
    $form['views_month']['dayGridMonth_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_month']['dayGridMonth_titleFormat'], 'views_month', $this
      ->t('Title format (Month - Day Grid)'));
    $form['views_month']['dayGridMonth_columnHeaderFormat'] = $this
      ->getColumnHeaderFormatElement($style_plugin->options['views_month']['dayGridMonth_columnHeaderFormat'], 'views_month', $this
      ->t('Column header format (Month - Day Grid)'));
    $form['views_week'] = $this
      ->getFieldsetElement($this
      ->t('Week'), $this
      ->t('Options that apply only to Week views'), FALSE, 'views_options');
    $form['views_week']['listWeek_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_week']['listWeek_buttonText'], 'views_week', $this
      ->t('Button text (Week - List)'));
    $form['views_week']['listWeek_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_week']['listWeek_titleFormat'], 'views_week', $this
      ->t('Title format (Week - List)'));
    $form['views_week']['dayGridWeek_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_week']['dayGridWeek_buttonText'], 'views_week', $this
      ->t('Button text (Week - Day Grid)'));
    $form['views_week']['dayGridWeek_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_week']['dayGridWeek_titleFormat'], 'views_week', $this
      ->t('Title format (Week - Day Grid)'));
    $form['views_week']['dayGridWeek_columnHeaderFormat'] = $this
      ->getColumnHeaderFormatElement($style_plugin->options['views_week']['dayGridWeek_columnHeaderFormat'], 'views_week', $this
      ->t('Column header format (Week - Day Grid)'));
    $form['views_week']['timeGridWeek_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_week']['timeGridWeek_buttonText'], 'views_week', $this
      ->t('Button text (Week - Time Grid)'));
    $form['views_week']['timeGridWeek_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_week']['timeGridWeek_titleFormat'], 'views_week', $this
      ->t('Title format (Week - Time Grid)'));
    $form['views_week']['timeGridWeek_columnHeaderFormat'] = $this
      ->getColumnHeaderFormatElement($style_plugin->options['views_week']['timeGridWeek_columnHeaderFormat'], 'views_week', $this
      ->t('Column header format (Week - Time Grid)'));
    $form['views_day'] = $this
      ->getFieldsetElement($this
      ->t('Day'), $this
      ->t('Options that apply only to Day views'), FALSE, 'views_options');
    $form['views_day']['listDay_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_day']['listDay_buttonText'], 'views_day', $this
      ->t('Button text (Day - List)'));
    $form['views_day']['listDay_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_day']['listDay_titleFormat'], 'views_day', $this
      ->t('Title format (Day - List)'));
    $form['views_day']['dayGridDay_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_day']['dayGridDay_buttonText'], 'views_day', $this
      ->t('Button text (Day - Day Grid)'));
    $form['views_day']['dayGridDay_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_day']['dayGridDay_titleFormat'], 'views_day', $this
      ->t('Title format (Day - Day Grid)'));
    $form['views_day']['dayGridDay_columnHeaderFormat'] = $this
      ->getColumnHeaderFormatElement($style_plugin->options['views_day']['dayGridDay_columnHeaderFormat'], 'views_day', $this
      ->t('Column header format (Day - Day Grid)'));
    $form['views_day']['timeGridDay_buttonText'] = $this
      ->getButtonTextElement($style_plugin->options['views_day']['timeGridDay_buttonText'], 'views_day', $this
      ->t('Button text (Day - Time Grid)'));
    $form['views_day']['timeGridDay_titleFormat'] = $this
      ->getTitleFormatElement($style_plugin->options['views_day']['timeGridDay_titleFormat'], 'views_day', $this
      ->t('Title format (Day - Time Grid)'));
    $form['views_day']['timeGridDay_columnHeaderFormat'] = $this
      ->getColumnHeaderFormatElement($style_plugin->options['views_day']['timeGridDay_columnHeaderFormat'], 'views_day', $this
      ->t('Column header format (Day - Time Grid)'));

    // Display settings
    $form['display'] = $this
      ->getFieldsetElement($this
      ->t('Display settings'));
    $form['display']['defaultView'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Initial display'),
      '#options' => [
        'dayGridMonth' => $this
          ->t('Month'),
        'timeGridWeek' => $this
          ->t('Week (Agenda)'),
        'dayGridWeek' => $this
          ->t('Week (Basic)'),
        'timeGridDay' => $this
          ->t('Day (Agenda)'),
        'dayGridDay' => $this
          ->t('Day (Basic)'),
        'listYear' => $this
          ->t('Year (List)'),
        'listMonth' => $this
          ->t('Month (List)'),
        'listWeek' => $this
          ->t('Week (List)'),
        'listDay' => $this
          ->t('Day (List)'),
      ],
      '#empty_option' => $this
        ->t('- Select -'),
      '#description' => Link::fromTextAndUrl($this
        ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/intro', [
        'attributes' => [
          'target' => '_blank',
        ],
      ])),
      '#default_value' => $style_plugin->options['display']['defaultView'],
      '#prefix' => '<div class="views-left-30">',
      '#suffix' => '</div>',
      '#fieldset' => 'display',
    ];
    $form['display']['firstDay'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Week starts on'),
      '#options' => DateHelper::weekDays(TRUE),
      '#default_value' => $style_plugin->options['display']['firstDay'],
      '#prefix' => '<div class="views-left-30">',
      '#suffix' => '</div>',
      '#fieldset' => 'display',
    ];

    // Date/Time display
    $form['times'] = $this
      ->getFieldsetElement($this
      ->t('Date & Time Display'), $this
      ->t('Settings that control presence/absence of dates as well as their styling and text. These settings work across a variety of different views. @more-info', [
      '@more-info' => Link::fromTextAndUrl($this
        ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/date-display', [
        'attributes' => [
          'target' => '_blank',
        ],
      ]))
        ->toString(),
    ]));
    $form['times']['weekends'] = [
      '#type' => 'checkbox',
      '#title' => t('Weekends'),
      '#description' => $this
        ->t('Whether to include Saturday/Sunday columns in any of the calendar views (true).'),
      '#default_value' => $style_plugin->options['times']['weekends'],
      '#data_type' => 'bool',
      '#fieldset' => 'times',
    ];
    $form['times']['hiddenDays'] = [
      '#type' => 'textfield',
      '#title' => t('Exclude days'),
      '#description' => $this
        ->t('Exclude certain days-of-the-week from being displayed. By default, no days are hidden, unless weekends is set to false. Enter comma-separated numbers e.g. 2, 4 @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/hiddenDays', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['times']['hiddenDays'],
      '#size' => '40',
      '#fieldset' => 'times',
    ];
    $form['times']['columnHeader'] = [
      '#type' => 'checkbox',
      '#title' => t('Column header'),
      '#description' => $this
        ->t('Whether the day headers should appear. For the Month, TimeGrid, and DayGrid views (true).'),
      '#default_value' => $style_plugin->options['times']['columnHeader'],
      '#data_type' => 'bool',
      '#fieldset' => 'times',
    ];
    $form['axis'] = $this
      ->getFieldsetElement($this
      ->t('Time-axis settings'), $this
      ->t('Settings that control display of times along the side of the calendar. @more-info', [
      '@more-info' => Link::fromTextAndUrl($this
        ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/date-display', [
        'attributes' => [
          'target' => '_blank',
        ],
      ]))
        ->toString(),
    ]));
    $form['axis']['slotDuration'] = [
      '#type' => 'textfield',
      '#title' => t('Duration of slots'),
      '#description' => $this
        ->t('The frequency for displaying time slots. (default: 00:30:00) @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/slotDuration', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['slotDuration'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['axis']['slotLabelInterval'] = [
      '#type' => 'textfield',
      '#title' => t('Interval of slot labels'),
      '#description' => $this
        ->t('The frequency that the time slots should be labelled with text. If not specified, a reasonable value will be automatically computed based on slotDuration. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/slotLabelInterval', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['slotLabelInterval'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['axis']['slotLabelFormat'] = [
      '#type' => 'textfield',
      '#title' => t('Format of slot labels'),
      '#description' => $this
        ->t('Determines the text that will be displayed within a time slot. Enter comma-separated, object properties e.g. hour:numeric, minute:2-digit, omitZeroMinute:true, meridiem:short @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/slotLabelFormat', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['slotLabelFormat'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['axis']['minTime'] = [
      '#type' => 'textfield',
      '#title' => t('First time slot'),
      '#description' => $this
        ->t('Determines the first time slot that will be displayed for each day. (default: 00:00:00) @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/minTime', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['minTime'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['axis']['maxTime'] = [
      '#type' => 'textfield',
      '#title' => t('Last time slot'),
      '#description' => $this
        ->t('Determines the last time slot that will be displayed for each day. This MUST be specified as an exclusive end time. (default: 24:00:00) @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/maxTime', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['maxTime'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['axis']['scrollTime'] = [
      '#type' => 'textfield',
      '#title' => t('Scroll time'),
      '#description' => $this
        ->t('Determines how far forward the scroll pane is initially scrolled. The user will be able to scroll back to see events before this time. (default: 06:00:00) @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/scrollTime', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['axis']['scrollTime'],
      '#size' => '40',
      '#fieldset' => 'axis',
    ];
    $form['nav'] = $this
      ->getFieldsetElement($this
      ->t('Date navigation'));
    $form['nav']['defaultDate'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Default date'),
      '#description' => $this
        ->t('The initial date displayed when the calendar first loads. When not specified, this value defaults to the current date. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/defaultDate', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['nav']['defaultDate'],
      '#size' => '40',
      '#fieldset' => 'nav',
    ];
    $form['nav']['validRange'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Valid date range'),
      '#description' => $this
        ->t('Limits which dates the user can navigate to and where events can go. Dates outside of the valid range will be grayed-out. Enter comma-separated key:value properties e.g. start:2017-05-01, end:2017-06-01 @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/validRange', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['nav']['validRange'],
      '#size' => '40',
      '#fieldset' => 'nav',
    ];
    $form['links'] = $this
      ->getFieldsetElement($this
      ->t('Date Nav Links'));
    $form['links']['navLinks'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable nav links'),
      '#description' => $this
        ->t('Determines if day names and week names are clickable. When true, day headings and weekNumbers will become clickable. Default: false. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/navLinks', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['links']['navLinks'],
      '#data_type' => 'bool',
      '#fieldset' => 'links',
    ];
    $form['links']['navLinkDayClick'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Day click function'),
      '#description' => $this
        ->t('Determines what happens upon a day heading nav-link click. By default, the user is taken to the first day-view that appears in the header. Enter the name of a function you have written for this feature. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/navLinkDayClick', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['links']['navLinkDayClick'],
      '#size' => '40',
      '#fieldset' => 'links',
      '#states' => [
        'visible' => [
          ':input[name="style_options[links][navLinks]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
    ];
    $form['links']['navLinkWeekClick'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Week click function'),
      '#description' => $this
        ->t('Determines what happens upon a week-number nav-link click. By default, the user is taken to the a the first week-view that appears in the header. Enter the name of a function you have written for this feature. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/navLinkWeekClick', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['links']['navLinkWeekClick'],
      '#size' => '40',
      '#fieldset' => 'links',
      '#states' => [
        'visible' => [
          ':input[name="style_options[links][navLinks]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
    ];
    $form['week'] = $this
      ->getFieldsetElement($this
      ->t('Week Numbers'));
    $form['week']['weekNumbers'] = [
      '#type' => 'checkbox',
      '#title' => t('Display week numbers'),
      '#description' => $this
        ->t('Determines if week numbers should be displayed on the calendar. If set to true, week numbers will be displayed in a separate left column in the Month/DayGrid views as well as at the top-left corner of the TimeGrid views. Default: false. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/weekNumbers', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['week']['weekNumbers'],
      '#data_type' => 'bool',
      '#fieldset' => 'week',
    ];
    $form['week']['weekNumbersWithinDays'] = [
      '#type' => 'checkbox',
      '#title' => t('Display week numbers in Month and DayGrid views'),
      '#description' => $this
        ->t('Determines the styling for week numbers in Month and DayGrid views. Default: false. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/weekNumbersWithinDays', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['week']['weekNumbersWithinDays'],
      '#data_type' => 'bool',
      '#fieldset' => 'week',
    ];
    $form['week']['weekNumberCalculation'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Week label'),
      '#description' => $this
        ->t('The method for calculating week numbers that are displayed with the weekNumbers setting e.g. local, ISO, or name of function you have written for this feature. Default: "local" @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/weekNumberCalculation', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['week']['weekNumberCalculation'],
      '#size' => '40',
      '#fieldset' => 'week',
    ];
    $form['week']['weekLabel'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Week label'),
      '#description' => $this
        ->t('The heading text for week numbers. Also affects weeks in date formatting. Default: "W" @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/weekLabel', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['week']['weekLabel'],
      '#size' => '40',
      '#fieldset' => 'week',
    ];
    $form['now'] = $this
      ->getFieldsetElement($this
      ->t('Now Indicator'));
    $form['now']['nowIndicator'] = [
      '#type' => 'checkbox',
      '#title' => t('Now indicator'),
      '#description' => $this
        ->t('Whether or not to display a marker indicating the current time. Default: false. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/nowIndicator', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['now']['nowIndicator'],
      '#data_type' => 'bool',
      '#fieldset' => 'now',
    ];
    $form['now']['now'] = [
      '#type' => 'checkbox',
      '#title' => t('Now'),
      '#description' => $this
        ->t('Explicitly sets the "today" date of the calendar - the day that is normally highlighted in yellow. Enter a @parsable-date or name of function you have written for this feature. @more-info', [
        '@parsable-date' => Link::fromTextAndUrl($this
          ->t('parsable date'), Url::fromUri(self::FC_DOCS_URL . '/date-parsing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/now', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['now']['now'],
      '#data_type' => 'bool',
      '#fieldset' => 'now',
    ];
    $form['business'] = $this
      ->getFieldsetElement($this
      ->t('Business Hours'));
    $form['business']['businessHours'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Business Hours'),
      '#description' => $this
        ->t('Emphasizes certain time slots on the calendar. By default, Monday-Friday, 9am-5pm. For better control, see below. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/businessHours', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['business']['businessHours'],
      '#data_type' => 'bool',
      '#fieldset' => 'business',
    ];
    $form['business']['businessHours2'] = [
      '#type' => 'textfield',
      '#title' => t('Business hours format'),
      '#description' => $this
        ->t('For fine-grain control over business hours enter key:value pairs for object properties. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/businessHours', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['business']['businessHours2'],
      '#size' => '40',
      '#fieldset' => 'business',
      '#states' => [
        'visible' => [
          ':input[name="style_options[business][businessHours]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
    ];
    $form['style'] = $this
      ->getFieldsetElement($this
      ->t('Appearance/Sizing'));
    $form['style']['themeSystem'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Theme'),
      '#options' => [
        'standard' => $this
          ->t('Standard'),
        'bootstrap' => $this
          ->t('Bootstrap 4'),
      ],
      '#default_value' => $style_plugin->options['style']['themeSystem'],
      '#fieldset' => 'style',
    ];
    $form['style']['height'] = [
      '#type' => 'textfield',
      '#title' => t('Height'),
      '#description' => $this
        ->t('Sets the height of the entire calendar, including header and footer. Enter a number, "parent", "auto" or name of function you have written for this feature. Default: This option is unset and the calendar\'s height is calculated by aspectRatio. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/sizing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['style']['height'],
      '#size' => '40',
      '#fieldset' => 'style',
    ];
    $form['style']['contentHeight'] = [
      '#type' => 'textfield',
      '#title' => t('View area height'),
      '#description' => $this
        ->t('Sets the height of the view area of the calendar. Enter a number, "auto" or name of function you have written for this feature. Default: This option is unset and the calendar\'s height is calculated by aspectRatio. @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/sizing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['style']['contentHeight'],
      '#size' => '40',
      '#fieldset' => 'style',
    ];
    $form['style']['aspectRatio'] = [
      '#type' => 'textfield',
      '#title' => t('Width-height ratio'),
      '#description' => $this
        ->t('Sets the width-to-height aspect ratio of the calendar. Enter a float. Default: 1.35 @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/sizing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['style']['aspectRatio'],
      '#size' => '40',
      '#fieldset' => 'style',
    ];
    $form['style']['handleWindowResize'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Resize calendar'),
      '#description' => $this
        ->t('Automatically resize the calendar when the browser window resizes. Default: true @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/sizing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['style']['handleWindowResize'],
      '#data_type' => 'bool',
      '#fieldset' => 'style',
    ];
    $form['style']['windowResizeDelay'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Resize delay'),
      '#description' => $this
        ->t('The time the calendar will wait to adjust its size after a window resize occurs, in milliseconds. Default: 100 @more-info', [
        '@more-info' => Link::fromTextAndUrl($this
          ->t('More info'), Url::fromUri(self::FC_DOCS_URL . '/sizing', [
          'attributes' => [
            'target' => '_blank',
          ],
        ]))
          ->toString(),
      ]),
      '#default_value' => $style_plugin->options['style']['windowResizeDelay'],
      '#size' => '40',
      '#fieldset' => 'style',
    ];

    // Get the regular fields.
    $field_options = $style_plugin->displayHandler
      ->getFieldLabels();

    // Get the date fields.
    $date_fields = $style_plugin
      ->parseFields();
    $form['fields'] = $this
      ->getFieldsetElement($this
      ->t('Customise fields'), $this
      ->t('Customise the Drupal fields to use in the Calendar view.'));
    $form['fields']['title'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Use a custom title'),
      '#default_value' => $style_plugin->options['fields']['title'],
      '#data_type' => 'bool',
      '#fieldset' => 'fields',
    ];
    $form['fields']['title_field'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Title field'),
      '#options' => $field_options,
      '#default_value' => $style_plugin->options['fields']['title_field'],
      '#empty_option' => $this
        ->t('- Select -'),
      '#description' => $this
        ->t('Choose the field with the custom title.'),
      '#process' => [
        '\\Drupal\\Core\\Render\\Element\\Select::processSelect',
      ],
      '#states' => [
        'visible' => [
          ':input[name="style_options[fields][title]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
      '#fieldset' => 'fields',
    ];
    $form['fields']['url'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Use a custom URL'),
      '#default_value' => $style_plugin->options['fields']['url'],
      '#data_type' => 'bool',
      '#fieldset' => 'fields',
    ];
    $form['fields']['url_field'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('URL field'),
      '#options' => $field_options,
      '#default_value' => $style_plugin->options['fields']['url_field'],
      '#empty_option' => $this
        ->t('- Select -'),
      '#description' => $this
        ->t('Choose the field with the custom link.'),
      '#process' => [
        '\\Drupal\\Core\\Render\\Element\\Select::processSelect',
      ],
      '#states' => [
        'visible' => [
          ':input[name="style_options[fields][url]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
      '#fieldset' => 'fields',
    ];
    $form['fields']['date'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Use a custom date field'),
      '#default_value' => $style_plugin->options['fields']['date'],
      '#data_type' => 'bool',
      '#fieldset' => 'fields',
    ];
    $form['fields']['date_field'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Date fields'),
      '#options' => $date_fields,
      '#default_value' => $style_plugin->options['fields']['date_field'],
      '#description' => $this
        ->t('Select one or more date fields.'),
      '#multiple' => TRUE,
      '#size' => count($date_fields),
      '#process' => [
        '\\Drupal\\Core\\Render\\Element\\Select::processSelect',
      ],
      '#states' => [
        'visible' => [
          ':input[name="style_options[fields][date]"]' => [
            'checked' => TRUE,
          ],
        ],
      ],
      '#fieldset' => 'fields',
    ];

    // Disable form elements when not needed.
    if (empty($field_options)) {
      $form['fields']['#description'] = $this
        ->t('All the options are hidden, you need to add fields first.');
      $form['fields']['title']['#type'] = 'hidden';
      $form['fields']['url']['#type'] = 'hidden';
      $form['fields']['date']['#type'] = 'hidden';
      $form['fields']['title_field']['#disabled'] = TRUE;
      $form['fields']['url_field']['#disabled'] = TRUE;
      $form['fields']['date_field']['#disabled'] = TRUE;
    }
    elseif (empty($date_fields)) {
      $form['fields']['date']['#type'] = 'hidden';
      $form['fields']['date_field']['#disabled'] = TRUE;
    }

    // Custom CSS.
    $form['#attached']['library'][] = 'fullcalendar/drupal.fullcalendar.admin';
  }

  /**
   * {@inheritdoc}
   */
  public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = []) {
    $options = $form_state
      ->getValue('style_options');

    // These field options have empty defaults, make sure they stay that way.
    foreach ([
      'title',
      'url',
      'date',
    ] as $field) {
      if (empty($options['fields'][$field]) && isset($options['fields'][$field . '_field'])) {
        unset($options['fields'][$field . '_field']);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function preView(&$settings) {
    $options = [];

    // Get enabled plugins.
    if ($plugins = $this
      ->getEnabledFullcalendarPlugins($settings)) {
      $options['plugins'] = $plugins;
    }

    // Get updated settings.
    $settings = $this
      ->filterSettings($settings);
    $defaultKeys = $this
      ->getCalendarProperties();

    // buttonIcons - true, false or object.
    if (isset($settings['buttonIcons'])) {
      $_type = in_array($settings['buttonIcons'], [
        'true',
        'false',
      ]) ? 'scalar' : 'object';
      $defaultKeys[$_type][] = 'buttonIcons';
      unset($_type);
    }

    // Prepare FC view-specific options.
    $views = [];
    foreach ([
      'views_year',
      'views_month',
      'views_week',
      'views_day',
    ] as $_views) {
      if (!empty($settings[$_views])) {
        foreach ($settings[$_views] as $key => $value) {
          list($_view, $_option) = explode('_', $key);
          $array = $this
            ->convertKeyValuePairsToArray($value);
          if ($_option === 'buttonText') {
            $views[$_view][$array['key']] = $array['value'];
          }
          else {
            $views[$_view][$_option][$array['key']] = $array['value'];
          }
        }
      }
      unset($settings[$_views]);
    }
    if ($views) {
      $options['views'] = $views;
    }

    // Prepare other FC options.
    $settings = $this
      ->flattenMultidimensionalArray($settings);
    $keys = array_keys($settings);
    foreach ($defaultKeys as $type => $properties) {
      foreach ($properties as $property) {
        if (in_array($property, $keys)) {
          switch ($type) {
            case 'scalar':
              switch ($settings[$property]) {
                case 'true':
                  $value = TRUE;
                  break;
                case 'false':
                  $value = FALSE;
                  break;
                default:
                  $value = $settings[$property];
                  break;
              }
              $options[$property] = $value;
              unset($value);
              break;
            case 'array':
              $items = explode(',', $settings[$property]);
              $options[$property] = array_map('trim', $items);
              break;
            case 'object':
              $string = $this
                ->fixCommaSeparatedValues($settings[$property]);
              $values = explode(',', $string);
              foreach ($values as $value) {
                $value = str_replace('_COMMA_', ',', $value);
                $array = $this
                  ->convertKeyValuePairsToArray($value);
                $options[$property][$array['key']] = $array['value'];
              }
              break;
          }
        }
        unset($settings[$property]);
      }
    }
    $settings['options'] = $options;
  }

  /**
   * Check for differences in default settings for this view.
   *
   * @param array $settings
   *  Array of view settings.
   *
   * @return array
   *   Settings that are different from the defaults.
   */
  public function filterSettings(array $settings) {

    // Prepare default options - move 'default' and 'contains' keys a level up.
    $defaults = [];
    $_defaults = $this
      ->defineOptions();
    foreach ($_defaults as $key => $value) {
      if (isset($value['default'])) {
        $defaults[$key] = $value['default'];
      }
      elseif (isset($value['contains'])) {
        foreach ($value['contains'] as $key1 => $value1) {
          $defaults[$key][$key1] = $value1['default'];
        }
      }
    }

    // Diff current settings against default.
    return $this
      ->arrayRecursiveDiff($settings, $defaults);
  }

  /**
   * Check nested arrays for differences.
   *
   * @param array $array1
   *   The original array to check against.
   * @param array $array2
   *  The array to check for in the original one.
   *
   * @return array
   *   Elements in $array1 that are different in $array2.
   */
  public function arrayRecursiveDiff($array1, $array2) {
    $aReturn = [];
    foreach ($array1 as $mKey => $mValue) {
      if (array_key_exists($mKey, $array2)) {
        if (is_array($mValue)) {
          $aRecursiveDiff = $this
            ->arrayRecursiveDiff($mValue, $array2[$mKey]);
          if (count($aRecursiveDiff)) {
            $aReturn[$mKey] = $aRecursiveDiff;
          }
        }
        else {
          if ($mValue != $array2[$mKey]) {
            $aReturn[$mKey] = $mValue;
          }
        }
      }
      else {
        $aReturn[$mKey] = $mValue;
      }
    }
    return $aReturn;
  }

  /**
   * Helper function to check for empty values.
   *
   * @param array $array
   * @param mixed $value
   * @param string $depth1
   * @param string $depth2
   */
  public function filterEmptyValue(&$array, $value, $depth1, $depth2 = '') {
    if (!is_bool($value)) {
      if (!strlen($value)) {
        return;
      }
    }
    if (!strlen($depth2)) {
      $array[$depth1] = $value;
    }
    else {
      $array[$depth1][$depth2] = $value;
    }
  }

  /**
   * Split a key:value pair into array.
   *
   * @param string $value
   *   A string with key:value pairs.
   *
   * @return array
   *   Associative array of values.
   */
  public function convertKeyValuePairsToArray($value) {
    if (strpos($value, self::COMMA_REPLACEMENT) === FALSE) {
      $value = $this
        ->fixCommaSeparatedValues($value);
    }
    $items = explode(',', $value);
    $items = array_map('trim', $items);
    $array = [];
    foreach ($items as $item) {
      list($key, $value) = explode(':', $item);
      $array['key'] = $key;
      $array['value'] = str_replace("'", '', str_replace(self::COMMA_REPLACEMENT, ',', $value));
    }
    return $array;
  }

  /**
   * Replace commas in quoted strings with _COMMA_.
   *
   * This is to allow us split comma-separated values into arrays later.
   *
   * @param string $value
   *   The comma-separates value.
   *
   * @return string|string[]
   *   The string with any commas "," between quotes replaced with _COMMA_.
   */
  public function fixCommaSeparatedValues($value) {

    // Match and replace "," commas between single quotes.
    preg_match_all("/('[^',]+),([^']+')/", $value, $matches);
    if ($matches) {
      foreach ($matches[0] as $match) {
        $_match = str_replace(',', self::COMMA_REPLACEMENT, $match);
        $value = str_replace($match, $_match, $value);
      }
    }
    return $value;
  }

  /**
   * Get list of enabled FC plugins.
   *
   * @param array $settings
   *   Settings for the view.
   *
   * @return array
   */
  public function getEnabledFullcalendarPlugins($settings) {
    $plugins = [];
    $form_fields = [
      'month_view' => 'dayGrid',
      'timegrid_view' => 'timeGrid',
      'list_view' => 'list',
      'daygrid_view' => 'dayGrid',
    ];
    foreach ($form_fields as $field => $fcPlugin) {
      if (isset($settings[$field]) && (bool) $settings[$field] === TRUE) {
        $plugins[] = $fcPlugin;
      }
    }
    if (!empty($settings['google']['googleCalendarApiKey'])) {
      $plugins[] = 'googleCalendar';
    }
    return $plugins;
  }

}

Classes

Namesort descending Description
FullCalendar Plugin annotation @FullcalendarOption( id = "fullcalendar", module = "fullcalendar", js = TRUE, weight = "-20" )