mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[WIP] Site ID Fixes (#6390)
* Fix docs for INVENTREE_SITE_URL * Adjust default SITE_ID * Optional support for multi-site - Disable by default * Prevent site setting from being changed if set by config parameter * Update site url setting on server launch * Update log messages * Update RULESET_MODELS * Update unit tests * More fixes for unit tests * Update docs * Update SSO image
This commit is contained in:
parent
538ff9be7b
commit
5bc00298c6
@ -58,6 +58,7 @@ class InvenTreeConfig(AppConfig):
|
||||
# Let the background worker check for migrations
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.check_for_migrations)
|
||||
|
||||
self.update_site_url()
|
||||
self.collect_notification_methods()
|
||||
self.collect_state_transition_methods()
|
||||
|
||||
@ -223,6 +224,46 @@ class InvenTreeConfig(AppConfig):
|
||||
except Exception as e:
|
||||
logger.exception('Error updating exchange rates: %s (%s)', e, type(e))
|
||||
|
||||
def update_site_url(self):
|
||||
"""Update the site URL setting.
|
||||
|
||||
- If a fixed SITE_URL is specified (via configuration), it should override the INVENTREE_BASE_URL setting
|
||||
- If multi-site support is enabled, update the site URL for the current site
|
||||
"""
|
||||
import common.models
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase():
|
||||
return
|
||||
|
||||
if InvenTree.ready.isImportingData() or InvenTree.ready.isRunningMigrations():
|
||||
return
|
||||
|
||||
if settings.SITE_URL:
|
||||
try:
|
||||
if (
|
||||
common.models.InvenTreeSetting.get_setting('INVENTREE_BASE_URL')
|
||||
!= settings.SITE_URL
|
||||
):
|
||||
common.models.InvenTreeSetting.set_setting(
|
||||
'INVENTREE_BASE_URL', settings.SITE_URL
|
||||
)
|
||||
logger.info('Updated INVENTREE_SITE_URL to %s', settings.SITE_URL)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If multi-site support is enabled, update the site URL for the current site
|
||||
try:
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
site = Site.objects.get_current()
|
||||
site.domain = settings.SITE_URL
|
||||
site.save()
|
||||
|
||||
logger.info('Updated current site URL to %s', settings.SITE_URL)
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def add_user_on_startup(self):
|
||||
"""Add a user on startup."""
|
||||
# stop if checks were already created
|
||||
|
@ -72,7 +72,7 @@ def user_roles(request):
|
||||
|
||||
roles = {}
|
||||
|
||||
for role in RuleSet.RULESET_MODELS.keys():
|
||||
for role in RuleSet.get_ruleset_models().keys():
|
||||
permissions = {}
|
||||
|
||||
for perm in ['view', 'add', 'change', 'delete']:
|
||||
|
@ -6,7 +6,6 @@ from urllib.parse import urlencode
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -23,6 +22,7 @@ from crispy_forms.layout import Field, Layout
|
||||
from dj_rest_auth.registration.serializers import RegisterSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
import InvenTree.helpers_model
|
||||
import InvenTree.sso
|
||||
from common.models import InvenTreeSetting
|
||||
from InvenTree.exceptions import log_error
|
||||
@ -293,7 +293,8 @@ class CustomUrlMixin:
|
||||
def get_email_confirmation_url(self, request, emailconfirmation):
|
||||
"""Custom email confirmation (activation) url."""
|
||||
url = reverse('account_confirm_email', args=[emailconfirmation.key])
|
||||
return Site.objects.get_current().domain + url
|
||||
|
||||
return InvenTree.helpers_model.construct_absolute_url(url)
|
||||
|
||||
|
||||
class CustomAccountAdapter(
|
||||
|
@ -34,47 +34,59 @@ def getSetting(key, backup_value=None):
|
||||
return common.models.InvenTreeSetting.get_setting(key, backup_value=backup_value)
|
||||
|
||||
|
||||
def construct_absolute_url(*arg, **kwargs):
|
||||
"""Construct (or attempt to construct) an absolute URL from a relative URL.
|
||||
def get_base_url(request=None):
|
||||
"""Return the base URL for the InvenTree server.
|
||||
|
||||
This is useful when (for example) sending an email to a user with a link
|
||||
to something in the InvenTree web framework.
|
||||
A URL is constructed in the following order:
|
||||
1. If settings.SITE_URL is set (e.g. in the Django settings), use that
|
||||
2. If the InvenTree setting INVENTREE_BASE_URL is set, use that
|
||||
3. Otherwise, use the current request URL (if available)
|
||||
The base URL is determined in the following order of decreasing priority:
|
||||
|
||||
1. If a request object is provided, use the request URL
|
||||
2. Multi-site is enabled, and the current site has a valid URL
|
||||
3. If settings.SITE_URL is set (e.g. in the Django settings), use that
|
||||
4. If the InvenTree setting INVENTREE_BASE_URL is set, use that
|
||||
"""
|
||||
relative_url = '/'.join(arg)
|
||||
# Check if a request is provided
|
||||
if request:
|
||||
return request.build_absolute_uri('/')
|
||||
|
||||
# If a site URL is provided, use that
|
||||
site_url = getattr(settings, 'SITE_URL', None)
|
||||
|
||||
if not site_url:
|
||||
# Otherwise, try to use the InvenTree setting
|
||||
# Check if multi-site is enabled
|
||||
try:
|
||||
site_url = common.models.InvenTreeSetting.get_setting(
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
return Site.objects.get_current().domain
|
||||
except (ImportError, RuntimeError):
|
||||
pass
|
||||
|
||||
# Check if a global site URL is provided
|
||||
if site_url := getattr(settings, 'SITE_URL', None):
|
||||
return site_url
|
||||
|
||||
# Check if a global InvenTree setting is provided
|
||||
try:
|
||||
if site_url := common.models.InvenTreeSetting.get_setting(
|
||||
'INVENTREE_BASE_URL', create=False, cache=False
|
||||
)
|
||||
):
|
||||
return site_url
|
||||
except (ProgrammingError, OperationalError):
|
||||
pass
|
||||
|
||||
if not site_url:
|
||||
# Otherwise, try to use the current request
|
||||
request = kwargs.get('request', None)
|
||||
|
||||
if request:
|
||||
site_url = request.build_absolute_uri('/')
|
||||
|
||||
if not site_url:
|
||||
# No site URL available, return the relative URL
|
||||
return relative_url
|
||||
|
||||
return urljoin(site_url, relative_url)
|
||||
# No base URL available
|
||||
return ''
|
||||
|
||||
|
||||
def get_base_url(**kwargs):
|
||||
"""Return the base URL for the InvenTree server."""
|
||||
return construct_absolute_url('', **kwargs)
|
||||
def construct_absolute_url(*arg, base_url=None, request=None):
|
||||
"""Construct (or attempt to construct) an absolute URL from a relative URL.
|
||||
|
||||
Args:
|
||||
*arg: The relative URL to construct
|
||||
base_url: The base URL to use for the construction (if not provided, will attempt to determine from settings)
|
||||
request: The request object to use for the construction (optional)
|
||||
"""
|
||||
relative_url = '/'.join(arg)
|
||||
|
||||
if not base_url:
|
||||
base_url = get_base_url(request=request)
|
||||
|
||||
return urljoin(base_url, relative_url)
|
||||
|
||||
|
||||
def download_image_from_url(remote_url, timeout=2.5):
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.mail import send_mail
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
@ -13,18 +12,20 @@ from rest_framework import serializers
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
import InvenTree.version
|
||||
|
||||
|
||||
def send_simple_login_email(user, link):
|
||||
"""Send an email with the login link to this user."""
|
||||
site = Site.objects.get_current()
|
||||
site_name = InvenTree.version.inventreeInstanceName()
|
||||
|
||||
context = {'username': user.username, 'site_name': site.name, 'link': link}
|
||||
context = {'username': user.username, 'site_name': site_name, 'link': link}
|
||||
email_plaintext_message = render_to_string(
|
||||
'InvenTree/user_simple_login.txt', context
|
||||
)
|
||||
|
||||
send_mail(
|
||||
_(f'[{site.name}] Log in to the app'),
|
||||
_(f'[{site_name}] Log in to the app'),
|
||||
email_plaintext_message,
|
||||
settings.DEFAULT_FROM_EMAIL,
|
||||
[user.email],
|
||||
|
@ -7,7 +7,6 @@ from decimal import Decimal
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -26,7 +25,7 @@ from taggit.serializers import TaggitSerializer
|
||||
import common.models as common_models
|
||||
from common.settings import currency_code_default, currency_code_mappings
|
||||
from InvenTree.fields import InvenTreeRestURLField, InvenTreeURLField
|
||||
from InvenTree.helpers_model import download_image_from_url
|
||||
from InvenTree.helpers_model import download_image_from_url, get_base_url
|
||||
|
||||
|
||||
class InvenTreeMoneySerializer(MoneyField):
|
||||
@ -445,19 +444,23 @@ class UserCreateSerializer(ExendedUserSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Send an e email to the user after creation."""
|
||||
base_url = get_base_url()
|
||||
|
||||
instance = super().create(validated_data)
|
||||
|
||||
# Make sure the user cannot login until they have set a password
|
||||
instance.set_unusable_password()
|
||||
# Send the user an onboarding email (from current site)
|
||||
current_site = Site.objects.get_current()
|
||||
domain = current_site.domain
|
||||
instance.email_user(
|
||||
subject=_(f'Welcome to {current_site.name}'),
|
||||
|
||||
message = _(
|
||||
f'Your account has been created.\n\nPlease use the password reset function to get access (at https://{domain}).'
|
||||
),
|
||||
'Your account has been created.\n\nPlease use the password reset function to login'
|
||||
)
|
||||
|
||||
if base_url:
|
||||
message += f'\nURL: {base_url}'
|
||||
|
||||
# Send the user an onboarding email (from current site)
|
||||
instance.email_user(subject=_('Welcome to InvenTree'), message=message)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
|
@ -978,13 +978,30 @@ CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
# Use database transactions when importing / exporting data
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
|
||||
SITE_ID = 1
|
||||
# Site URL can be specified statically, or via a run-time setting
|
||||
SITE_URL = get_setting('INVENTREE_SITE_URL', 'site_url', None)
|
||||
|
||||
if SITE_URL:
|
||||
logger.info('Using Site URL: %s', SITE_URL)
|
||||
|
||||
# Check that the site URL is valid
|
||||
validator = URLValidator()
|
||||
validator(SITE_URL)
|
||||
|
||||
# Enable or disable multi-site framework
|
||||
SITE_MULTI = get_boolean_setting('INVENTREE_SITE_MULTI', 'site_multi', False)
|
||||
|
||||
# If a SITE_ID is specified
|
||||
SITE_ID = get_setting('INVENTREE_SITE_ID', 'site_id', 1 if SITE_MULTI else None)
|
||||
|
||||
# Load the allauth social backends
|
||||
SOCIAL_BACKENDS = get_setting(
|
||||
'INVENTREE_SOCIAL_BACKENDS', 'social_backends', [], typecast=list
|
||||
)
|
||||
|
||||
if not SITE_MULTI:
|
||||
INSTALLED_APPS.remove('django.contrib.sites')
|
||||
|
||||
for app in SOCIAL_BACKENDS:
|
||||
# Ensure that the app starts with 'allauth.socialaccount.providers'
|
||||
social_prefix = 'allauth.socialaccount.providers.'
|
||||
@ -1096,16 +1113,6 @@ PLUGIN_RETRY = get_setting(
|
||||
) # How often should plugin loading be tried?
|
||||
PLUGIN_FILE_CHECKED = False # Was the plugin file checked?
|
||||
|
||||
# Site URL can be specified statically, or via a run-time setting
|
||||
SITE_URL = get_setting('INVENTREE_SITE_URL', 'site_url', None)
|
||||
|
||||
if SITE_URL:
|
||||
logger.info('Site URL: %s', SITE_URL)
|
||||
|
||||
# Check that the site URL is valid
|
||||
validator = URLValidator()
|
||||
validator(SITE_URL)
|
||||
|
||||
# User interface customization values
|
||||
CUSTOM_LOGO = get_custom_file(
|
||||
'INVENTREE_CUSTOM_LOGO', 'customize.logo', 'custom logo', lookup_media=True
|
||||
|
@ -10,7 +10,6 @@ from unittest import mock
|
||||
import django.core.exceptions as django_exceptions
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core import mail
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase, override_settings
|
||||
@ -372,7 +371,7 @@ class TestHelpers(TestCase):
|
||||
for url, expected in tests.items():
|
||||
# Test with supplied base URL
|
||||
self.assertEqual(
|
||||
InvenTree.helpers_model.construct_absolute_url(url, site_url=base),
|
||||
InvenTree.helpers_model.construct_absolute_url(url, base_url=base),
|
||||
expected,
|
||||
)
|
||||
|
||||
@ -1049,6 +1048,12 @@ class TestInstanceName(InvenTreeTestCase):
|
||||
|
||||
self.assertEqual(version.inventreeInstanceTitle(), 'Testing title')
|
||||
|
||||
try:
|
||||
from django.contrib.sites.models import Site
|
||||
except (ImportError, RuntimeError):
|
||||
# Multi-site support not enabled
|
||||
return
|
||||
|
||||
# The site should also be changed
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
self.assertEqual(site_obj.name, 'Testing title')
|
||||
@ -1060,9 +1065,18 @@ class TestInstanceName(InvenTreeTestCase):
|
||||
'INVENTREE_BASE_URL', 'http://127.1.2.3', self.user
|
||||
)
|
||||
|
||||
# No further tests if multi-site support is not enabled
|
||||
if not settings.SITE_MULTI:
|
||||
return
|
||||
|
||||
# The site should also be changed
|
||||
try:
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
self.assertEqual(site_obj.domain, 'http://127.1.2.3')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class TestOffloadTask(InvenTreeTestCase):
|
||||
@ -1234,7 +1248,7 @@ class MagicLoginTest(InvenTreeTestCase):
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.data, {'status': 'ok'})
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, '[example.com] Log in to the app')
|
||||
self.assertEqual(mail.outbox[0].subject, '[InvenTree] Log in to the app')
|
||||
|
||||
# Check that the token is in the email
|
||||
self.assertTrue('http://testserver/api/email/login/' in mail.outbox[0].body)
|
||||
|
@ -24,7 +24,6 @@ from django.contrib.auth.models import Group, User
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import AppRegistryNotReady, ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator, URLValidator
|
||||
@ -101,6 +100,10 @@ class BaseURLValidator(URLValidator):
|
||||
"""Make sure empty values pass."""
|
||||
value = str(value).strip()
|
||||
|
||||
# If a configuration level value has been specified, prevent change
|
||||
if settings.SITE_URL:
|
||||
raise ValidationError(_('Site URL is locked by configuration'))
|
||||
|
||||
if len(value) == 0:
|
||||
pass
|
||||
|
||||
@ -647,7 +650,7 @@ class BaseInvenTreeSetting(models.Model):
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def set_setting(cls, key, value, change_user, create=True, **kwargs):
|
||||
def set_setting(cls, key, value, change_user=None, create=True, **kwargs):
|
||||
"""Set the value of a particular setting. If it does not exist, option to create it.
|
||||
|
||||
Args:
|
||||
@ -1065,6 +1068,15 @@ def settings_group_options():
|
||||
|
||||
def update_instance_url(setting):
|
||||
"""Update the first site objects domain to url."""
|
||||
if not settings.SITE_MULTI:
|
||||
return
|
||||
|
||||
try:
|
||||
from django.contrib.sites.models import Site
|
||||
except (ImportError, RuntimeError):
|
||||
# Multi-site support not enabled
|
||||
return
|
||||
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
site_obj.domain = setting.value
|
||||
site_obj.save()
|
||||
@ -1072,6 +1084,15 @@ def update_instance_url(setting):
|
||||
|
||||
def update_instance_name(setting):
|
||||
"""Update the first site objects name to instance name."""
|
||||
if not settings.SITE_MULTI:
|
||||
return
|
||||
|
||||
try:
|
||||
from django.contrib.sites.models import Site
|
||||
except (ImportError, RuntimeError):
|
||||
# Multi-site support not enabled
|
||||
return
|
||||
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
site_obj.name = setting.value
|
||||
site_obj.save()
|
||||
|
@ -90,8 +90,8 @@ language: en-us
|
||||
timezone: UTC
|
||||
|
||||
# Base URL for the InvenTree server
|
||||
# Use the environment variable INVENTREE_BASE_URL
|
||||
# base_url: 'http://localhost:8000'
|
||||
# Use the environment variable INVENTREE_SITE_URL
|
||||
# site_url: 'http://localhost:8000'
|
||||
|
||||
# Base currency code (or use env var INVENTREE_BASE_CURRENCY)
|
||||
base_currency: USD
|
||||
|
@ -201,7 +201,10 @@ class RuleSet(models.Model):
|
||||
|
||||
RULESET_PERMISSIONS = ['view', 'add', 'change', 'delete']
|
||||
|
||||
RULESET_MODELS = {
|
||||
@staticmethod
|
||||
def get_ruleset_models():
|
||||
"""Return a dictionary of models associated with each ruleset."""
|
||||
ruleset_models = {
|
||||
'admin': [
|
||||
'auth_group',
|
||||
'auth_user',
|
||||
@ -215,7 +218,6 @@ class RuleSet(models.Model):
|
||||
'report_salesorderreport',
|
||||
'account_emailaddress',
|
||||
'account_emailconfirmation',
|
||||
'sites_site',
|
||||
'socialaccount_socialaccount',
|
||||
'socialaccount_socialapp',
|
||||
'socialaccount_socialtoken',
|
||||
@ -325,8 +327,16 @@ class RuleSet(models.Model):
|
||||
],
|
||||
}
|
||||
|
||||
if settings.SITE_MULTI:
|
||||
ruleset_models['admin'].append('sites_site')
|
||||
|
||||
return ruleset_models
|
||||
|
||||
# Database models we ignore permission sets for
|
||||
RULESET_IGNORE = [
|
||||
@staticmethod
|
||||
def get_ruleset_ignore():
|
||||
"""Return a list of database tables which do not require permissions."""
|
||||
return [
|
||||
# Core django models (not user configurable)
|
||||
'admin_logentry',
|
||||
'contenttypes_contenttype',
|
||||
@ -409,12 +419,12 @@ class RuleSet(models.Model):
|
||||
return True
|
||||
|
||||
# If the table does *not* require permissions
|
||||
if table in cls.RULESET_IGNORE:
|
||||
if table in cls.get_ruleset_ignore():
|
||||
return True
|
||||
|
||||
# Work out which roles touch the given table
|
||||
for role in cls.RULESET_NAMES:
|
||||
if table in cls.RULESET_MODELS[role]:
|
||||
if table in cls.get_ruleset_models()[role]:
|
||||
if check_user_role(user, role, permission):
|
||||
return True
|
||||
|
||||
@ -474,7 +484,7 @@ class RuleSet(models.Model):
|
||||
|
||||
def get_models(self):
|
||||
"""Return the database tables / models that this ruleset covers."""
|
||||
return self.RULESET_MODELS.get(self.name, [])
|
||||
return self.get_ruleset_models().get(self.name, [])
|
||||
|
||||
|
||||
def split_model(model):
|
||||
@ -669,7 +679,7 @@ def clear_user_role_cache(user):
|
||||
Args:
|
||||
user: The User object to be expunged from the cache
|
||||
"""
|
||||
for role in RuleSet.RULESET_MODELS.keys():
|
||||
for role in RuleSet.get_ruleset_models().keys():
|
||||
for perm in ['add', 'change', 'view', 'delete']:
|
||||
key = f'role_{user}_{role}_{perm}'
|
||||
cache.delete(key)
|
||||
|
@ -14,7 +14,7 @@ class RuleSetModelTest(TestCase):
|
||||
|
||||
def test_ruleset_models(self):
|
||||
"""Test that the role rulesets work as intended."""
|
||||
keys = RuleSet.RULESET_MODELS.keys()
|
||||
keys = RuleSet.get_ruleset_models().keys()
|
||||
|
||||
# Check if there are any rulesets which do not have models defined
|
||||
|
||||
@ -30,16 +30,16 @@ class RuleSetModelTest(TestCase):
|
||||
|
||||
if len(extra) > 0: # pragma: no cover
|
||||
print(
|
||||
'The following rulesets have been improperly added to RULESET_MODELS:'
|
||||
'The following rulesets have been improperly added to get_ruleset_models():'
|
||||
)
|
||||
for e in extra:
|
||||
print('-', e)
|
||||
|
||||
# Check that each ruleset has models assigned
|
||||
empty = [key for key in keys if len(RuleSet.RULESET_MODELS[key]) == 0]
|
||||
empty = [key for key in keys if len(RuleSet.get_ruleset_models()[key]) == 0]
|
||||
|
||||
if len(empty) > 0: # pragma: no cover
|
||||
print('The following rulesets have empty entries in RULESET_MODELS:')
|
||||
print('The following rulesets have empty entries in get_ruleset_models():')
|
||||
for e in empty:
|
||||
print('-', e)
|
||||
|
||||
@ -62,8 +62,8 @@ class RuleSetModelTest(TestCase):
|
||||
assigned_models = set()
|
||||
|
||||
# Now check that each defined model is a valid table name
|
||||
for key in RuleSet.RULESET_MODELS.keys():
|
||||
models = RuleSet.RULESET_MODELS[key]
|
||||
for key in RuleSet.get_ruleset_models().keys():
|
||||
models = RuleSet.get_ruleset_models()[key]
|
||||
|
||||
for m in models:
|
||||
assigned_models.add(m)
|
||||
@ -72,7 +72,8 @@ class RuleSetModelTest(TestCase):
|
||||
|
||||
for model in available_tables:
|
||||
if (
|
||||
model not in assigned_models and model not in RuleSet.RULESET_IGNORE
|
||||
model not in assigned_models
|
||||
and model not in RuleSet.get_ruleset_ignore()
|
||||
): # pragma: no cover
|
||||
missing_models.add(model)
|
||||
|
||||
@ -90,7 +91,7 @@ class RuleSetModelTest(TestCase):
|
||||
for model in assigned_models:
|
||||
defined_models.add(model)
|
||||
|
||||
for model in RuleSet.RULESET_IGNORE:
|
||||
for model in RuleSet.get_ruleset_ignore():
|
||||
defined_models.add(model)
|
||||
|
||||
for model in defined_models: # pragma: no cover
|
||||
@ -118,7 +119,7 @@ class RuleSetModelTest(TestCase):
|
||||
# Check that all permissions have been assigned permissions?
|
||||
permission_set = set()
|
||||
|
||||
for models in RuleSet.RULESET_MODELS.values():
|
||||
for models in RuleSet.get_ruleset_models().values():
|
||||
for model in models:
|
||||
permission_set.add(model)
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 84 KiB |
@ -88,3 +88,15 @@ This can be used to track usage and performance of the InvenTree backend and con
|
||||
| `tracing.append_http` | `INVENTREE_TRACING_APPEND_HTTP` | Append default url routes (v1) to `tracing.endpoint` |
|
||||
| `tracing.console` | `INVENTREE_TRACING_CONSOLE` | Print out all exports (additionally) to the console for debugging. Do not use in production |
|
||||
| `tracing.resources` | `INVENTREE_TRACING_RESOURCES` | Add additional resources to all exports. This can be used to add custom tags to the traces. Format as a dict. |
|
||||
|
||||
## Multi Site Support
|
||||
|
||||
If your InvenTree instance is used in a multi-site environment, you can enable multi-site support. Note that supporting multiple sites is well outside the scope of most InvenTree installations. If you know what you are doing, and have a good reason to enable multi-site support, you can do so by setting the `INVENTREE_SITE_MULTI` environment variable to `True`.
|
||||
|
||||
!!! tip "Django Documentation"
|
||||
For more information on multi-site support, refer to the [Django documentation](https://docs.djangoproject.com/en/3.2/ref/contrib/sites/).
|
||||
|
||||
| Environment Variable | Config Key | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SITE_MULTI | site_multi | Enable multiple sites | False |
|
||||
| INVENTREE_SITE_ID | site_id | Specify a fixed site ID | *Not specified* |
|
||||
|
@ -55,6 +55,7 @@ The following basic options are available:
|
||||
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
|
||||
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
|
||||
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
|
||||
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) | True |
|
||||
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
|
||||
| INVENTREE_LANGUAGE | language | Default language | en-us |
|
||||
|
Loading…
Reference in New Issue
Block a user