mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-24 22:34:24 +02:00
Merge pull request #4354 from Icinga/feature/integrate-flatpickr
Add datetime picker widget
This commit is contained in:
commit
577e47142e
@ -94,6 +94,7 @@ class PreferenceForm extends Form
|
|||||||
$this->preferences = new Preferences($this->store ? $this->store->load() : array());
|
$this->preferences = new Preferences($this->store ? $this->store->load() : array());
|
||||||
|
|
||||||
$oldTheme = $this->preferences->getValue('icingaweb', 'theme');
|
$oldTheme = $this->preferences->getValue('icingaweb', 'theme');
|
||||||
|
$oldLocale = $this->preferences->getValue('icingaweb', 'language');
|
||||||
|
|
||||||
$webPreferences = $this->preferences->get('icingaweb', array());
|
$webPreferences = $this->preferences->get('icingaweb', array());
|
||||||
foreach ($this->getValues() as $key => $value) {
|
foreach ($this->getValues() as $key => $value) {
|
||||||
@ -118,6 +119,12 @@ class PreferenceForm extends Form
|
|||||||
$this->getResponse()->setReloadCss(true);
|
$this->getResponse()->setReloadCss(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (($locale = $this->getElement('language')) !== null
|
||||||
|
&& $locale->getValue() !== $oldLocale
|
||||||
|
) {
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Redirect-Http', 'yes');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($this->store && $this->getElement('btn_submit')->isChecked()) {
|
if ($this->store && $this->getElement('btn_submit')->isChecked()) {
|
||||||
$this->save();
|
$this->save();
|
||||||
|
@ -11,6 +11,7 @@ if (array_key_exists('_dev', $_GET)) {
|
|||||||
$cssfile = 'css/icinga.min.css';
|
$cssfile = 'css/icinga.min.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$timezone = date_default_timezone_get();
|
||||||
$lang = Translator::splitLocaleCode()->language;
|
$lang = Translator::splitLocaleCode()->language;
|
||||||
$isIframe = $this->layout()->isIframe;
|
$isIframe = $this->layout()->isIframe;
|
||||||
$showFullscreen = $this->layout()->showFullscreen;
|
$showFullscreen = $this->layout()->showFullscreen;
|
||||||
@ -93,7 +94,9 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.name = '<?= $this->protectId('Icinga') ?>';
|
window.name = '<?= $this->protectId('Icinga') ?>';
|
||||||
var icinga = new Icinga({
|
var icinga = new Icinga({
|
||||||
baseUrl: '<?= $this->baseUrl(); ?>'
|
baseUrl: '<?= $this->baseUrl(); ?>',
|
||||||
|
locale: '<?= $lang; ?>',
|
||||||
|
timezone: '<?= $timezone ?>'
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -49,7 +49,7 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
|
|||||||
$type = $attribs['local'] === true ? 'datetime-local' : 'datetime';
|
$type = $attribs['local'] === true ? 'datetime-local' : 'datetime';
|
||||||
unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs)
|
unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs)
|
||||||
$html5 = sprintf(
|
$html5 = sprintf(
|
||||||
'<input type="%s" name="%s" id="%s" step="1" value="%s"%s%s%s',
|
'<input type="%s" data-use-datetime-picker name="%s" id="%s" step="1" value="%s"%s%s%s',
|
||||||
$type,
|
$type,
|
||||||
$this->view->escape($name),
|
$this->view->escape($name),
|
||||||
$this->view->escape($id),
|
$this->view->escape($id),
|
||||||
|
@ -111,6 +111,13 @@ class Auth
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload entire layout if the locale changed
|
||||||
|
if (($locale = $user->getPreferences()->getValue('icingaweb', 'language')) !== null) {
|
||||||
|
if (setlocale(LC_ALL, 0) !== $locale && $this->getRequest()->isXmlHttpRequest()) {
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Redirect-Http', 'yes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
if ($persist) {
|
if ($persist) {
|
||||||
$this->persistCurrentUser();
|
$this->persistCurrentUser();
|
||||||
|
@ -42,7 +42,8 @@ class JavaScript
|
|||||||
'js/icinga/behavior/filtereditor.js',
|
'js/icinga/behavior/filtereditor.js',
|
||||||
'js/icinga/behavior/selectable.js',
|
'js/icinga/behavior/selectable.js',
|
||||||
'js/icinga/behavior/modal.js',
|
'js/icinga/behavior/modal.js',
|
||||||
'js/icinga/behavior/input-enrichment.js'
|
'js/icinga/behavior/input-enrichment.js',
|
||||||
|
'js/icinga/behavior/datetime-picker.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static $vendorFiles = [
|
protected static $vendorFiles = [
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
> .content {
|
> .content {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
|
||||||
> * {
|
> .icinga-form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,33 @@
|
|||||||
// Form colors
|
// Form colors
|
||||||
@button-primary-color: lighten(@base02, 20);
|
@button-primary-color: lighten(@base02, 20);
|
||||||
|
|
||||||
|
// Datetime picker colors
|
||||||
|
@fp-calendarBackground: #3f4458;
|
||||||
|
@fp-calendarBorderColor: darken(#3f4458, 50%);
|
||||||
|
|
||||||
|
@fp-monthForeground: #fff;
|
||||||
|
@fp-monthBackground: #3f4458;
|
||||||
|
|
||||||
|
@fp-weekdaysBackground: transparent;
|
||||||
|
@fp-weekdaysForeground: #fff;
|
||||||
|
|
||||||
|
@fp-dayForeground: fadeout(white, 5%);
|
||||||
|
@fp-dayHoverBackground: lighten(@fp-calendarBackground, 25%);
|
||||||
|
|
||||||
|
@fp-todayColor: #eee;
|
||||||
|
@fp-today_fg_color: #3f4458;
|
||||||
|
|
||||||
|
@fp-selectedDayBackground: #80CBC4;
|
||||||
|
|
||||||
|
.icinga-datetime-picker .flatpickr-day.today {
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: @fp-today_fg_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datetime picker colors (end)
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
background-color: @base02;
|
background-color: @base02;
|
||||||
box-shadow: inset -0.5em 0 1em rgba(0,0,0,0.3);
|
box-shadow: inset -0.5em 0 1em rgba(0,0,0,0.3);
|
||||||
|
@ -60,17 +60,33 @@
|
|||||||
*/
|
*/
|
||||||
define.resolve = function (name) {
|
define.resolve = function (name) {
|
||||||
var requirements = define.defines[name]['requirements'];
|
var requirements = define.defines[name]['requirements'];
|
||||||
if (requirements.filter(define.has).length < requirements.length) {
|
|
||||||
|
var exports, ref;
|
||||||
|
var requiredRefs = [];
|
||||||
|
for (var i = 0; i < requirements.length; i++) {
|
||||||
|
if (define.has(requirements[i])) {
|
||||||
|
ref = define.get(requirements[i]);
|
||||||
|
} else if (requirements[i] === 'exports') {
|
||||||
|
exports = ref = {};
|
||||||
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredRefs = [];
|
requiredRefs.push(ref);
|
||||||
for (var i = 0; i < requirements.length; i++) {
|
|
||||||
requiredRefs.push(define.get(requirements[i]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var factory = define.defines[name]['factory'];
|
var factory = define.defines[name]['factory'];
|
||||||
define.set(name, factory.apply(null, requiredRefs));
|
var resolved = factory.apply(null, requiredRefs);
|
||||||
|
|
||||||
|
if (typeof exports === 'object') {
|
||||||
|
if (typeof resolved !== 'undefined') {
|
||||||
|
throw new Error('Factory for ' + name + ' returned, although exports were populated');
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
define.set(name, resolved);
|
||||||
|
|
||||||
for (var definedName in define.defines) {
|
for (var definedName in define.defines) {
|
||||||
if (define.defines[definedName]['requirements'].indexOf(name) >= 0) {
|
if (define.defines[definedName]['requirements'].indexOf(name) >= 0) {
|
||||||
|
186
public/js/icinga/behavior/datetime-picker.js
Normal file
186
public/js/icinga/behavior/datetime-picker.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/* Icinga Web 2 | (c) 2021 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatetimePicker - Behavior for inputs that should show a date and time picker
|
||||||
|
*/
|
||||||
|
;(function(Icinga, $) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
try {
|
||||||
|
var Flatpickr = require('icinga/ipl/vendor/flatpickr');
|
||||||
|
var notjQuery = require('icinga/ipl/notjQuery');
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Unable to provide datetime picker. Libraries not available:', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior for datetime pickers.
|
||||||
|
*
|
||||||
|
* @param icinga {Icinga} The current Icinga Object
|
||||||
|
*/
|
||||||
|
var DatetimePicker = function(icinga) {
|
||||||
|
Icinga.EventListener.call(this, icinga);
|
||||||
|
this.icinga = icinga;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The formats the server expects
|
||||||
|
*
|
||||||
|
* In a syntax flatpickr understands. Based on https://flatpickr.js.org/formatting/
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.server_full_format = 'Y-m-d\\TH:i:S';
|
||||||
|
this.server_date_format = 'Y-m-d';
|
||||||
|
this.server_time_format = 'H:i:S';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flatpickr instances created
|
||||||
|
*
|
||||||
|
* @type {Map<Flatpickr, string>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this._pickers = new Map();
|
||||||
|
|
||||||
|
this.on('rendered', this.onRendered, this);
|
||||||
|
this.on('close-column', this.onCloseContainer, this);
|
||||||
|
this.on('close-modal', this.onCloseContainer, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
DatetimePicker.prototype = new Icinga.EventListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add flatpickr widget on selected inputs
|
||||||
|
*
|
||||||
|
* @param event {Event}
|
||||||
|
*/
|
||||||
|
DatetimePicker.prototype.onRendered = function(event) {
|
||||||
|
var _this = event.data.self;
|
||||||
|
var containerId = event.target.dataset.icingaContainerId;
|
||||||
|
var inputs = event.target.querySelectorAll('input[data-use-datetime-picker]');
|
||||||
|
|
||||||
|
// Cleanup left-over pickers from the previous content
|
||||||
|
_this.cleanupPickers(containerId);
|
||||||
|
|
||||||
|
$.each(inputs, function () {
|
||||||
|
var server_format = _this.server_full_format;
|
||||||
|
if (this.type === 'date') {
|
||||||
|
server_format = _this.server_date_format;
|
||||||
|
} else if (this.type === 'time') {
|
||||||
|
server_format = _this.server_time_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enableTime = server_format !== _this.server_date_format;
|
||||||
|
var disableDate = server_format === _this.server_time_format;
|
||||||
|
var dateTimeFormatter = _this.createFormatter(! disableDate, enableTime);
|
||||||
|
var options = {
|
||||||
|
locale: _this.loadFlatpickrLocale(),
|
||||||
|
appendTo: this.form.parentNode,
|
||||||
|
altInput: true,
|
||||||
|
enableTime: enableTime,
|
||||||
|
noCalendar: disableDate,
|
||||||
|
dateFormat: server_format,
|
||||||
|
formatDate: function (date, format, locale) {
|
||||||
|
return format === this.dateFormat
|
||||||
|
? Flatpickr.formatDate(date, format, locale)
|
||||||
|
: dateTimeFormatter.format(date);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (name in this.dataset) {
|
||||||
|
if (name.length > 9 && name.substr(0, 9) === 'flatpickr') {
|
||||||
|
var value = this.dataset[name];
|
||||||
|
if (value === '') {
|
||||||
|
value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
options[name.charAt(9).toLowerCase() + name.substr(10)] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fp = Flatpickr(this, options);
|
||||||
|
fp.calendarContainer.classList.add('icinga-datetime-picker');
|
||||||
|
this.parentNode.insertBefore(_this.renderIcon(), fp.altInput.nextSibling);
|
||||||
|
|
||||||
|
_this._pickers.set(fp, containerId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup all flatpickr instances in the closed container
|
||||||
|
*
|
||||||
|
* @param event {Event}
|
||||||
|
*/
|
||||||
|
DatetimePicker.prototype.onCloseContainer = function (event) {
|
||||||
|
var _this = event.data.self;
|
||||||
|
var containerId = event.target.dataset.icingaContainerId;
|
||||||
|
|
||||||
|
_this.cleanupPickers(containerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy all flatpickr instances in the container with the given id
|
||||||
|
*
|
||||||
|
* @param containerId {String}
|
||||||
|
*/
|
||||||
|
DatetimePicker.prototype.cleanupPickers = function (containerId) {
|
||||||
|
this._pickers.forEach(function (cId, fp) {
|
||||||
|
if (cId === containerId) {
|
||||||
|
this._pickers.delete(fp);
|
||||||
|
fp.destroy();
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
DatetimePicker.prototype.createFormatter = function (withDate, withTime) {
|
||||||
|
var options = {};
|
||||||
|
if (withDate) {
|
||||||
|
options.year = 'numeric';
|
||||||
|
options.month = 'numeric';
|
||||||
|
options.day = 'numeric';
|
||||||
|
}
|
||||||
|
if (withTime) {
|
||||||
|
options.hour = 'numeric';
|
||||||
|
options.minute = 'numeric';
|
||||||
|
options.timeZoneName = 'short';
|
||||||
|
options.timeZone = this.icinga.config.timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat([this.icinga.config.locale, 'en'], options);
|
||||||
|
};
|
||||||
|
|
||||||
|
DatetimePicker.prototype.loadFlatpickrLocale = function () {
|
||||||
|
switch (this.icinga.config.locale) {
|
||||||
|
case 'ar':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/ar').Arabic;
|
||||||
|
case 'de':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/de').German;
|
||||||
|
case 'es':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/es').Spanish;
|
||||||
|
case 'fi':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/fi').Finnish;
|
||||||
|
case 'it':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/it').Italian;
|
||||||
|
case 'ja':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/ja').Japanese;
|
||||||
|
case 'pt':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/pt').Portuguese;
|
||||||
|
case 'ru':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/ru').Russian;
|
||||||
|
case 'uk':
|
||||||
|
return require('icinga/ipl/vendor/flatpickr/l10n/uk').Ukrainian;
|
||||||
|
default:
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DatetimePicker.prototype.renderIcon = function () {
|
||||||
|
return notjQuery.render('<i class="icon fa fa-calendar" role="image"></i>');
|
||||||
|
};
|
||||||
|
|
||||||
|
Icinga.Behaviors.DatetimePicker = DatetimePicker;
|
||||||
|
|
||||||
|
})(Icinga, jQuery);
|
@ -190,6 +190,7 @@
|
|||||||
$modal.removeClass('active');
|
$modal.removeClass('active');
|
||||||
// Using `setTimeout` here to let the transition finish
|
// Using `setTimeout` here to let the transition finish
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
$modal.find('#modal-content').trigger('close-modal');
|
||||||
$modal.remove();
|
$modal.remove();
|
||||||
}, 200);
|
}, 200);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user