mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 21:15:41 +00:00
Merge branch 'inventree:master' into webhooks
This commit is contained in:
@ -61,7 +61,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
single values (e.g. one-off settings values).
|
||||
"""
|
||||
|
||||
GLOBAL_SETTINGS = {}
|
||||
SETTINGS = {}
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@ -73,7 +73,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
self.key = str(self.key).upper()
|
||||
|
||||
self.clean()
|
||||
self.clean(**kwargs)
|
||||
self.validate_unique()
|
||||
|
||||
super().save()
|
||||
@ -90,6 +90,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
results = cls.objects.all()
|
||||
|
||||
# Optionally filter by user
|
||||
if user is not None:
|
||||
results = results.filter(user=user)
|
||||
|
||||
@ -101,13 +102,13 @@ class BaseInvenTreeSetting(models.Model):
|
||||
settings[setting.key.upper()] = setting.value
|
||||
|
||||
# Specify any "default" values which are not in the database
|
||||
for key in cls.GLOBAL_SETTINGS.keys():
|
||||
for key in cls.SETTINGS.keys():
|
||||
|
||||
if key.upper() not in settings:
|
||||
settings[key.upper()] = cls.get_setting_default(key)
|
||||
|
||||
if exclude_hidden:
|
||||
hidden = cls.GLOBAL_SETTINGS[key].get('hidden', False)
|
||||
hidden = cls.SETTINGS[key].get('hidden', False)
|
||||
|
||||
if hidden:
|
||||
# Remove hidden items
|
||||
@ -131,98 +132,92 @@ class BaseInvenTreeSetting(models.Model):
|
||||
return settings
|
||||
|
||||
@classmethod
|
||||
def get_setting_name(cls, key):
|
||||
def get_setting_definition(cls, key, **kwargs):
|
||||
"""
|
||||
Return the 'definition' of a particular settings value, as a dict object.
|
||||
|
||||
- The 'settings' dict can be passed as a kwarg
|
||||
- If not passed, look for cls.SETTINGS
|
||||
- Returns an empty dict if the key is not found
|
||||
"""
|
||||
|
||||
settings = kwargs.get('settings', cls.SETTINGS)
|
||||
|
||||
key = str(key).strip().upper()
|
||||
|
||||
if settings is not None and key in settings:
|
||||
return settings[key]
|
||||
else:
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def get_setting_name(cls, key, **kwargs):
|
||||
"""
|
||||
Return the name of a particular setting.
|
||||
|
||||
If it does not exist, return an empty string.
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
return setting.get('name', '')
|
||||
else:
|
||||
return ''
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
return setting.get('name', '')
|
||||
|
||||
@classmethod
|
||||
def get_setting_description(cls, key):
|
||||
def get_setting_description(cls, key, **kwargs):
|
||||
"""
|
||||
Return the description for a particular setting.
|
||||
|
||||
If it does not exist, return an empty string.
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
return setting.get('description', '')
|
||||
else:
|
||||
return ''
|
||||
return setting.get('description', '')
|
||||
|
||||
@classmethod
|
||||
def get_setting_units(cls, key):
|
||||
def get_setting_units(cls, key, **kwargs):
|
||||
"""
|
||||
Return the units for a particular setting.
|
||||
|
||||
If it does not exist, return an empty string.
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
return setting.get('units', '')
|
||||
else:
|
||||
return ''
|
||||
return setting.get('units', '')
|
||||
|
||||
@classmethod
|
||||
def get_setting_validator(cls, key):
|
||||
def get_setting_validator(cls, key, **kwargs):
|
||||
"""
|
||||
Return the validator for a particular setting.
|
||||
|
||||
If it does not exist, return None
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
return setting.get('validator', None)
|
||||
else:
|
||||
return None
|
||||
return setting.get('validator', None)
|
||||
|
||||
@classmethod
|
||||
def get_setting_default(cls, key):
|
||||
def get_setting_default(cls, key, **kwargs):
|
||||
"""
|
||||
Return the default value for a particular setting.
|
||||
|
||||
If it does not exist, return an empty string
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
return setting.get('default', '')
|
||||
else:
|
||||
return ''
|
||||
return setting.get('default', '')
|
||||
|
||||
@classmethod
|
||||
def get_setting_choices(cls, key):
|
||||
def get_setting_choices(cls, key, **kwargs):
|
||||
"""
|
||||
Return the validator choices available for a particular setting.
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
setting = cls.GLOBAL_SETTINGS[key]
|
||||
choices = setting.get('choices', None)
|
||||
else:
|
||||
choices = None
|
||||
choices = setting.get('choices', None)
|
||||
|
||||
if callable(choices):
|
||||
# Evaluate the function (we expect it will return a list of tuples...)
|
||||
@ -245,17 +240,40 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
key = str(key).strip().upper()
|
||||
|
||||
settings = cls.objects.all()
|
||||
|
||||
# Filter by user
|
||||
user = kwargs.get('user', None)
|
||||
|
||||
if user is not None:
|
||||
settings = settings.filter(user=user)
|
||||
|
||||
try:
|
||||
setting = cls.objects.filter(**cls.get_filters(key, **kwargs)).first()
|
||||
setting = settings.filter(**cls.get_filters(key, **kwargs)).first()
|
||||
except (ValueError, cls.DoesNotExist):
|
||||
setting = None
|
||||
except (IntegrityError, OperationalError):
|
||||
setting = None
|
||||
|
||||
plugin = kwargs.pop('plugin', None)
|
||||
|
||||
if plugin:
|
||||
from plugin import InvenTreePlugin
|
||||
|
||||
if issubclass(plugin.__class__, InvenTreePlugin):
|
||||
plugin = plugin.plugin_config()
|
||||
|
||||
kwargs['plugin'] = plugin
|
||||
|
||||
# Setting does not exist! (Try to create it)
|
||||
if not setting:
|
||||
|
||||
setting = cls(key=key, value=cls.get_setting_default(key), **kwargs)
|
||||
# Attempt to create a new settings object
|
||||
setting = cls(
|
||||
key=key,
|
||||
value=cls.get_setting_default(key, **kwargs),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
try:
|
||||
# Wrap this statement in "atomic", so it can be rolled back if it fails
|
||||
@ -267,21 +285,6 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
return setting
|
||||
|
||||
@classmethod
|
||||
def get_setting_pk(cls, key):
|
||||
"""
|
||||
Return the primary-key value for a given setting.
|
||||
|
||||
If the setting does not exist, return None
|
||||
"""
|
||||
|
||||
setting = cls.get_setting_object(cls)
|
||||
|
||||
if setting:
|
||||
return setting.pk
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_setting(cls, key, backup_value=None, **kwargs):
|
||||
"""
|
||||
@ -291,18 +294,19 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
# If no backup value is specified, atttempt to retrieve a "default" value
|
||||
if backup_value is None:
|
||||
backup_value = cls.get_setting_default(key)
|
||||
backup_value = cls.get_setting_default(key, **kwargs)
|
||||
|
||||
setting = cls.get_setting_object(key, **kwargs)
|
||||
|
||||
if setting:
|
||||
value = setting.value
|
||||
|
||||
# If the particular setting is defined as a boolean, cast the value to a boolean
|
||||
if setting.is_bool():
|
||||
# Cast to boolean if necessary
|
||||
if setting.is_bool(**kwargs):
|
||||
value = InvenTree.helpers.str2bool(value)
|
||||
|
||||
if setting.is_int():
|
||||
# Cast to integer if necessary
|
||||
if setting.is_int(**kwargs):
|
||||
try:
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
@ -365,7 +369,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
def units(self):
|
||||
return self.__class__.get_setting_units(self.key)
|
||||
|
||||
def clean(self):
|
||||
def clean(self, **kwargs):
|
||||
"""
|
||||
If a validator (or multiple validators) are defined for a particular setting key,
|
||||
run them against the 'value' field.
|
||||
@ -373,25 +377,16 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
super().clean()
|
||||
|
||||
validator = self.__class__.get_setting_validator(self.key)
|
||||
validator = self.__class__.get_setting_validator(self.key, **kwargs)
|
||||
|
||||
if self.is_bool():
|
||||
self.value = InvenTree.helpers.str2bool(self.value)
|
||||
|
||||
if self.is_int():
|
||||
try:
|
||||
self.value = int(self.value)
|
||||
except (ValueError):
|
||||
raise ValidationError(_('Must be an integer value'))
|
||||
if validator is not None:
|
||||
self.run_validator(validator)
|
||||
|
||||
options = self.valid_options()
|
||||
|
||||
if options and self.value not in options:
|
||||
raise ValidationError(_("Chosen value is not a valid option"))
|
||||
|
||||
if validator is not None:
|
||||
self.run_validator(validator)
|
||||
|
||||
def run_validator(self, validator):
|
||||
"""
|
||||
Run a validator against the 'value' field for this InvenTreeSetting object.
|
||||
@ -403,7 +398,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
value = self.value
|
||||
|
||||
# Boolean validator
|
||||
if self.is_bool():
|
||||
if validator is bool:
|
||||
# Value must "look like" a boolean value
|
||||
if InvenTree.helpers.is_bool(value):
|
||||
# Coerce into either "True" or "False"
|
||||
@ -414,7 +409,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
})
|
||||
|
||||
# Integer validator
|
||||
if self.is_int():
|
||||
if validator is int:
|
||||
|
||||
try:
|
||||
# Coerce into an integer value
|
||||
@ -467,12 +462,12 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
return [opt[0] for opt in choices]
|
||||
|
||||
def is_bool(self):
|
||||
def is_bool(self, **kwargs):
|
||||
"""
|
||||
Check if this setting is required to be a boolean value
|
||||
"""
|
||||
|
||||
validator = self.__class__.get_setting_validator(self.key)
|
||||
validator = self.__class__.get_setting_validator(self.key, **kwargs)
|
||||
|
||||
return self.__class__.validator_is_bool(validator)
|
||||
|
||||
@ -485,15 +480,15 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
return InvenTree.helpers.str2bool(self.value)
|
||||
|
||||
def setting_type(self):
|
||||
def setting_type(self, **kwargs):
|
||||
"""
|
||||
Return the field type identifier for this setting object
|
||||
"""
|
||||
|
||||
if self.is_bool():
|
||||
if self.is_bool(**kwargs):
|
||||
return 'boolean'
|
||||
|
||||
elif self.is_int():
|
||||
elif self.is_int(**kwargs):
|
||||
return 'integer'
|
||||
|
||||
else:
|
||||
@ -512,12 +507,12 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
return False
|
||||
|
||||
def is_int(self):
|
||||
def is_int(self, **kwargs):
|
||||
"""
|
||||
Check if the setting is required to be an integer value:
|
||||
"""
|
||||
|
||||
validator = self.__class__.get_setting_validator(self.key)
|
||||
validator = self.__class__.get_setting_validator(self.key, **kwargs)
|
||||
|
||||
return self.__class__.validator_is_int(validator)
|
||||
|
||||
@ -549,21 +544,20 @@ class BaseInvenTreeSetting(models.Model):
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def is_protected(cls, key):
|
||||
def is_protected(cls, key, **kwargs):
|
||||
"""
|
||||
Check if the setting value is protected
|
||||
"""
|
||||
|
||||
key = str(key).strip().upper()
|
||||
setting = cls.get_setting_definition(key, **kwargs)
|
||||
|
||||
if key in cls.GLOBAL_SETTINGS:
|
||||
return cls.GLOBAL_SETTINGS[key].get('protected', False)
|
||||
else:
|
||||
return False
|
||||
return setting.get('protected', False)
|
||||
|
||||
|
||||
def settings_group_options():
|
||||
"""build up group tuple for settings based on gour choices"""
|
||||
"""
|
||||
Build up group tuple for settings based on your choices
|
||||
"""
|
||||
return [('', _('No group')), *[(str(a.id), str(a)) for a in Group.objects.all()]]
|
||||
|
||||
|
||||
@ -585,7 +579,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
super().save()
|
||||
|
||||
if self.requires_restart():
|
||||
InvenTreeSetting.set_setting('SERVER_REQUIRES_RESTART', True, None)
|
||||
InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', True, None)
|
||||
|
||||
"""
|
||||
Dict of all global settings values:
|
||||
@ -603,7 +597,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
The keys must be upper-case
|
||||
"""
|
||||
|
||||
GLOBAL_SETTINGS = {
|
||||
SETTINGS = {
|
||||
|
||||
'SERVER_RESTART_REQUIRED': {
|
||||
'name': _('Restart required'),
|
||||
@ -985,13 +979,6 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
'requires_restart': True,
|
||||
},
|
||||
'ENABLE_PLUGINS_GLOBALSETTING': {
|
||||
'name': _('Enable global setting integration'),
|
||||
'description': _('Enable plugins to integrate into inventree global settings'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
'requires_restart': True,
|
||||
},
|
||||
'ENABLE_PLUGINS_APP': {
|
||||
'name': _('Enable app integration'),
|
||||
'description': _('Enable plugins to add apps'),
|
||||
@ -999,6 +986,13 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
'requires_restart': True,
|
||||
},
|
||||
'ENABLE_PLUGINS_SCHEDULE': {
|
||||
'name': _('Enable schedule integration'),
|
||||
'description': _('Enable plugins to run scheduled tasks'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
'requires_restart': True,
|
||||
}
|
||||
}
|
||||
|
||||
class Meta:
|
||||
@ -1025,7 +1019,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
Return True if this setting requires a server restart after changing
|
||||
"""
|
||||
|
||||
options = InvenTreeSetting.GLOBAL_SETTINGS.get(self.key, None)
|
||||
options = InvenTreeSetting.SETTINGS.get(self.key, None)
|
||||
|
||||
if options:
|
||||
return options.get('requires_restart', False)
|
||||
@ -1038,7 +1032,7 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
An InvenTreeSetting object with a usercontext
|
||||
"""
|
||||
|
||||
GLOBAL_SETTINGS = {
|
||||
SETTINGS = {
|
||||
'HOMEPAGE_PART_STARRED': {
|
||||
'name': _('Show subscribed parts'),
|
||||
'description': _('Show subscribed parts on the homepage'),
|
||||
|
@ -50,9 +50,9 @@ class SettingsTest(TestCase):
|
||||
- Ensure that every global setting has a description.
|
||||
"""
|
||||
|
||||
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
|
||||
for key in InvenTreeSetting.SETTINGS.keys():
|
||||
|
||||
setting = InvenTreeSetting.GLOBAL_SETTINGS[key]
|
||||
setting = InvenTreeSetting.SETTINGS[key]
|
||||
|
||||
name = setting.get('name', None)
|
||||
|
||||
@ -65,14 +65,14 @@ class SettingsTest(TestCase):
|
||||
raise ValueError(f'Missing GLOBAL_SETTING description for {key}')
|
||||
|
||||
if not key == key.upper():
|
||||
raise ValueError(f"GLOBAL_SETTINGS key '{key}' is not uppercase")
|
||||
raise ValueError(f"SETTINGS key '{key}' is not uppercase")
|
||||
|
||||
def test_defaults(self):
|
||||
"""
|
||||
Populate the settings with default values
|
||||
"""
|
||||
|
||||
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
|
||||
for key in InvenTreeSetting.SETTINGS.keys():
|
||||
|
||||
value = InvenTreeSetting.get_setting_default(key)
|
||||
|
||||
|
Reference in New Issue
Block a user