mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 21:25:42 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/inventree/InvenTree into plugin-2037
This commit is contained in:
		
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -12,11 +12,31 @@ class SettingsAdmin(ImportExportModelAdmin): | |||||||
|  |  | ||||||
|     list_display = ('key', 'value') |     list_display = ('key', 'value') | ||||||
|  |  | ||||||
|  |     def get_readonly_fields(self, request, obj=None): | ||||||
|  |         """ | ||||||
|  |         Prevent the 'key' field being edited once the setting is created | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         if obj: | ||||||
|  |             return ['key'] | ||||||
|  |         else: | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserSettingsAdmin(ImportExportModelAdmin): | class UserSettingsAdmin(ImportExportModelAdmin): | ||||||
|  |  | ||||||
|     list_display = ('key', 'value', 'user', ) |     list_display = ('key', 'value', 'user', ) | ||||||
|  |  | ||||||
|  |     def get_readonly_fields(self, request, obj=None): | ||||||
|  |         """ | ||||||
|  |         Prevent the 'key' field being edited once the setting is created | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         if obj: | ||||||
|  |             return ['key'] | ||||||
|  |         else: | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|  |  | ||||||
| class WebhookAdmin(ImportExportModelAdmin): | class WebhookAdmin(ImportExportModelAdmin): | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,30 @@ | |||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | import logging | ||||||
|  |  | ||||||
| from django.apps import AppConfig | from django.apps import AppConfig | ||||||
|  |  | ||||||
|  |  | ||||||
|  | logger = logging.getLogger('inventree') | ||||||
|  |  | ||||||
|  |  | ||||||
| class CommonConfig(AppConfig): | class CommonConfig(AppConfig): | ||||||
|     name = 'common' |     name = 'common' | ||||||
|  |  | ||||||
|     def ready(self): |     def ready(self): | ||||||
|         pass |          | ||||||
|  |         self.clear_restart_flag() | ||||||
|  |  | ||||||
|  |     def clear_restart_flag(self): | ||||||
|  |         """ | ||||||
|  |         Clear the SERVER_RESTART_REQUIRED setting | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             import common.models | ||||||
|  |              | ||||||
|  |             if common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'): | ||||||
|  |                 logger.info("Clearing SERVER_RESTART_REQUIRED flag") | ||||||
|  |                 common.models.InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', False, None) | ||||||
|  |         except: | ||||||
|  |             pass | ||||||
|   | |||||||
| @@ -71,13 +71,15 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|         Enforce validation and clean before saving |         Enforce validation and clean before saving | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  |         self.key = str(self.key).upper() | ||||||
|  |  | ||||||
|         self.clean() |         self.clean() | ||||||
|         self.validate_unique() |         self.validate_unique() | ||||||
|  |  | ||||||
|         super().save() |         super().save() | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def allValues(cls, user=None): |     def allValues(cls, user=None, exclude_hidden=False): | ||||||
|         """ |         """ | ||||||
|         Return a dict of "all" defined global settings. |         Return a dict of "all" defined global settings. | ||||||
|  |  | ||||||
| @@ -102,9 +104,15 @@ class BaseInvenTreeSetting(models.Model): | |||||||
|         for key in cls.GLOBAL_SETTINGS.keys(): |         for key in cls.GLOBAL_SETTINGS.keys(): | ||||||
|  |  | ||||||
|             if key.upper() not in settings: |             if key.upper() not in settings: | ||||||
|  |  | ||||||
|                 settings[key.upper()] = cls.get_setting_default(key) |                 settings[key.upper()] = cls.get_setting_default(key) | ||||||
|  |  | ||||||
|  |             if exclude_hidden: | ||||||
|  |                 hidden = cls.GLOBAL_SETTINGS[key].get('hidden', False) | ||||||
|  |  | ||||||
|  |                 if hidden: | ||||||
|  |                     # Remove hidden items | ||||||
|  |                     del settings[key.upper()] | ||||||
|  |  | ||||||
|         for key, value in settings.items(): |         for key, value in settings.items(): | ||||||
|             validator = cls.get_setting_validator(key) |             validator = cls.get_setting_validator(key) | ||||||
|  |  | ||||||
| @@ -568,6 +576,17 @@ class InvenTreeSetting(BaseInvenTreeSetting): | |||||||
|     even if that key does not exist. |     even if that key does not exist. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  |     def save(self, *args, **kwargs): | ||||||
|  |         """ | ||||||
|  |         When saving a global setting, check to see if it requires a server restart. | ||||||
|  |         If so, set the "SERVER_RESTART_REQUIRED" setting to True | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         super().save() | ||||||
|  |  | ||||||
|  |         if self.requires_restart(): | ||||||
|  |             InvenTreeSetting.set_setting('SERVER_REQUIRES_RESTART', True, None) | ||||||
|  |  | ||||||
|     """ |     """ | ||||||
|     Dict of all global settings values: |     Dict of all global settings values: | ||||||
|  |  | ||||||
| @@ -586,6 +605,14 @@ class InvenTreeSetting(BaseInvenTreeSetting): | |||||||
|  |  | ||||||
|     GLOBAL_SETTINGS = { |     GLOBAL_SETTINGS = { | ||||||
|  |  | ||||||
|  |         'SERVER_RESTART_REQUIRED': { | ||||||
|  |             'name': _('Restart required'), | ||||||
|  |             'description': _('A setting has been changed which requires a server restart'), | ||||||
|  |             'default': False, | ||||||
|  |             'validator': bool, | ||||||
|  |             'hidden': True, | ||||||
|  |         }, | ||||||
|  |  | ||||||
|         'INVENTREE_INSTANCE': { |         'INVENTREE_INSTANCE': { | ||||||
|             'name': _('InvenTree Instance Name'), |             'name': _('InvenTree Instance Name'), | ||||||
|             'default': 'InvenTree server', |             'default': 'InvenTree server', | ||||||
| @@ -983,6 +1010,18 @@ class InvenTreeSetting(BaseInvenTreeSetting): | |||||||
|  |  | ||||||
|         return self.__class__.get_setting(self.key) |         return self.__class__.get_setting(self.key) | ||||||
|  |  | ||||||
|  |     def requires_restart(self): | ||||||
|  |         """ | ||||||
|  |         Return True if this setting requires a server restart after changing | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         options = InvenTreeSetting.GLOBAL_SETTINGS.get(self.key, None) | ||||||
|  |  | ||||||
|  |         if options: | ||||||
|  |             return options.get('requires_restart', False) | ||||||
|  |         else: | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvenTreeUserSetting(BaseInvenTreeSetting): | class InvenTreeUserSetting(BaseInvenTreeSetting): | ||||||
|     """ |     """ | ||||||
| @@ -1316,9 +1355,6 @@ def get_price(instance, quantity, moq=True, multiples=True, currency=None, break | |||||||
|  |  | ||||||
| class ColorTheme(models.Model): | class ColorTheme(models.Model): | ||||||
|     """ Color Theme Setting """ |     """ Color Theme Setting """ | ||||||
|  |  | ||||||
|     default_color_theme = ('', _('Default')) |  | ||||||
|  |  | ||||||
|     name = models.CharField(max_length=20, |     name = models.CharField(max_length=20, | ||||||
|                             default='', |                             default='', | ||||||
|                             blank=True) |                             blank=True) | ||||||
| @@ -1338,10 +1374,7 @@ class ColorTheme(models.Model): | |||||||
|         # Get color themes choices (CSS sheets) |         # Get color themes choices (CSS sheets) | ||||||
|         choices = [(file_name.lower(), _(file_name.replace('-', ' ').title())) |         choices = [(file_name.lower(), _(file_name.replace('-', ' ').title())) | ||||||
|                    for file_name, file_ext in files_list |                    for file_name, file_ext in files_list | ||||||
|                    if file_ext == '.css' and file_name.lower() != 'default'] |                    if file_ext == '.css'] | ||||||
|  |  | ||||||
|         # Add default option as empty option |  | ||||||
|         choices.insert(0, cls.default_color_theme) |  | ||||||
|  |  | ||||||
|         return choices |         return choices | ||||||
|  |  | ||||||
|   | |||||||
| @@ -251,6 +251,15 @@ def global_settings(*args, **kwargs): | |||||||
|     return InvenTreeSetting.allValues() |     return InvenTreeSetting.allValues() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @register.simple_tag() | ||||||
|  | def visible_global_settings(*args, **kwargs): | ||||||
|  |     """ | ||||||
|  |     Return any global settings which are not marked as 'hidden' | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     return InvenTreeSetting.allValues(exclude_hidden=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def progress_bar(val, max, *args, **kwargs): | def progress_bar(val, max, *args, **kwargs): | ||||||
|     """ |     """ | ||||||
| @@ -292,6 +301,19 @@ def progress_bar(val, max, *args, **kwargs): | |||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def get_color_theme_css(username): | def get_color_theme_css(username): | ||||||
|  |     user_theme_name = get_user_color_theme(username) | ||||||
|  |     # Build path to CSS sheet | ||||||
|  |     inventree_css_sheet = os.path.join('css', 'color-themes', user_theme_name + '.css') | ||||||
|  |  | ||||||
|  |     # Build static URL | ||||||
|  |     inventree_css_static_url = os.path.join(settings.STATIC_URL, inventree_css_sheet) | ||||||
|  |  | ||||||
|  |     return inventree_css_static_url | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @register.simple_tag() | ||||||
|  | def get_user_color_theme(username): | ||||||
|  |     """ Get current user color theme """ | ||||||
|     try: |     try: | ||||||
|         user_theme = ColorTheme.objects.filter(user=username).get() |         user_theme = ColorTheme.objects.filter(user=username).get() | ||||||
|         user_theme_name = user_theme.name |         user_theme_name = user_theme.name | ||||||
| @@ -300,13 +322,7 @@ def get_color_theme_css(username): | |||||||
|     except ColorTheme.DoesNotExist: |     except ColorTheme.DoesNotExist: | ||||||
|         user_theme_name = 'default' |         user_theme_name = 'default' | ||||||
|  |  | ||||||
|     # Build path to CSS sheet |     return user_theme_name | ||||||
|     inventree_css_sheet = os.path.join('css', 'color-themes', user_theme_name + '.css') |  | ||||||
|  |  | ||||||
|     # Build static URL |  | ||||||
|     inventree_css_static_url = os.path.join(settings.STATIC_URL, inventree_css_sheet) |  | ||||||
|  |  | ||||||
|     return inventree_css_static_url |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
|   | |||||||
| @@ -170,35 +170,6 @@ | |||||||
|  |  | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  |  | ||||||
| <div class='panel-heading'> |  | ||||||
|     <h4>{% trans "Theme Settings" %}</h4> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| <div class='row'> |  | ||||||
|  |  | ||||||
|     <div class='col-sm-6'> |  | ||||||
|         <form action='{% url "settings-appearance" %}' method='post'> |  | ||||||
|             {% csrf_token %} |  | ||||||
|             <input name='next' type='hidden' value='{% url "settings" %}'> |  | ||||||
|             <label for='theme' class=' requiredField'> |  | ||||||
|                 {% trans "Select theme" %} |  | ||||||
|             </label> |  | ||||||
|             <div class='form-group input-group mb-3'> |  | ||||||
|                 <select id='theme' name='theme' class='select form-control'> |  | ||||||
|                     {% get_available_themes as themes %} |  | ||||||
|                     {% for theme in themes %} |  | ||||||
|                     <option value='{{ theme.key }}'>{{ theme.name }}</option> |  | ||||||
|                     {% endfor %} |  | ||||||
|                 </select> |  | ||||||
|                 <div class='input-group-append'> |  | ||||||
|                     <input type="submit" value="{% trans 'Set Theme' %}" class="btn btn-primary"> |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|         </form> |  | ||||||
|     </div> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| <div class='panel-heading'> | <div class='panel-heading'> | ||||||
|     <h4>{% trans "Language Settings" %}</h4> |     <h4>{% trans "Language Settings" %}</h4> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -21,4 +21,33 @@ | |||||||
|     </table> |     </table> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | <div class='panel-heading'> | ||||||
|  |     <h4>{% trans "Theme Settings" %}</h4> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | <div class='row'> | ||||||
|  |  | ||||||
|  |     <div class='col-sm-6'> | ||||||
|  |         <form action='{% url "settings-appearance" %}' method='post'> | ||||||
|  |             {% csrf_token %} | ||||||
|  |             <input name='next' type='hidden' value='{% url "settings" %}'> | ||||||
|  |             <label for='theme' class=' requiredField'> | ||||||
|  |                 {% trans "Select theme" %} | ||||||
|  |             </label> | ||||||
|  |             <div class='form-group input-group mb-3'> | ||||||
|  |                 <select id='theme' name='theme' class='select form-control'> | ||||||
|  |                     {% get_available_themes as themes %} | ||||||
|  |                     {% get_user_color_theme request.user.username as user_theme %} | ||||||
|  |                     {% for theme in themes %} | ||||||
|  |                     <option value='{{ theme.key }}'{% if theme.key == user_theme %} selected{% endif%}>{{ theme.name }}</option> | ||||||
|  |                     {% endfor %} | ||||||
|  |                 </select> | ||||||
|  |                 <div class='input-group-append'> | ||||||
|  |                     <input type="submit" value="{% trans 'Set Theme' %}" class="btn btn-primary"> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
| @@ -5,6 +5,7 @@ | |||||||
| {% settings_value 'BARCODE_ENABLE' as barcodes %} | {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||||
| {% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %} | {% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %} | ||||||
| {% settings_value "REPORT_ENABLE" as report_enabled %} | {% settings_value "REPORT_ENABLE" as report_enabled %} | ||||||
|  | {% settings_value "SERVER_RESTART_REQUIRED" as server_restart_required %} | ||||||
|  |  | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| @@ -86,6 +87,21 @@ | |||||||
|         </div> |         </div> | ||||||
|         <main class='col ps-md-2 pt-2 pe-2'> |         <main class='col ps-md-2 pt-2 pe-2'> | ||||||
|  |  | ||||||
|  |             {% if server_restart_required %} | ||||||
|  |             <div class='notification-area' id='restart-required'> | ||||||
|  |                 <div id='alert-restart-server' class='alert alert-danger' role='alert'> | ||||||
|  |                     <span class='fas fa-server'></span> | ||||||
|  |                     <b>{% trans "Server Restart Required" %}</b> | ||||||
|  |                     <small> | ||||||
|  |                         <br> | ||||||
|  |                         {% trans "A configuration option has been changed which requires a server restart" %}. | ||||||
|  |                         <br> | ||||||
|  |                         {% trans "Contact your system administrator for further information" %} | ||||||
|  |                     </small> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             {% endif %} | ||||||
|  |  | ||||||
|             {% block alerts %} |             {% block alerts %} | ||||||
|             <div class='notification-area' id='alerts'> |             <div class='notification-area' id='alerts'> | ||||||
|                 <!-- Div for displayed alerts --> |                 <!-- Div for displayed alerts --> | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ const user_settings = { | |||||||
|     {% endfor %} |     {% endfor %} | ||||||
| }; | }; | ||||||
|  |  | ||||||
| {% global_settings as GLOBAL_SETTINGS %} | {% visible_global_settings as GLOBAL_SETTINGS %} | ||||||
| const global_settings = { | const global_settings = { | ||||||
|     {% for key, value in GLOBAL_SETTINGS.items %} |     {% for key, value in GLOBAL_SETTINGS.items %} | ||||||
|     {{ key }}: {% primitive_to_javascript value %}, |     {{ key }}: {% primitive_to_javascript value %}, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user