nautobot icon indicating copy to clipboard operation
nautobot copied to clipboard

Allow to customize CSS to differentiate multiple instances

Open u1735067 opened this issue 3 years ago • 1 comments
trafficstars

As ...

Patti - Platform Admin

I want ...

to be able to customize the CSS (maybe the background-color but not just this) of a Nautobot instance

So that ...

Users (operators, developpers, ..) are able to easily distinguish the different instances (dev, test, staging, prod, ...)

I know this is done when...

  • An option to input CSS is present in the admin section
  • This option is overridable using settings.py (to allow copying data from an instance to another while keeping this property)
  • The CSS is applied on every pages

Currently, the only option is to set a banner, but having a banner on each instance with the same blue color doesn't help to differentiate without reading the banner text.

Optional - Feature groups this request pertains to.

  • [ ] Automation
  • [ ] Circuits
  • [ ] DCIM
  • [ ] IPAM
  • [ ] Misc (including Data Sources)
  • [ ] Organization
  • [ ] Plugins (and other Extensibility)
  • [ ] Security (Secrets, etc)
  • [ ] Image Management
  • [X] UI/UX
  • [ ] Documentation
  • [ ] Other (not directly a platform feature)

Database Changes

  • Add an option?

External Dependencies

None ?

u1735067 avatar Jun 07 '22 08:06 u1735067

This is certainly not what you'll want to implement, but here's what I'm using to add a custom style (path to the files):

  • patcher.py
try:
  import nautobot, rest_framework
  nautobot_base_path = nautobot.__path__[0]
  rest_framework_base_path = rest_framework.__path__[0]
  del nautobot, rest_framework
except Exception:
  print('Unable to locate Nautobot or Django REST framework installation path')
  raise SystemExit(1)

templates = (
    (nautobot_base_path, 'core/templates/base.html'),
    (nautobot_base_path, 'core/templates/admin/base.html'),
    (nautobot_base_path, 'core/templates/graphene/graphiql.html'),
    (rest_framework_base_path, 'templates/rest_framework/base.html'),
    # Alternative: nautobot/core/templates/rest_framework/api.html
)
# Alternative: nautobot/core/templates/inc/media.html + graphql

where = '</head>'
patch = '''\
    {% if settings.MY_CUSTOM_STYLE %}
    <style>{{ settings.MY_CUSTOM_STYLE | safe }}</style>
    {% endif %}
'''  # '    {% if se..\n    <styl..\n    {% en..\n'

for pkg_path, template_path in templates:
    with open(f'{pkg_path}/{template_path}', 'r+') as fh:
        content = fh.read()
        content_len = len(content)
        content = content.replace(where, patch + where)

        if len(content) == content_len:
            print(f'Unable to patch file {fh.name!r}')
            raise SystemExit(1)

        fh.seek(0)
        fh.truncate()
        fh.write(content)

    print(f'Patched {pkg_path.split("/")[-1]}{template_path!r}')
  • settings.py
# .navbar-default, .footer { background: linear-gradient(to right, #f8f8f8, moccasin); }
if os.getenv('MY_CUSTOM_STYLE'):
    MY_CUSTOM_STYLE = os.getenv('MY_CUSTOM_STYLE').strip()
elif os.getenv('MY_CUSTOM_STYLE_FILE'):
    with open(
        os.getenv('MY_CUSTOM_STYLE_FILE'),
        mode='r',
        encoding='utf-8'
    ) as fh:
        MY_CUSTOM_STYLE = fh.read()

u1735067 avatar Oct 03 '22 08:10 u1735067

Brainstorming on a feature-set and requirements here:

  • Given that we'll eventually want to set a content-security-policy in nautobot that disallows unsafe-inline, we probably don't want to add a feature that directly allows for injection of JS and CSS as a string. It'd be safer to instead provide a way to easily add another .js or .css file to inc/media.html and inc/javascript.html.
  • One way to do this would be via Apps, as any App can bundle additional static-files which we could then cleanly reference without needing to modify any content-security-policy configuration (as would be required to allow resources to load from a different domain). Different instances could install different apps (or different versions of the same app) to provide the desired differentiaion.
  • A less elegant approach would be to allow the specification of arbitrary URLs to load from as a part of the settings, but that will likely run afoul of content-security-policy restrictions.

glennmatthews avatar Apr 29 '24 16:04 glennmatthews