mirror of
https://github.com/dkmstr/openuds.git
synced 2025-10-13 11:33:47 +03:00
Removed dictionary from UserInterface fields _data
This commit is contained in:
@@ -309,7 +309,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
||||
'Kind of service counting for calculating if MAX is reached'
|
||||
),
|
||||
'type': types.ui.FieldType.CHOICE,
|
||||
'rdonly': False,
|
||||
'readonly': False,
|
||||
'order': 101,
|
||||
},
|
||||
)
|
||||
|
@@ -293,7 +293,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': gettext('Base service'),
|
||||
'tooltip': gettext('Service used as base of this service pool'),
|
||||
'type': types.ui.FieldType.CHOICE,
|
||||
'rdonly': True,
|
||||
'readonly': True,
|
||||
'order': 100, # Ensures is At end
|
||||
},
|
||||
{
|
||||
@@ -305,7 +305,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': gettext('OS Manager'),
|
||||
'tooltip': gettext('OS Manager used as base of this service pool'),
|
||||
'type': types.ui.FieldType.CHOICE,
|
||||
'rdonly': True,
|
||||
'readonly': True,
|
||||
'order': 101,
|
||||
},
|
||||
{
|
||||
|
@@ -113,7 +113,7 @@ class BaseModelHandler(Handler):
|
||||
'length': field.get('length', 128),
|
||||
'multiline': field.get('multiline', 0),
|
||||
'tooltip': field.get('tooltip', ''),
|
||||
'rdonly': field.get('rdonly', False),
|
||||
'readonly': field.get('readonly', False),
|
||||
'type': str(field.get('type', types.ui.FieldType.TEXT)),
|
||||
'order': field.get('order', 0),
|
||||
'choices': choices,
|
||||
|
@@ -72,7 +72,7 @@ class InternalDBAuth(auths.Authenticator):
|
||||
order=1,
|
||||
tooltip=_('If checked, each host will have a different user name'),
|
||||
default='false',
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
reverseDns = gui.CheckBoxField(
|
||||
@@ -80,7 +80,7 @@ class InternalDBAuth(auths.Authenticator):
|
||||
order=2,
|
||||
tooltip=_('If checked, the host will be reversed dns'),
|
||||
default='false',
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
acceptProxy = gui.CheckBoxField(
|
||||
|
@@ -29,9 +29,10 @@
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import typing
|
||||
import enum
|
||||
import dataclasses
|
||||
import enum
|
||||
import re
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
@@ -44,6 +45,21 @@ class Tab(enum.StrEnum):
|
||||
DISPLAY = gettext_noop('Display')
|
||||
MFA = gettext_noop('MFA')
|
||||
|
||||
@staticmethod
|
||||
def fromStr(value: typing.Optional[str]) -> typing.Union['Tab', str, None]:
|
||||
"""Returns a Tab from a string
|
||||
If value is not a valid Tab, returns Tab.PARAMETERS
|
||||
|
||||
Args:
|
||||
value (str): String to convert to Tab
|
||||
"""
|
||||
if not value:
|
||||
return None
|
||||
try:
|
||||
return Tab(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
class FieldType(enum.StrEnum):
|
||||
TEXT = 'text'
|
||||
@@ -60,11 +76,38 @@ class FieldType(enum.StrEnum):
|
||||
DATE = 'date'
|
||||
INFO = 'internal-info'
|
||||
|
||||
@staticmethod
|
||||
def fromStr(value: str) -> 'FieldType':
|
||||
"""Returns a FieldType from a string
|
||||
If value is not a valid FieldType, returns FieldType.TEXT
|
||||
|
||||
Args:
|
||||
value (str): String to convert to FieldType
|
||||
"""
|
||||
try:
|
||||
return FieldType(value)
|
||||
except ValueError:
|
||||
return FieldType.TEXT
|
||||
|
||||
|
||||
class FieldPatternType(enum.StrEnum):
|
||||
IPV4 = 'ipv4'
|
||||
IPV6 = 'ipv6'
|
||||
IP = 'ip'
|
||||
MAC = 'mac'
|
||||
URL = 'url'
|
||||
EMAIL = 'email'
|
||||
FQDN = 'fqdn'
|
||||
HOSTNAME = 'hostname'
|
||||
HOST = 'host'
|
||||
PATH = 'path'
|
||||
NONE = ''
|
||||
|
||||
|
||||
class FillerType(typing.TypedDict):
|
||||
callbackName: str
|
||||
function: typing.Callable[..., typing.Any]
|
||||
parameters: typing.List[str]
|
||||
function: typing.NotRequired[typing.Callable[..., typing.Any]]
|
||||
|
||||
|
||||
class ChoiceType(typing.TypedDict):
|
||||
@@ -75,101 +118,26 @@ class ChoiceType(typing.TypedDict):
|
||||
ChoicesType = typing.Union[typing.Callable[[], typing.List[ChoiceType]], typing.List[ChoiceType]]
|
||||
|
||||
|
||||
class FieldDataType(typing.TypedDict):
|
||||
length: int
|
||||
required: bool
|
||||
label: str
|
||||
default: str
|
||||
rdonly: bool
|
||||
order: int
|
||||
tooltip: str
|
||||
value: typing.Any
|
||||
type: str
|
||||
multiline: typing.NotRequired[int]
|
||||
pattern: typing.NotRequired[str]
|
||||
tab: typing.NotRequired[str]
|
||||
choices: typing.NotRequired[ChoicesType]
|
||||
minValue: typing.NotRequired[int]
|
||||
maxValue: typing.NotRequired[int]
|
||||
fills: typing.NotRequired[FillerType]
|
||||
rows: typing.NotRequired[int]
|
||||
|
||||
@dataclasses.dataclass
|
||||
class FieldInfoType:
|
||||
length: int
|
||||
required: bool
|
||||
label: str
|
||||
default: str
|
||||
rdonly: bool
|
||||
default: typing.Union[typing.Callable[[], str], str]
|
||||
readonly: bool
|
||||
order: int
|
||||
tooltip: str
|
||||
value: typing.Any
|
||||
type: str
|
||||
value: typing.Union[typing.Callable[[], typing.Any], typing.Any]
|
||||
type: FieldType
|
||||
multiline: typing.Optional[int] = None
|
||||
pattern: typing.Optional[str] = None
|
||||
tab: typing.Optional[str] = None
|
||||
pattern: typing.Union[FieldPatternType, 're.Pattern'] = FieldPatternType.NONE
|
||||
tab: typing.Union[Tab, str, None] = None
|
||||
choices: typing.Optional[ChoicesType] = None
|
||||
minValue: typing.Optional[int] = None
|
||||
maxValue: typing.Optional[int] = None
|
||||
fills: typing.Optional[FillerType] = None
|
||||
rows: typing.Optional[int] = None
|
||||
|
||||
# Temporal methods to allow access to dataclass fields
|
||||
# using dict
|
||||
def __getitem__(
|
||||
self,
|
||||
key: typing.Literal[
|
||||
'lentgh',
|
||||
'required',
|
||||
'label',
|
||||
'default',
|
||||
'rdonly',
|
||||
'order',
|
||||
'tooltip',
|
||||
'value',
|
||||
'type',
|
||||
'multiline',
|
||||
'pattern',
|
||||
'tab',
|
||||
'choices',
|
||||
'minValue',
|
||||
'maxValue',
|
||||
'fills',
|
||||
'rows',
|
||||
],
|
||||
) -> typing.Any:
|
||||
return getattr(self, key)
|
||||
|
||||
def __setitem__(
|
||||
self,
|
||||
key: typing.Literal[
|
||||
'lentgh',
|
||||
'required',
|
||||
'label',
|
||||
'default',
|
||||
'rdonly',
|
||||
'order',
|
||||
'tooltip',
|
||||
'value',
|
||||
'type',
|
||||
'multiline',
|
||||
'pattern',
|
||||
'tab',
|
||||
'choices',
|
||||
'minValue',
|
||||
'maxValue',
|
||||
'fills',
|
||||
'rows',
|
||||
],
|
||||
value: typing.Any,
|
||||
) -> None:
|
||||
setattr(self, key, value)
|
||||
|
||||
def asDict(self) -> typing.Dict[str, typing.Any]:
|
||||
"""Returns a dict with all fields that are not None
|
||||
"""
|
||||
return {
|
||||
k: v
|
||||
for k, v in dataclasses.asdict(self).items()
|
||||
if v is not None
|
||||
}
|
||||
"""Returns a dict with all fields that are not None"""
|
||||
return {k: v for k, v in dataclasses.asdict(self).items() if v is not None}
|
||||
|
@@ -219,7 +219,7 @@ class gui:
|
||||
return sorted(choices, key=lambda item: item['text'].lower())
|
||||
|
||||
@staticmethod
|
||||
def toBool(value: typing.Union[str, bool, int]) -> bool:
|
||||
def toBool(value: typing.Union[str, bytes, bool, int]) -> bool:
|
||||
"""
|
||||
Converts the string "true" (case insensitive) to True (boolean).
|
||||
Anything else is converted to false
|
||||
@@ -230,9 +230,7 @@ class gui:
|
||||
Returns:
|
||||
True if the string is "true" (case insensitive), False else.
|
||||
"""
|
||||
if value is True or str(value).lower() in [gui.TRUE, '1', 'yes']:
|
||||
return True
|
||||
return False
|
||||
return value in (True, 'True', b'true', b'True', 1, '1', b'1', gui.TRUE)
|
||||
|
||||
@staticmethod
|
||||
def fromBool(bol: bool) -> str:
|
||||
@@ -265,7 +263,7 @@ class gui:
|
||||
* label: Label used with this field. Defaults to ''
|
||||
* dafault: Default value for the field. Defaults to '' (this is
|
||||
always an string)
|
||||
* rdonly: If the field is read only on modification. On creation,
|
||||
* readonly: If the field is read only on modification. On creation,
|
||||
all fields are "writable". Defaults to False
|
||||
* order: order inside the form, defaults to 0 (if two or more fields
|
||||
has same order, the output order may be anything)
|
||||
@@ -278,7 +276,7 @@ class gui:
|
||||
* label
|
||||
* tooltip
|
||||
* default (if not included, will be ''). Alias for this field is defaultValue
|
||||
* rdonly if can't be modified once it's created. Aliases for this field is readOnly
|
||||
* readonly if can't be modified once it's created. Aliases for this field is readOnly
|
||||
|
||||
Any other paremeter needed is indicated in the corresponding field class.
|
||||
|
||||
@@ -300,47 +298,47 @@ class gui:
|
||||
label = kwargs.get('label', '')
|
||||
# if defvalue or defaultValue or defValue in kwargs, emit a warning
|
||||
# with the new name (that is "default"), but use the old one
|
||||
for i in ('defvalue', 'defaultValue', 'defValue'):
|
||||
if i in kwargs:
|
||||
try:
|
||||
caller = inspect.stack()[
|
||||
2
|
||||
] # bypass this method and the caller (that is a derived class)
|
||||
except IndexError:
|
||||
caller = inspect.stack()[1] # bypass only this method
|
||||
logger.warning(
|
||||
'Field %s: %s parameter is deprecated, use "default" instead. Called from %s:%s',
|
||||
label,
|
||||
i,
|
||||
caller.filename,
|
||||
caller.lineno,
|
||||
)
|
||||
kwargs['default'] = kwargs[i]
|
||||
break
|
||||
for new_name, old_names in (
|
||||
('default', ('defvalue', 'defaultValue', 'defValue')),
|
||||
('readonly', ('rdonly, readOnly')),
|
||||
):
|
||||
for i in old_names:
|
||||
if i in kwargs:
|
||||
try:
|
||||
caller = inspect.stack()[
|
||||
2
|
||||
] # bypass this method and the caller (that is a derived class)
|
||||
except IndexError:
|
||||
caller = inspect.stack()[1] # bypass only this method
|
||||
logger.warning(
|
||||
'Field %s: %s parameter is deprecated, use "%s" instead. Called from %s:%s',
|
||||
label,
|
||||
i,
|
||||
new_name,
|
||||
caller.filename,
|
||||
caller.lineno,
|
||||
)
|
||||
kwargs[new_name] = kwargs[i]
|
||||
break
|
||||
default = kwargs.get('default', '')
|
||||
# Length is not used on some kinds of fields, but present in all anyway
|
||||
# This property only affects in "modify" operations
|
||||
self._data = types.ui.FieldInfoType(
|
||||
length=kwargs.get(
|
||||
'length', gui.InputField.DEFAULT_LENTGH
|
||||
), # Length is not used on some kinds of fields, but present in all anyway
|
||||
length=kwargs.get('length', gui.InputField.DEFAULT_LENTGH),
|
||||
required=kwargs.get('required', False),
|
||||
label=kwargs.get('label', ''),
|
||||
default=str(default) if not callable(default) else default,
|
||||
rdonly=kwargs.get(
|
||||
'rdonly',
|
||||
kwargs.get('readOnly', kwargs.get('readonly', False)),
|
||||
), # This property only affects in "modify" operations
|
||||
readonly=kwargs.get('readonly', False),
|
||||
order=kwargs.get('order', 0),
|
||||
tooltip=kwargs.get('tooltip', ''),
|
||||
value=kwargs.get('value', default),
|
||||
type=kwargs.get('type', ''),
|
||||
type=types.ui.FieldType.fromStr(kwargs.get('type', '')),
|
||||
tab=types.ui.Tab.fromStr(kwargs.get('tab')),
|
||||
)
|
||||
|
||||
if 'tab' in kwargs and kwargs['tab']:
|
||||
self._data['tab'] = str(kwargs['tab']) # Ensure it's a string
|
||||
|
||||
@property
|
||||
def type(self) -> 'types.ui.FieldType':
|
||||
return types.ui.FieldType(self._data['type'])
|
||||
return types.ui.FieldType(self._data.type)
|
||||
|
||||
@type.setter
|
||||
def type(self, type_: 'types.ui.FieldType') -> None:
|
||||
@@ -350,22 +348,28 @@ class gui:
|
||||
Args:
|
||||
type: Type to set (from constants of this class)
|
||||
"""
|
||||
self._data['type'] = str(type_)
|
||||
self._data.type = type_
|
||||
|
||||
def isType(self, type_: typing.Union['types.ui.FieldType', str]) -> bool:
|
||||
def isType(self, type_: str) -> bool:
|
||||
"""
|
||||
Returns true if this field is of specified type
|
||||
"""
|
||||
return self._data['type'] == str(type_)
|
||||
return self._data.type == type_
|
||||
|
||||
def isSerializable(self) -> bool:
|
||||
return True
|
||||
|
||||
def num(self) -> int:
|
||||
return -1
|
||||
try:
|
||||
return int(self.value)
|
||||
except Exception:
|
||||
return -1
|
||||
|
||||
def isTrue(self) -> bool:
|
||||
return False
|
||||
try:
|
||||
return gui.toBool(self.value)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@property
|
||||
def value(self) -> typing.Any:
|
||||
@@ -375,9 +379,9 @@ class gui:
|
||||
returns default value instead.
|
||||
This is mainly used for hidden fields, so we have correctly initialized
|
||||
"""
|
||||
if callable(self._data['value']):
|
||||
return self._data['value']()
|
||||
return self._data['value'] if self._data['value'] is not None else self.default
|
||||
if callable(self._data.value):
|
||||
return self._data.value()
|
||||
return self._data.value if self._data.value is not None else self.default
|
||||
|
||||
@value.setter
|
||||
def value(self, value: typing.Any) -> None:
|
||||
@@ -390,7 +394,7 @@ class gui:
|
||||
"""
|
||||
So we can override value setting at descendants
|
||||
"""
|
||||
self._data['value'] = value
|
||||
self._data.value = value
|
||||
|
||||
def guiDescription(self) -> typing.Dict[str, typing.Any]:
|
||||
"""
|
||||
@@ -414,7 +418,7 @@ class gui:
|
||||
"""
|
||||
Returns the default value for this field
|
||||
"""
|
||||
defValue = self._data['default']
|
||||
defValue = self._data.default
|
||||
return defValue() if callable(defValue) else defValue
|
||||
|
||||
@default.setter
|
||||
@@ -428,11 +432,11 @@ class gui:
|
||||
Args:
|
||||
value: Default value (string)
|
||||
"""
|
||||
self._data['default'] = value
|
||||
self._data.default = value
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
return self._data['label']
|
||||
return self._data.label
|
||||
|
||||
def serialize(self) -> str:
|
||||
"""Serialize value to an string"""
|
||||
@@ -444,7 +448,7 @@ class gui:
|
||||
|
||||
@property
|
||||
def required(self) -> bool:
|
||||
return self._data['required']
|
||||
return self._data.required
|
||||
|
||||
def validate(self) -> bool:
|
||||
"""
|
||||
@@ -486,26 +490,13 @@ class gui:
|
||||
# tooltip "Other info", that is not required, that is not
|
||||
# required and that is not editable after creation.
|
||||
other = gui.TextField(length=64, label = _('Other'), order = 1,
|
||||
tooltip = _('Other info'), rdonly = True)
|
||||
tooltip = _('Other info'), readonly = True)
|
||||
|
||||
"""
|
||||
|
||||
class PatternType(enum.StrEnum):
|
||||
IPV4 = 'ipv4'
|
||||
IPV6 = 'ipv6'
|
||||
IP = 'ip'
|
||||
MAC = 'mac'
|
||||
URL = 'url'
|
||||
EMAIL = 'email'
|
||||
FQDN = 'fqdn'
|
||||
HOSTNAME = 'hostname'
|
||||
HOST = 'host'
|
||||
PATH = 'path'
|
||||
NONE = ''
|
||||
|
||||
def __init__(self, **options) -> None:
|
||||
super().__init__(**options, type=types.ui.FieldType.TEXT)
|
||||
self._data['multiline'] = min(max(int(options.get('multiline', 0)), 0), 8)
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs, type=types.ui.FieldType.TEXT)
|
||||
self._data.multiline = min(max(int(kwargs.get('multiline', 0)), 0), 8)
|
||||
# Pattern to validate the value
|
||||
# Can contain an regex or PatternType
|
||||
# - 'ipv4' # IPv4 address
|
||||
@@ -520,9 +511,11 @@ class gui:
|
||||
# - 'path' # Path (absolute or relative, Windows or Unix)
|
||||
# Note:
|
||||
# Checks are performed on admin side, so they are not 100% reliable.
|
||||
self._data['pattern'] = options.get('pattern', gui.TextField.PatternType.NONE)
|
||||
if isinstance(self._data['pattern'], str):
|
||||
self._data['pattern'] = gui.TextField.PatternType(self._data['pattern'])
|
||||
if 'pattern' in kwargs:
|
||||
try:
|
||||
self._data.pattern = types.ui.FieldPatternType(kwargs['pattern'])
|
||||
except ValueError:
|
||||
self._data.pattern = re.compile(kwargs['pattern']) # as regex
|
||||
|
||||
def cleanStr(self):
|
||||
return str(self.value).strip()
|
||||
@@ -531,39 +524,38 @@ class gui:
|
||||
return super().validate() and self._validatePattern()
|
||||
|
||||
def _validatePattern(self) -> bool:
|
||||
thePattern = self._data.pattern
|
||||
if isinstance(thePattern, gui.TextField.PatternType):
|
||||
pattern = self._data.pattern
|
||||
if isinstance(pattern, types.ui.FieldPatternType):
|
||||
try:
|
||||
pattern: gui.TextField.PatternType = thePattern
|
||||
if pattern == gui.TextField.PatternType.IPV4:
|
||||
if pattern == types.ui.FieldPatternType.IPV4:
|
||||
validators.validateIpv4(self.value)
|
||||
elif pattern == gui.TextField.PatternType.IPV6:
|
||||
elif pattern == types.ui.FieldPatternType.IPV6:
|
||||
validators.validateIpv6(self.value)
|
||||
elif pattern == gui.TextField.PatternType.IP:
|
||||
elif pattern == types.ui.FieldPatternType.IP:
|
||||
validators.validateIpv4OrIpv6(self.value)
|
||||
elif pattern == gui.TextField.PatternType.MAC:
|
||||
elif pattern == types.ui.FieldPatternType.MAC:
|
||||
validators.validateMac(self.value)
|
||||
elif pattern == gui.TextField.PatternType.URL:
|
||||
elif pattern == types.ui.FieldPatternType.URL:
|
||||
validators.validateUrl(self.value)
|
||||
elif pattern == gui.TextField.PatternType.EMAIL:
|
||||
elif pattern == types.ui.FieldPatternType.EMAIL:
|
||||
validators.validateEmail(self.value)
|
||||
elif pattern == gui.TextField.PatternType.FQDN:
|
||||
elif pattern == types.ui.FieldPatternType.FQDN:
|
||||
validators.validateFqdn(self.value)
|
||||
elif pattern == gui.TextField.PatternType.HOSTNAME:
|
||||
elif pattern == types.ui.FieldPatternType.HOSTNAME:
|
||||
validators.validateHostname(self.value)
|
||||
elif pattern == gui.TextField.PatternType.HOST:
|
||||
elif pattern == types.ui.FieldPatternType.HOST:
|
||||
try:
|
||||
validators.validateHostname(self.value, allowDomain=True)
|
||||
except exceptions.ValidationError:
|
||||
validators.validateIpv4OrIpv6(self.value)
|
||||
elif pattern == gui.TextField.PatternType.PATH:
|
||||
elif pattern == types.ui.FieldPatternType.PATH:
|
||||
validators.validatePath(self.value)
|
||||
return True
|
||||
except exceptions.ValidationError:
|
||||
return False
|
||||
elif isinstance(thePattern, str):
|
||||
elif isinstance(pattern, str):
|
||||
# It's a regex
|
||||
return re.match(thePattern, self.value) is not None
|
||||
return re.match(pattern, self.value) is not None
|
||||
return True # No pattern, so it's valid
|
||||
|
||||
def __str__(self):
|
||||
@@ -590,13 +582,13 @@ class gui:
|
||||
)
|
||||
kwargs['choices'] = kwargs['values']
|
||||
|
||||
self._data['choices'] = gui.convertToChoices(kwargs.get('choices', []))
|
||||
self._data.choices = gui.convertToChoices(kwargs.get('choices', []))
|
||||
|
||||
def setChoices(self, values: typing.List[str]):
|
||||
def setChoices(self, values: typing.Iterable[typing.Union[str, typing.Dict[str, str]]]):
|
||||
"""
|
||||
Set the values for this choice field
|
||||
"""
|
||||
self._data['choices'] = gui.convertToChoices(values)
|
||||
self._data.choices = gui.convertToChoices(values)
|
||||
|
||||
class NumericField(InputField):
|
||||
"""
|
||||
@@ -622,11 +614,11 @@ class gui:
|
||||
|
||||
def __init__(self, **options):
|
||||
super().__init__(**options, type=types.ui.FieldType.NUMERIC)
|
||||
self._data['minValue'] = int(
|
||||
self._data.minValue = int(
|
||||
# Marker for no min value, no need to be negative, but one not probably used
|
||||
options.get('minValue', options.get('minvalue', '987654321'))
|
||||
)
|
||||
self._data['maxValue'] = int(
|
||||
self._data.maxValue = int(
|
||||
# Marker for no max value, no need to too big, but one not probably used
|
||||
options.get('maxValue', options.get('maxvalue', '987654321'))
|
||||
)
|
||||
@@ -640,10 +632,9 @@ class gui:
|
||||
Return value as integer
|
||||
"""
|
||||
try:
|
||||
v = int(self.value)
|
||||
return int(self.value)
|
||||
except Exception:
|
||||
v = 0
|
||||
return v
|
||||
return 0
|
||||
|
||||
@property
|
||||
def int_value(self) -> int:
|
||||
@@ -789,21 +780,17 @@ class gui:
|
||||
def __init__(self, **options):
|
||||
super().__init__(**options, type=types.ui.FieldType.CHECKBOX)
|
||||
|
||||
@staticmethod
|
||||
def _checkTrue(val: typing.Union[str, bytes, bool]) -> bool:
|
||||
return val in (True, 'true', 'True', b'true', b'True', 1, '1', b'1')
|
||||
|
||||
def _setValue(self, value: typing.Union[str, bytes, bool]):
|
||||
"""
|
||||
Override to set value to True or False (bool)
|
||||
"""
|
||||
self._data['value'] = self._checkTrue(value)
|
||||
self._data.value = gui.toBool(value)
|
||||
|
||||
def isTrue(self):
|
||||
"""
|
||||
Checks that the value is true
|
||||
"""
|
||||
return self.value in (True, 'true', 'True', b'true', b'True')
|
||||
return gui.toBool(self.value)
|
||||
|
||||
def asBool(self) -> bool:
|
||||
"""
|
||||
@@ -890,7 +877,7 @@ class gui:
|
||||
# stuff
|
||||
# ...
|
||||
resourcePool = gui.ChoiceField(
|
||||
label=_("Resource Pool"), rdonly = False, order = 5,
|
||||
label=_("Resource Pool"), readonly = False, order = 5,
|
||||
fills = {
|
||||
'callbackName' : 'vcFillMachinesFromResource',
|
||||
'function' : VCHelpers.getMachines,
|
||||
@@ -908,7 +895,7 @@ class gui:
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs, type=types.ui.FieldType.CHOICE)
|
||||
# And store values in a list
|
||||
if 'values' in kwargs:
|
||||
@@ -921,20 +908,24 @@ class gui:
|
||||
)
|
||||
kwargs['choices'] = kwargs['values']
|
||||
|
||||
self._data['choices'] = gui.convertToChoices(kwargs.get('choices'))
|
||||
self._data.choices = gui.convertToChoices(kwargs.get('choices', []))
|
||||
if 'fills' in kwargs:
|
||||
# Save fnc to register as callback
|
||||
fills = kwargs['fills']
|
||||
fills: types.ui.FillerType = kwargs['fills']
|
||||
if 'function' not in fills or 'callbackName' not in fills:
|
||||
raise ValueError('Invalid fills parameters')
|
||||
fnc = fills['function']
|
||||
fills.pop('function')
|
||||
self._data['fills'] = fills
|
||||
gui.callbacks[fills['callbackName']] = fnc
|
||||
self._data.fills = fills
|
||||
# Readd it only if not already present
|
||||
if fills['callbackName'] not in gui.callbacks:
|
||||
gui.callbacks[fills['callbackName']] = fnc
|
||||
|
||||
def setChoices(self, values: typing.List['types.ui.ChoiceType']):
|
||||
def setChoices(self, values: typing.Iterable[typing.Union[str, typing.Dict[str, str]]]):
|
||||
"""
|
||||
Set the values for this choice field
|
||||
"""
|
||||
self._data['choices'] = values
|
||||
self._data.choices = gui.convertToChoices(values)
|
||||
|
||||
class ImageChoiceField(InputField):
|
||||
def __init__(self, **kwargs):
|
||||
@@ -949,13 +940,13 @@ class gui:
|
||||
)
|
||||
kwargs['choices'] = kwargs['values']
|
||||
|
||||
self._data['choices'] = kwargs.get('choices', [])
|
||||
self._data.choices = gui.convertToChoices(kwargs.get('choices', []))
|
||||
|
||||
def setChoices(self, values: typing.List[typing.Any]):
|
||||
def setChoices(self, values: typing.Iterable[typing.Union[str, typing.Dict[str, str]]]):
|
||||
"""
|
||||
Set the values for this choice field
|
||||
"""
|
||||
self._data['choices'] = values
|
||||
self._data.choices = gui.convertToChoices(values)
|
||||
|
||||
class MultiChoiceField(InputField):
|
||||
"""
|
||||
@@ -983,7 +974,7 @@ class gui:
|
||||
# this field is required and has 2 selectable items: "datastore0"
|
||||
with id "0" and "datastore1" with id "1"
|
||||
datastores = gui.MultiChoiceField(label = _("Datastores"),
|
||||
rdonly = False, rows = 5, order = 8,
|
||||
readonly = False, rows = 5, order = 8,
|
||||
tooltip = _('Datastores where to put incrementals'),
|
||||
required = True,
|
||||
values = [ {'id': '0', 'text': 'datastore0' },
|
||||
@@ -1005,14 +996,14 @@ class gui:
|
||||
|
||||
if kwargs.get('choices') and isinstance(kwargs.get('choices'), dict):
|
||||
kwargs['choices'] = gui.convertToChoices(kwargs['choices'])
|
||||
self._data['choices'] = kwargs.get('choices', [])
|
||||
self._data['rows'] = kwargs.get('rows', -1)
|
||||
self._data.rows = kwargs.get('rows', -1)
|
||||
self._data.choices = gui.convertToChoices(kwargs.get('choices', []))
|
||||
|
||||
def setChoices(self, values: typing.List[typing.Any]) -> None:
|
||||
def setChoices(self, values: typing.Iterable[typing.Union[str, typing.Dict[str, str]]]):
|
||||
"""
|
||||
Set the values for this multi choice field
|
||||
Set the values for this choice field
|
||||
"""
|
||||
self._data['choices'] = gui.convertToChoices(values)
|
||||
self._data.choices = gui.convertToChoices(values)
|
||||
|
||||
class EditableListField(InputField):
|
||||
"""
|
||||
@@ -1141,14 +1132,14 @@ class UserInterface(metaclass=UserInterfaceType):
|
||||
|
||||
# If a field has a callable on defined attributes(value, default, choices)
|
||||
# update the reference to the new copy
|
||||
for attrName, val in self._gui.items(): # And refresh self references to them
|
||||
for _, val in self._gui.items(): # And refresh self references to them
|
||||
# cast _data to dict, so we can check for deveral values
|
||||
data = typing.cast(typing.Dict[str, typing.Any], val._data)
|
||||
for field in ['choices']: # ['value', 'default']:
|
||||
if field in data and callable(data[field]):
|
||||
data[field] = data[field]()
|
||||
# val is an InputField derived instance, so it is a reference to self._gui[key]
|
||||
setattr(self, attrName, val)
|
||||
for field in ['choices', 'value', 'default']: # ['value', 'default']:
|
||||
logger.debug('Checking field %s', field)
|
||||
attr = getattr(val._data, field, None)
|
||||
if attr and callable(attr):
|
||||
# val is an InputField derived instance, so it is a reference to self._gui[key]
|
||||
setattr(val._data, field, attr())
|
||||
|
||||
if values is not None:
|
||||
for k, v in self._gui.items():
|
||||
|
@@ -139,7 +139,7 @@ class EmailMFA(mfas.MFA):
|
||||
|
||||
networks = gui.MultiChoiceField(
|
||||
label=_('Mail OTP Networks'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
rows=5,
|
||||
order=32,
|
||||
tooltip=_('Networks for Email OTP authentication'),
|
||||
|
@@ -120,7 +120,7 @@ class RadiusOTP(mfas.MFA):
|
||||
|
||||
networks = gui.MultiChoiceField(
|
||||
label=_('Radius OTP networks'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
rows=5,
|
||||
order=32,
|
||||
tooltip=_('Networks for Radius OTP authentication'),
|
||||
|
@@ -248,7 +248,7 @@ class SMSMFA(mfas.MFA):
|
||||
|
||||
networks = gui.MultiChoiceField(
|
||||
label=_('SMS networks'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
rows=5,
|
||||
order=32,
|
||||
tooltip=_('Networks for SMS authentication'),
|
||||
|
@@ -72,7 +72,7 @@ class TOTP_MFA(mfas.MFA):
|
||||
order=1,
|
||||
tooltip=_('Issuer for OTP. Once it\'s created it can\'t be changed'),
|
||||
required=True,
|
||||
rdonly=True, # This is not editable, as it is used to generate the QR code. Once generated, it can't be changed
|
||||
readonly=True, # This is not editable, as it is used to generate the QR code. Once generated, it can't be changed
|
||||
)
|
||||
|
||||
validWindow = gui.NumericField(
|
||||
@@ -88,7 +88,7 @@ class TOTP_MFA(mfas.MFA):
|
||||
)
|
||||
networks = gui.MultiChoiceField(
|
||||
label=_('TOTP networks'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
rows=5,
|
||||
order=32,
|
||||
tooltip=_('Users within these networks will not be asked for OTP'),
|
||||
|
@@ -61,7 +61,7 @@ class LinuxOsManager(osmanagers.OSManager):
|
||||
onLogout = gui.ChoiceField(
|
||||
label=_('Logout Action'),
|
||||
order=10,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tooltip=_('What to do when user logs out from service'),
|
||||
choices=[
|
||||
{'id': 'keep', 'text': gettext_lazy('Keep service assigned')},
|
||||
@@ -78,7 +78,7 @@ class LinuxOsManager(osmanagers.OSManager):
|
||||
label=_("Max.Idle time"),
|
||||
length=4,
|
||||
default=-1,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=11,
|
||||
tooltip=_(
|
||||
'Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max idle time).'
|
||||
|
@@ -59,7 +59,7 @@ class TestOSManager(osmanagers.OSManager):
|
||||
onLogout = gui.ChoiceField(
|
||||
label=_('Logout Action'),
|
||||
order=10,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tooltip=_('What to do when user logs out from service'),
|
||||
choices=[
|
||||
{'id': 'keep', 'text': gettext_lazy('Keep service assigned')},
|
||||
@@ -76,7 +76,7 @@ class TestOSManager(osmanagers.OSManager):
|
||||
label=_("Max.Idle time"),
|
||||
length=4,
|
||||
default=-1,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=11,
|
||||
tooltip=_(
|
||||
'Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max. idle time)'
|
||||
|
@@ -52,7 +52,7 @@ class WindowsOsManager(osmanagers.OSManager):
|
||||
onLogout = gui.ChoiceField(
|
||||
label=_('Logout Action'),
|
||||
order=10,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tooltip=_('What to do when user logs out from service'),
|
||||
choices=[
|
||||
{'id': 'keep', 'text': gettext_lazy('Keep service assigned')},
|
||||
@@ -69,7 +69,7 @@ class WindowsOsManager(osmanagers.OSManager):
|
||||
label=_("Max.Idle time"),
|
||||
length=4,
|
||||
default=-1,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=11,
|
||||
tooltip=_(
|
||||
'Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max. idle time)'
|
||||
|
@@ -103,7 +103,7 @@ class OVirtProvider(
|
||||
tooltip=_('oVirt Server Version'),
|
||||
# In this case, the choice can have none value selected by default
|
||||
required=True,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
choices=[
|
||||
gui.choiceItem('4', '4.x'),
|
||||
],
|
||||
@@ -170,7 +170,7 @@ class OVirtProvider(
|
||||
label=_('Macs range'),
|
||||
default='52:54:00:00:00:00-52:54:00:FF:FF:FF',
|
||||
order=91,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tooltip=_('Range of valid macs for UDS managed machines'),
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
|
@@ -120,7 +120,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
|
||||
datastore = gui.ChoiceField(
|
||||
label=_("Datastore Domain"),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=101,
|
||||
tooltip=_('Datastore domain where to publish and put incrementals'),
|
||||
required=True,
|
||||
@@ -149,7 +149,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
length=4,
|
||||
default=512,
|
||||
minValue=0,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=111,
|
||||
tooltip=_('Memory assigned to machines'),
|
||||
tab=_('Machine'),
|
||||
@@ -161,7 +161,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
length=4,
|
||||
default=256,
|
||||
minValue=0,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=112,
|
||||
tooltip=_('Physical memory guaranteed to machines'),
|
||||
tab=_('Machine'),
|
||||
@@ -170,7 +170,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
|
||||
usb = gui.ChoiceField(
|
||||
label=_('USB'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=113,
|
||||
tooltip=_('Enable usb redirection for SPICE'),
|
||||
choices=[
|
||||
@@ -184,7 +184,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
|
||||
display = gui.ChoiceField(
|
||||
label=_('Display'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=114,
|
||||
tooltip=_('Display type (only for administration purposes)'),
|
||||
choices=[gui.choiceItem('spice', 'Spice'), gui.choiceItem('vnc', 'Vnc')],
|
||||
@@ -193,7 +193,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
)
|
||||
baseName = gui.TextField(
|
||||
label=_('Machine Names'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=115,
|
||||
tooltip=_('Base name for clones from this machine'),
|
||||
tab=_('Machine'),
|
||||
|
@@ -150,7 +150,7 @@ class OGService(services.Service):
|
||||
minValue=0,
|
||||
maxValue=99999,
|
||||
default=0,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
tooltip=_('Maximum number of allowed services (0 or less means no limit)'),
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED
|
||||
|
@@ -122,7 +122,7 @@ class LiveService(services.Service):
|
||||
|
||||
baseName = gui.TextField(
|
||||
label=_('Machine Names'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=111,
|
||||
tooltip=_('Base name for clones from this machine'),
|
||||
tab=_('Machine'),
|
||||
|
@@ -111,7 +111,7 @@ class LiveService(services.Service):
|
||||
order=1,
|
||||
tooltip=_('Service region'),
|
||||
required=True,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
)
|
||||
project = gui.ChoiceField(
|
||||
label=_('Project'),
|
||||
@@ -123,7 +123,7 @@ class LiveService(services.Service):
|
||||
},
|
||||
tooltip=_('Project for this service'),
|
||||
required=True,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
)
|
||||
availabilityZone = gui.ChoiceField(
|
||||
label=_('Availability Zones'),
|
||||
@@ -142,7 +142,7 @@ class LiveService(services.Service):
|
||||
},
|
||||
tooltip=_('Service availability zones'),
|
||||
required=True,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
)
|
||||
volume = gui.ChoiceField(
|
||||
label=_('Volume'),
|
||||
@@ -177,7 +177,7 @@ class LiveService(services.Service):
|
||||
|
||||
baseName = gui.TextField(
|
||||
label=_('Machine Names'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=9,
|
||||
tooltip=_('Base name for clones from this machine'),
|
||||
required=True,
|
||||
|
@@ -65,7 +65,7 @@ class IPMachinesService(IPServiceBase):
|
||||
),
|
||||
default='',
|
||||
required=False,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
ipList = gui.EditableListField(
|
||||
|
@@ -139,7 +139,7 @@ class ProxmoxProvider(
|
||||
order=91,
|
||||
tooltip=_('Starting machine id on proxmox'),
|
||||
required=True,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
|
||||
@@ -148,7 +148,7 @@ class ProxmoxProvider(
|
||||
label=_('Macs range'),
|
||||
default='52:54:00:00:00:00-52:54:00:FF:FF:FF',
|
||||
order=91,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
tooltip=_(
|
||||
'Range of valid macs for created machines. Any value accepted by Proxmox is valid here.'
|
||||
),
|
||||
|
@@ -120,7 +120,7 @@ class ProxmoxLinkedService(services.Service): # pylint: disable=too-many-public
|
||||
label=_('HA'),
|
||||
order=2,
|
||||
tooltip=_('Select if HA is enabled and HA group for machines of this service'),
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
guestShutdown = gui.CheckBoxField(
|
||||
@@ -147,7 +147,7 @@ class ProxmoxLinkedService(services.Service): # pylint: disable=too-many-public
|
||||
|
||||
datastore = gui.ChoiceField(
|
||||
label=_("Storage"),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=111,
|
||||
tooltip=_('Storage for publications & machines.'),
|
||||
tab=_('Machine'),
|
||||
@@ -156,7 +156,7 @@ class ProxmoxLinkedService(services.Service): # pylint: disable=too-many-public
|
||||
|
||||
gpu = gui.ChoiceField(
|
||||
label=_("GPU Availability"),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=112,
|
||||
choices={
|
||||
'0': _('Do not check'),
|
||||
@@ -170,7 +170,7 @@ class ProxmoxLinkedService(services.Service): # pylint: disable=too-many-public
|
||||
|
||||
baseName = gui.TextField(
|
||||
label=_('Machine Names'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=115,
|
||||
tooltip=_('Base name for clones from this machine'),
|
||||
tab=_('Machine'),
|
||||
|
@@ -151,7 +151,7 @@ class XenProvider(ServiceProvider): # pylint: disable=too-many-public-methods
|
||||
label=_('Macs range'),
|
||||
default='02:46:00:00:00:00-02:46:00:FF:FF:FF',
|
||||
order=90,
|
||||
rdonly=True,
|
||||
readonly=True,
|
||||
tooltip=_('Range of valid macs for created machines'),
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
|
@@ -104,7 +104,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
# Now the form part
|
||||
datastore = gui.ChoiceField(
|
||||
label=_("Storage SR"),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=100,
|
||||
tooltip=_(
|
||||
'Storage where to publish and put incrementals (only shared storages are supported)'
|
||||
@@ -131,7 +131,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
|
||||
network = gui.ChoiceField(
|
||||
label=_("Network"),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=111,
|
||||
tooltip=_('Network used for virtual machines'),
|
||||
tab=_('Machine'),
|
||||
@@ -142,7 +142,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
label=_("Memory (Mb)"),
|
||||
length=4,
|
||||
default=512,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=112,
|
||||
tooltip=_('Memory assigned to machines'),
|
||||
tab=_('Machine'),
|
||||
@@ -153,7 +153,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
label=_("Shadow"),
|
||||
length=1,
|
||||
default=1,
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=113,
|
||||
tooltip=_('Shadow memory multiplier (use with care)'),
|
||||
tab=_('Machine'),
|
||||
@@ -162,7 +162,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
|
||||
baseName = gui.TextField(
|
||||
label=_('Machine Names'),
|
||||
rdonly=False,
|
||||
readonly=False,
|
||||
order=114,
|
||||
tooltip=_('Base name for clones from this machine'),
|
||||
tab=_('Machine'),
|
||||
|
File diff suppressed because one or more lines are too long
@@ -102,6 +102,6 @@
|
||||
</svg>
|
||||
</div>
|
||||
</uds-root>
|
||||
<script src="/uds/res/admin/runtime.js?stamp=1693324324" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1693324324" type="module"></script><script src="/uds/res/admin/main.js?stamp=1693324324" type="module"></script></body>
|
||||
<script src="/uds/res/admin/runtime.js?stamp=1693445319" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1693445319" type="module"></script><script src="/uds/res/admin/main.js?stamp=1693445319" type="module"></script></body>
|
||||
|
||||
</html>
|
||||
|
@@ -129,7 +129,7 @@ class BaseSpiceTransport(transports.Transport):
|
||||
),
|
||||
required=False,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
pattern=gui.TextField.PatternType.URL,
|
||||
pattern=types.ui.FieldPatternType.URL,
|
||||
)
|
||||
|
||||
overridedProxy = gui.TextField(
|
||||
|
Reference in New Issue
Block a user