. **/ namespace Zabbix\Widgets\Fields; use CAbsoluteTimeParser, CApiInputValidator, CParser, CRangeTimeParser, CRelativeTimeParser, CTimePeriodHelper, DateTimeZone; use Zabbix\Widgets\CWidgetField; class CWidgetFieldTimePeriod extends CWidgetField { public const DEFAULT_VIEW = \CWidgetFieldTimePeriodView::class; public const DEFAULT_VALUE = ['from' => '', 'to' => '']; public const DATA_SOURCE_DEFAULT = 0; public const DATA_SOURCE_WIDGET = 1; public const DATA_SOURCE_DASHBOARD = 2; private ?string $from_label = null; private ?string $to_label = null; private array $default_period = ['from' => '', 'to' => '']; private ?int $min_period = 0; private ?int $max_period = 0; private ?DateTimeZone $timezone = null; private bool $is_date_only = false; public function __construct(string $name, string $label = null) { parent::__construct($name, $label); $this ->setDefault(self::DEFAULT_VALUE) ->setMaxLength(255) ->setValidationRules(['type' => API_OBJECT, 'fields' => [ 'from' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => $this->getMaxLength()], 'to' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => $this->getMaxLength()] ]]); } public function setValue($value): self { parent::setValue((array) $value); return $this; } public function validate(bool $strict = false): array { $validation_rules = $this->getValidationRules($strict); $value = $this->getValue(); $default = $this->getDefault(); $data_source = $this->getDataSource(); $errors = []; if ($data_source !== self::DATA_SOURCE_DEFAULT) { $data_source_label = $this->getComponentErrorLabel($data_source === self::DATA_SOURCE_DASHBOARD ? _('Dashboard') : _('Widget') ); $has_reference_key = array_key_exists(CWidgetField::FOREIGN_REFERENCE_KEY, $value); $reference_value = $has_reference_key ? $value[CWidgetField::FOREIGN_REFERENCE_KEY] : ''; if (!CApiInputValidator::validate($validation_rules['fields'][CWidgetField::FOREIGN_REFERENCE_KEY], $reference_value, $data_source_label, $error)) { $errors[] = $error; } elseif (!$has_reference_key && ($strict || ($this->getFlags() & self::FLAG_NOT_EMPTY) !== 0)) { $errors[] = _s('Invalid parameter "%1$s": %2$s.', $data_source_label, _('cannot be empty')); } elseif ($strict && $has_reference_key && $reference_value === '') { $errors[] = _s('Invalid parameter "%1$s": %2$s.', $data_source_label, _('referred widget is unavailable') ); } } else { $absolute_time_parser = new CAbsoluteTimeParser(); $relative_time_parser = new CRelativeTimeParser(); $field_labels = [ 'from' => $this->getComponentErrorLabel($this->getFromLabel()), 'to' => $this->getComponentErrorLabel($this->getToLabel()) ]; foreach (['from' => 'from_ts', 'to' => 'to_ts'] as $field => $field_ts) { if (!array_key_exists($field, $value)) { if ($strict || array_key_exists(self::FOREIGN_REFERENCE_KEY, $default)) { $errors[] = _s('Invalid parameter "%1$s": %2$s.', $this->name, _s('the parameter "%1$s" is missing', $field) ); continue; } $value[$field] = $default[$field]; } $field_value = &$value[$field]; $field_value_ts = &$value[$field_ts]; if (!CApiInputValidator::validate($validation_rules['fields'][$field], $field_value, $field_labels[$field], $error)) { $errors[] = $error; continue; } if ($field_value === '') { $field_value_ts = 0; continue; } if ($absolute_time_parser->parse($field_value) === CParser::PARSE_SUCCESS) { $datetime = $absolute_time_parser->getDateTime($field === 'from', $this->timezone); $time_range = $field === 'from' ? '00:00:00' : '23:59:59'; if (!$this->is_date_only || $datetime->format('H:i:s') === $time_range) { $field_value_ts = $datetime->getTimestamp(); continue; } } if ($relative_time_parser->parse($field_value) === CParser::PARSE_SUCCESS) { $datetime = $relative_time_parser->getDateTime($field === 'from', $this->timezone); $has_errors = false; if ($this->is_date_only) { foreach ($relative_time_parser->getTokens() as $token) { if ($token['suffix'] === 'h' || $token['suffix'] === 'm' || $token['suffix'] === 's') { $has_errors = true; break; } } } if (!$has_errors) { $field_value_ts = $datetime->getTimestamp(); continue; } } $errors[] = _s('Invalid parameter "%1$s": %2$s.', $field_labels[$field], $this->is_date_only ? _('a date is expected') : _('a time is expected') ); } if (!$errors) { foreach (['from' => 'from_ts', 'to' => 'to_ts'] as $field => $field_ts) { if ($value[$field_ts] < 0 || $value[$field_ts] > ZBX_MAX_DATE) { $errors[] = _s('Invalid parameter "%1$s": %2$s.', $field_labels[$field], $this->is_date_only ? _('a date is expected') : _('a time is expected') ); } } } if (!$errors && $value['from'] !== '' && $value['to'] !== '') { $min_period = $this->min_period !== 0 ? $this->min_period : ($this->is_date_only ? null : CTimePeriodHelper::getMinPeriod()); $max_period = $this->max_period !== 0 ? $this->max_period : ($this->is_date_only ? null : CTimePeriodHelper::getMaxPeriod($this->timezone)); $period = $value['to_ts'] - $value['from_ts'] + 1; if ($min_period === null && $period <= 1) { $errors[] = _s('Invalid parameter "%1$s": %2$s.', $field_labels['to'], _s('value must be greater than "%1$s"', $field_labels['from']) ); } elseif ($min_period !== null && $period < $min_period) { $errors[] = _n('Minimum time period to display is %1$s minute.', 'Minimum time period to display is %1$s minutes.', (int) ($min_period / SEC_PER_MIN) ); } elseif ($max_period !== null && $period > $max_period + 1) { $errors[] = _n('Maximum time period to display is %1$s day.', 'Maximum time period to display is %1$s days.', (int) round($max_period / SEC_PER_DAY) ); } } } if (!$errors) { $this->setValue($value); } return $errors; } public function toApi(array &$widget_fields = []): void { $value = $this->getValue(); $default = $this->getDefault(); switch ($this->getDataSource()) { case self::DATA_SOURCE_DEFAULT: foreach (['from', 'to'] as $name) { if (!array_key_exists($name, $value)) { return; } if (array_key_exists(self::FOREIGN_REFERENCE_KEY, $default) || $value[$name] !== $default[$name]) { $widget_fields[] = [ 'type' => ZBX_WIDGET_FIELD_TYPE_STR, 'name' => $this->name.'.'.$name, 'value' => $value[$name] ]; } } return; case self::DATA_SOURCE_WIDGET: case self::DATA_SOURCE_DASHBOARD: if (!array_key_exists(self::FOREIGN_REFERENCE_KEY, $value)) { return; } if (!array_key_exists(self::FOREIGN_REFERENCE_KEY, $default) || $value[self::FOREIGN_REFERENCE_KEY] !== $default[self::FOREIGN_REFERENCE_KEY]) { $widget_fields[] = [ 'type' => ZBX_WIDGET_FIELD_TYPE_STR, 'name' => $this->name.'.'.self::FOREIGN_REFERENCE_KEY, 'value' => $value[self::FOREIGN_REFERENCE_KEY] ]; } return; } } public function getDataSource(): int { $value = $this->getValue(); if (array_key_exists(self::FOREIGN_REFERENCE_KEY, $value)) { [ 'reference' => $reference ] = self::parseTypedReference($value[self::FOREIGN_REFERENCE_KEY]); return $reference === self::REFERENCE_DASHBOARD ? self::DATA_SOURCE_DASHBOARD : self::DATA_SOURCE_WIDGET; } elseif (array_key_exists('data_source', $value)) { return (int) $value['data_source']; } else { return self::DATA_SOURCE_DEFAULT; } } public function getFromLabel(): string { return $this->from_label ?? _('From'); } public function setFromLabel(string $label): self { $this->from_label = $label; return $this; } public function getToLabel(): string { return $this->to_label ?? _('To'); } public function setToLabel(string $label): self { $this->to_label = $label; return $this; } private function getComponentErrorLabel(string $component_label): string { $error_label = $component_label; if ($this->label !== null) { $error_label = $this->label.'/'.$error_label; } if ($this->label_prefix !== null) { $error_label = $this->label_prefix.': '.$error_label; } return $error_label; } public function getDefaultPeriod(): array { return $this->default_period; } public function setTimeZone(?DateTimeZone $timezone): self { $this->timezone = $timezone; return $this; } public function setDateOnly(bool $is_date_only = true): self { $this->is_date_only = $is_date_only; return $this; } /** * @param array $period * string $period['from'] * string $period['to'] * * @return $this */ public function setDefaultPeriod(array $period): self { $this->default_period = $period; return $this; } public function setMinPeriod(?int $min_period): self { $this->min_period = $min_period; return $this; } public function setMaxPeriod(?int $max_period): self { $this->max_period = $max_period; return $this; } protected function getValidationRules(bool $strict = false): array { if ($this->getDataSource() === self::DATA_SOURCE_DEFAULT) { $validation_rules = parent::getValidationRules($strict); if (($this->getFlags() & self::FLAG_NOT_EMPTY) !== 0) { self::setValidationRuleFlag($validation_rules['fields']['from'], API_NOT_EMPTY); self::setValidationRuleFlag($validation_rules['fields']['to'], API_NOT_EMPTY); } } else { $validation_rules = ['type' => API_OBJECT, 'fields' => [ CWidgetField::FOREIGN_REFERENCE_KEY => ['type' => API_STRING_UTF8] ]]; } return $validation_rules; } }