From 26ec8637bc66c773b663a9d5dbc315ecfcde66b5 Mon Sep 17 00:00:00 2001 From: LHBL2003 <46369917+LHBL2003@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:26:40 +0200 Subject: [PATCH 01/65] Create launch.json --- .vscode/launch.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1 @@ + From ca3582bb417b015a06c8aa411b002ef66cf64af1 Mon Sep 17 00:00:00 2001 From: LHBL2003 <46369917+LHBL2003@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:28:26 +0200 Subject: [PATCH 02/65] Create tasks.json --- .vscode/tasks.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..734d611 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,19 @@ +// Is required for debugging Docker-Compose in VSCode. +{ + "version": "2.0.0", + "tasks": [ + { + "label": "docker-run: debug", + "type": "docker-compose", + "dockerCompose": { + "up": { + "detached": true, + "build": true + }, + "files": [ + "${workspaceFolder}/develop/docker-compose.yml", + ] + } + } + ] + } From 2c851da1a9dee1543817f01a7230627753f41e12 Mon Sep 17 00:00:00 2001 From: LHBL2003 <46369917+LHBL2003@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:19:14 +0200 Subject: [PATCH 03/65] Debugging with breakpoint in VSCode and automatic creation of Django SuperUser for login to Netbox in the development environment (UN and PW=admin). --- .dockerignore | 29 ++++++++++++ Makefile | 24 +++++++++- README.md | 47 ++++++++++++++++++++ develop/docker-compose-debug.yml | 35 +++++++++++++++ develop/docker-compose.yml | 8 ++++ develop/management/__init__.py | 0 develop/management/commands/__init__.py | 0 develop/management/commands/makesuperuser.py | 36 +++++++++++++++ 8 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 develop/docker-compose-debug.yml create mode 100644 develop/management/__init__.py create mode 100644 develop/management/commands/__init__.py create mode 100644 develop/management/commands/makesuperuser.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f508c25 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +**/__pycache__ +**/.venv +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE + +# Part of Setup.py and therefore important for plugin installation. +# README.md \ No newline at end of file diff --git a/Makefile b/Makefile index 414332d..f37667b 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,9 @@ +# General: The Makefile is used for plugin development is used to simplify the execution of command sequences. +# Recommended VSCode plugin: Makefile-Tools (extension ID: ms-vscode.makefile-tools) +# To trigger a Makefile command (target), the following line must be written in the terminal window of VSCode, for example: +# make cbuild + +# Parameter PYTHON_VER?=3.12 NETBOX_VER?=v4.0.2 @@ -7,40 +13,56 @@ COMPOSE_FILE=./develop/docker-compose.yml BUILD_NAME=netbox_qrcode VERFILE=./netbox_qrcode/version.py - +# Build Docker with the specific Python and Netbox version cbuild: docker-compose -f ${COMPOSE_FILE} \ -p ${BUILD_NAME} build \ --build-arg netbox_ver=${NETBOX_VER} \ --build-arg python_ver=${PYTHON_VER} +# Start Docker with terminal window output debug: @echo "Starting Netbox .. " docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} up +# Start Docker with terminal window output. +# Brakepoints in e.g. Python files are supported in VSCode. Changes in Python and HTML are applied after saving. +debug-vscode: + @echo "Starting Netbox debug for VSCode.. " + docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} -f ${COMPOSE_FILE_DEBUG} up --build + +# Start Docker without connecting to the terminal window. (Runs independently of the terminal window.). start: @echo "Starting Netbox in detached mode.. " docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} up -d +# Stop Docker Container stop: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} down +# Stop Docker and remove containers destroy: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} down docker volume rm -f ${BUILD_NAME}_pgdata_netbox_qrcode +# Calls the Netbox shell. Exit with exit() +# NetBox includes a Python shell within which objects can be directly queried, created, modified, and deleted. nbshell: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} run netbox python manage.py nbshell +# Calls the Python shell. Exit with exit() shell: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} run netbox python manage.py shell +# To create the Django Superuser to be able to log on to Netbox Web. adduser: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} run netbox python manage.py createsuperuser +# Django collectstatic collectstatic: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} run netbox python manage.py collectstatic +# Migrate the Netbox system depending on the Django model migrations: docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} up -d postgres docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} \ diff --git a/README.md b/README.md index 7deed52..bbd2e7c 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,53 @@ Developing tools for this project based on [ntc-netbox-plugin-onboarding](https: Issues and pull requests are welcomed. +## Development + +### Contributing +Developing tools for this project based on [ntc-netbox-plugin-onboarding](https://github.com/networktocode/ntc-netbox-plugin-onboarding) repo. + +Issues and pull requests are welcomed. + +### Recommended extensions for VSCode: +With the extension ID you can search for the extension more easily. +- GitHub Pull Request (Extension ID: GitHub.vscode-pull-request-github) +- Docker (Extension ID: ms-azuretools.vscode-docker) +- Python (Extension ID: ms-python.python) +- Python Debugger (Extension ID: ms-python.debugpy) +- Makefile-Tools (extension ID: ms-vscode.makefile-tools) + +### Easy start of the development environment +Take a look at the topic "Makefile" and pay attention to the "Makefile" file in the project to be able to quickly start Netbox with the Docker-Compose. + +### Default Login (Netbox, DB, PostgreSQL, Redis) +If the project is started via Docker-Compose, the first login to Netbox is possible with the login data from the makesuperuser.py file. +Other login data for the database, PostgreSQL and Redis can be found in the dev.env file. + +Default Netbox Login (When start via Docker-Compose): +- User: admin +- PW: admin + +### Debugging with Breakpoint in VSCode +The following describes how to start the debugging mode in VSCode. + +*Start Container* +Write "make debug-vscode" without quotation marks in terminal window of VSCode (See also Makefile) +Wait until all containers are started and Starting development server at http://0.0.0.0:8000/ is displayed. + +*Start Debug:* +- Go to "Run and Debug" and start the debug "Docker: Python -Django" (The footer of vsCode should become Orang.) +- Set a breakpoint in the code +- Open the NetBox page with the plugin. +- VSCode will stop at the stopping point. + +If you change something in an HTML file, this will be displayed immediately after a reload of the website. If you change something in a *.py file, the web server is automatically restarted after saving the file. It may be advisable to deactivate this in VSCode under File --> Auto Save so that the web server does not restart so often. + +*Helpful documentary:* +- https://medium.com/django-unleashed/debug-django-application-in-docker-container-using-vscode-ca5967340262 +- https://testdriven.io/blog/django-debugging-vs-code/ +- https://github.com/microsoft/debugpy +- https://docs.python.org/3/using/cmdline.html#cmdoption-X + ## Screenshots Device QR code with text label diff --git a/develop/docker-compose-debug.yml b/develop/docker-compose-debug.yml new file mode 100644 index 0000000..3bf98a4 --- /dev/null +++ b/develop/docker-compose-debug.yml @@ -0,0 +1,35 @@ +# This Docker Compose Debug File supports you with debugging in VSCode For more details see README.md + +# Overwrites parts of the service from the Docker-Compose file: +--- +services: + netbox: + + # The following commands are executed + # - Install Current Version of DebugPy for Debug in Visual Studio Code https://github.com/microsoft/debugpy + # - PYDEVD_DISABLE_FILE_VALIDATION=1 --> You’re telling the debugger to skip this validation and proceed with debugging + # - Migrate Netbox + # - Create a user (superuser) for Netbox / Django so that you can log in directly to Netbox. More Infos: makesuperuser.py + # - Start Netbox with Modul DebugPy (Xfrozen_modules=off --> Disable Frozen Moduls in DebugPy Mode; 5678 is Debug Port) + command: > + sh -c "echo Run Netbox Django in debug mode && + pip install debugpy && + PYDEVD_DISABLE_FILE_VALIDATION=1 && + python manage.py migrate && + python manage.py makesuperuser && + python -Xfrozen_modules=off -m debugpy --listen 0.0.0.0:5678 manage.py runserver 0.0.0.0:8000 --insecure" + ports: + - '8000:8000' + - '5678:5678' + worker: + # Enables the synchronization of source code files with the Docker container during debugging. + # Without this option, changed files in the Docker container are only transferred to VSCode. + # Currently HTML files are changed live. After saving a *.py file, the container is automatically rebuilt. I don't know if this can be optimized. Greetings LHBL2003. + # https://www.docker.com/blog/docker-compose-experiment-sync-files-and-automatically-rebuild-services-with-watch-mode/ + x-develop: + watch: + - action: sync + path: ../netbox_qrcode + target: /source/netbox_qrcode + #- action: rebuild + #path: package.json diff --git a/develop/docker-compose.yml b/develop/docker-compose.yml index 4b7e984..02fd00b 100644 --- a/develop/docker-compose.yml +++ b/develop/docker-compose.yml @@ -5,8 +5,14 @@ services: build: context: ../ dockerfile: develop/Dockerfile + + # The following commands are executed + # - Migrate Netbox + # - Create a user (superuser) for Netbox / Django so that you can log in directly to Netbox. More Infos: makesuperuser.py + # - Start Netbox command: > sh -c "python manage.py migrate && + python manage.py makesuperuser && python manage.py runserver 0.0.0.0:8000" ports: - '8000:8000' @@ -18,6 +24,8 @@ services: volumes: - ./configuration.py:/opt/netbox/netbox/netbox/configuration.py - ../netbox_qrcode:/source/netbox_qrcode + # For automatic setup of the Django / Netbox SuperUser + - ../develop/management:/source/netbox_qrcode/management tty: true worker: build: diff --git a/develop/management/__init__.py b/develop/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/develop/management/commands/__init__.py b/develop/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/develop/management/commands/makesuperuser.py b/develop/management/commands/makesuperuser.py new file mode 100644 index 0000000..fa0b149 --- /dev/null +++ b/develop/management/commands/makesuperuser.py @@ -0,0 +1,36 @@ +# This script creates the Django Super User which is also used for the first login to Netbox. +# This means that no user needs to be created in the development phase, as this is done when Docker-Compose is started. +# The script is terminated prematurely if a user already exists. +# User name and password see parameters below "Username" and "password" +# +# This script must be located in the Django plugin path .../management/comands at development time. +# e.g.: Docker-Compose volumes: ../develop/management:/source/netbox_qrcode/management +# e.g.: Docker-Compose command: "python manage.py makesuperuser". + +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand + +User = get_user_model() + +class bcolors: + PURPLE = '\033[95m' + +class Command(BaseCommand): + def handle(self, *args, **options): + username = 'admin' + email = 'admin@admin.local' + password = 'admin' + try: + u = None + if not User.objects.filter(username=username).exists() and not User.objects.filter(is_superuser=True).exists(): + print("admin user not found, creating one") + + u = User.objects.create_superuser(username, email, password) + print(f"{bcolors.PURPLE}===================================") + print(f"{bcolors.PURPLE}A superuser for NetBox / Django '{username}' was created with email '{email}' and password '{password}'") + print(f"{bcolors.PURPLE}===================================") + else: + print("admin user found. Skipping super user creation") + print(u) + except Exception as e: + print(f"There was an error: {e}") \ No newline at end of file From 8abcc800495c18cded2db6e8d7dd62f7ee2ec5f7 Mon Sep 17 00:00:00 2001 From: LHBL2003 <46369917+LHBL2003@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:35:33 +0200 Subject: [PATCH 04/65] Update launch.json --- .vscode/launch.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8b13789..2324b25 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1 +1,26 @@ +// Is required for debugging Docker-Compose in VSCode. +{ + "configurations": [ + { + + "name": "Docker: Python - Django", + "type": "debugpy", + "request": "attach", + "connect": { + "host": "localhost", + "port": 5678 + }, + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "/source" + } + ], + + "django": true, + + "justMyCode": true, + } + ] +} From 2712fc1f848ae20be8ae2b724a91f184bd86dc9c Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 15 Jul 2024 15:55:09 +0200 Subject: [PATCH 05/65] COMPOSE_FILE_DEBUG missing for vscode debug --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f37667b..743680e 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ NETBOX_VER?=v4.0.2 NAME=netbox-qrcode COMPOSE_FILE=./develop/docker-compose.yml +COMPOSE_FILE_DEBUG=./develop/docker-compose-debug.yml BUILD_NAME=netbox_qrcode VERFILE=./netbox_qrcode/version.py From 66224e00c8556bebe95fcd8ca884d7db1132dedc Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 15 Jul 2024 17:53:46 +0200 Subject: [PATCH 06/65] Message texts improved --- develop/management/commands/makesuperuser.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/develop/management/commands/makesuperuser.py b/develop/management/commands/makesuperuser.py index fa0b149..ef12160 100644 --- a/develop/management/commands/makesuperuser.py +++ b/develop/management/commands/makesuperuser.py @@ -14,6 +14,7 @@ class bcolors: PURPLE = '\033[95m' + RED = '\033[91m' class Command(BaseCommand): def handle(self, *args, **options): @@ -30,7 +31,7 @@ def handle(self, *args, **options): print(f"{bcolors.PURPLE}A superuser for NetBox / Django '{username}' was created with email '{email}' and password '{password}'") print(f"{bcolors.PURPLE}===================================") else: - print("admin user found. Skipping super user creation") + print("{bcolors.PURPLE}A superuser for NetBox / Django Admin user already exists or was created during the first Docker-Compose start.") print(u) except Exception as e: - print(f"There was an error: {e}") \ No newline at end of file + print(f"{bcolors.RED}There was an error: {e}") \ No newline at end of file From 16e59ad1f282bf0f73cf0b4a1a5a54cc7b8a24fc Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 15 Jul 2024 17:58:43 +0200 Subject: [PATCH 07/65] Message texts improved --- develop/management/commands/makesuperuser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop/management/commands/makesuperuser.py b/develop/management/commands/makesuperuser.py index ef12160..7147ca0 100644 --- a/develop/management/commands/makesuperuser.py +++ b/develop/management/commands/makesuperuser.py @@ -31,7 +31,7 @@ def handle(self, *args, **options): print(f"{bcolors.PURPLE}A superuser for NetBox / Django '{username}' was created with email '{email}' and password '{password}'") print(f"{bcolors.PURPLE}===================================") else: - print("{bcolors.PURPLE}A superuser for NetBox / Django Admin user already exists or was created during the first Docker-Compose start.") + print(f"{bcolors.PURPLE}A superuser for NetBox / Django Admin user already exists or was created during the first Docker-Compose start.") print(u) except Exception as e: print(f"{bcolors.RED}There was an error: {e}") \ No newline at end of file From f43008259090198db5e7d0dae89ba19c45de56f4 Mon Sep 17 00:00:00 2001 From: LHBL2003 <46369917+LHBL2003@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:38:06 +0200 Subject: [PATCH 08/65] Update README.md Extension of the extension recommendation for Django, so that no errors are displayed in the HTML. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bbd2e7c..1b92177 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ With the extension ID you can search for the extension more easily. - Docker (Extension ID: ms-azuretools.vscode-docker) - Python (Extension ID: ms-python.python) - Python Debugger (Extension ID: ms-python.debugpy) +- Django (Extension ID: batisteo.vscode-django) - Makefile-Tools (extension ID: ms-vscode.makefile-tools) ### Easy start of the development environment From 5046d23a4584836aa09e3aa313359b1501399c1d Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Tue, 13 Aug 2024 11:46:08 +0200 Subject: [PATCH 09/65] First functional version without documentation. --- netbox_qrcode/__init__.py | 32 ++- netbox_qrcode/template_content.py | 190 ++++++++++-------- netbox_qrcode/template_content_functions.py | 149 ++++++++++++++ .../templates/netbox_qrcode/qrcode3.html | 144 ++++++++++++- .../netbox_qrcode/qrcode3_sub_qrcode.html | 13 ++ netbox_qrcode/utilities.py | 113 ++--------- 6 files changed, 450 insertions(+), 191 deletions(-) create mode 100644 netbox_qrcode/template_content_functions.py create mode 100644 netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index 6d16a96..3975ba4 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -11,15 +11,35 @@ class QRCodeConfig(PluginConfig): author_email = 'mgk.kolek@gmail.com' required_settings = [] default_settings = { + 'title': '', 'with_text': True, + 'with_qr': True, 'text_fields': ['name', 'serial'], - 'font': 'TahomaBold', + 'font': '\'Trebuchet MS\', sans-serif', + 'font_size': '3mm', 'custom_text': None, 'text_location': 'right', - 'qr_version': 1, + + #'url_template': 'Device-{{ obj.name }}', + + # These parameters are used to create the QR code image file. + 'qr_version': 1, # The higher the value, the more boxes you get. 'qr_error_correction': 0, - 'qr_box_size': 6, - 'qr_border': 4, + 'qr_box_size': 4, # The smaller the number of pixels, the blurrier the QR code will be if the label dimensions are too large, but the quicker the QR code will be ready. + 'qr_border': 0, + + # Parameters for the label (Horizontal) + 'label_qr_width': '12mm', + 'label_qr_height': '12mm', + 'label_qr_text_distance': '1mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '1.5mm', + 'label_edge_right': '1.5mm', + 'label_edge_bottom': '0mm', + + # Module-dependent configuration 'device': { 'text_fields': ['name', 'serial'] }, @@ -46,7 +66,9 @@ class QRCodeConfig(PluginConfig): }, 'powerpanel': { 'text_fields': ['name'] - } + }, + + 'logo': '' } config = QRCodeConfig # noqa E305 diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index d4b44ba..0d6073b 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -1,124 +1,152 @@ from packaging import version - from django.conf import settings from django.core.exceptions import ObjectDoesNotExist -from django.template import engines - from netbox.plugins import PluginTemplateExtension +from .template_content_functions import create_text, create_url, config_for_modul, create_QRCode -from .utilities import get_img_b64, get_qr, get_qr_text, get_concat - +# ****************************************************************************************** +# Contains the main functionalities of the plugin and thus creates the content for the +# individual modules, e.g: Device, Rack etc. +# ****************************************************************************************** +################################## +# Class for creating the plugin content class QRCode(PluginTemplateExtension): - def x_page(self): - config = self.context['config'] - obj = self.context['object'] - request = self.context['request'] - url = request.build_absolute_uri(obj.get_absolute_url()) - # get object settings - obj_cfg = config.get(self.model.replace('dcim.', '')) - if obj_cfg is None: - return '' - # and ovverride default - config.update(obj_cfg) - - qr_args = {} - for k, v in config.items(): - if k.startswith('qr_'): - qr_args[k.replace('qr_', '')] = v - - qr_img = get_qr(url, **qr_args) - if config.get('with_text'): - if config.get('text_template'): - django_engine = engines["django"] - template = django_engine.from_string(config.get('text_template')) - text = template.render({'obj': obj}) - else: - text = [] - for text_field in config.get('text_fields', []): - cfn = None - if '.' in text_field: - try: - text_field, cfn = text_field.split('.') - except ValueError: - cfn = None - if getattr(obj, text_field, None): - if cfn: - try: - if getattr(obj, text_field).get(cfn): - text.append('{}'.format(getattr(obj, text_field).get(cfn))) - except AttributeError: - # fix for nb3.3: trying to get cable termination and device in same way as custom field - if type(getattr(obj, text_field)) is list: - first_element = next(iter(getattr(obj, text_field)), None) - if first_element and getattr(first_element, cfn, None): - text.append('{}'.format(getattr(first_element, cfn))) - else: - text.append('{}'.format(getattr(obj, text_field))) - custom_text = config.get('custom_text') - if custom_text: - text.append(custom_text) - text = '\n'.join(text) - text_img = get_qr_text(qr_img.size, text, config.get('font'), config.get('font_size', 0)) - qr_with_text = get_concat(qr_img, text_img, config.get('text_location', 'right')) - - img = get_img_b64(qr_with_text) - else: - img = get_img_b64(qr_img) + ################################## + # Creates a plug-in view for a label. + # -------------------------------- + # Parameter: + # labelDesignNo: Which label design should be loaded. + def Create_SubPluginContent(self, labelDesignNo): + + thisSelf = self + + obj = self.context['object'] # An object of the type Device, Rack etc. + + # Config suitable for the module + config = config_for_modul(thisSelf, labelDesignNo) + + # Abort if no config data. + if config is None: + return '' + + # Get URL for QR code + url = create_url(thisSelf, config, obj) + + # Create a QR code + qrCode = create_QRCode(url, config) + + # Create the text for the label if required. + text = create_text(config, obj, qrCode) + + # Create plugin using template try: if version.parse(settings.VERSION).major >= 3: - return self.render( - 'netbox_qrcode/qrcode3.html', extra_context={'image': img} + + render = self.render( + 'netbox_qrcode/qrcode3.html', extra_context={ + 'title': config.get('title'), + 'labelDesignNo': labelDesignNo, + 'qrCode': qrCode, + 'with_text': config.get('with_text'), + 'text': text, + 'text_location': config.get('text_location'), + 'font': config.get('font'), + 'font_size': config.get('font_size'), + 'with_qr': config.get('with_qr'), + 'label_qr_width': config.get('label_qr_width'), + 'label_qr_height': config.get('label_qr_height'), + 'label_qr_text_distance': config.get('label_qr_text_distance'), + 'label_width': config.get('label_width'), + 'label_height': config.get('label_height'), + 'label_edge_top': config.get('label_edge_top'), + 'label_edge_left': config.get('label_edge_left'), + 'label_edge_right': config.get('label_edge_right'), + 'label_edge_bottom': config.get('label_edge_bottom') + } ) + + return render else: + # Versions 1 and 2 are no longer supported. return self.render( - 'netbox_qrcode/qrcode.html', extra_context={'image': img} + 'netbox_qrcode/qrcode.html', extra_context={'image': qrCode} ) except ObjectDoesNotExist: return '' + ################################## + # Create plugin content + # - First, a plugin view is created for the first label. + # - If there are further configuration entries for the object/model (e.g. device, rack etc.), + # further label views are also created as additional plugin views. + def Create_PluginContent(self): + + # First Plugin Content + pluginContent = QRCode.Create_SubPluginContent(self, 1) + + # Check whether there is another configuration for the object, e.g. device, rack, etc. + # Support up to 10 additional label configurations (objectName_2 to ..._10) per object (e.g. device, rack, etc.). + + config = self.context['config'] # Django configuration + for i in range(2, 11): + + configName = self.model.replace('dcim.', '') + '_' + str(i) + obj_cfg = config.get(configName) # Load configuration for additional label if possible. + + if(obj_cfg): + pluginContent += QRCode.Create_SubPluginContent(self, i) # Add another plugin view + else: + break + + return pluginContent + +################################## +# The following section serves to integrate the plugin into Netbox Core. + +# Class for creating a QR code for the model: Device class DeviceQRCode(QRCode): - model = 'dcim.device' + model = 'dcim.device' # Info for Netbox in which model the plugin should be integrated. def right_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Class for creating a QR code for the model: Rack class RackQRCode(QRCode): - model = 'dcim.rack' + model = 'dcim.rack' # Info for Netbox in which model the plugin should be integrated. def right_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Class for creating a QR code for the model: Cable class CableQRCode(QRCode): - model = 'dcim.cable' + model = 'dcim.cable' # Info for Netbox in which model the plugin should be integrated. def left_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Class for creating a QR code for the model: Location class LocationQRCode(QRCode): - model = 'dcim.location' + model = 'dcim.location' # Info for Netbox in which model the plugin should be integrated. def left_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Class for creating a QR code for the model: Power Feed class PowerFeedQRCode(QRCode): - model = 'dcim.powerfeed' + model = 'dcim.powerfeed' # Info for Netbox in which model the plugin should be integrated. def right_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Class for creating a QR code for the model: Power Panel class PowerPanelQRCode(QRCode): - model = 'dcim.powerpanel' + model = 'dcim.powerpanel' # Info for Netbox in which model the plugin should be integrated. def right_page(self): - return self.x_page() - + return self.Create_PluginContent() +# Connects Netbox Core with the plug-in classes template_extensions = [DeviceQRCode, RackQRCode, CableQRCode, LocationQRCode, PowerFeedQRCode, PowerPanelQRCode] diff --git a/netbox_qrcode/template_content_functions.py b/netbox_qrcode/template_content_functions.py new file mode 100644 index 0000000..1d17beb --- /dev/null +++ b/netbox_qrcode/template_content_functions.py @@ -0,0 +1,149 @@ +from .utilities import get_img_b64, get_qr +from django.template import engines + +# ****************************************************************************************** +# For better clarity, the sub-functions of template_content.py have been outsourced. +# ****************************************************************************************** + +################################## +# The configuration is taken and all fields that are module-specific (e.g. Device, Rack, etc.) are replaced. +# -------------------------------- +# Parameter: +# labelDesignNo: Which label design should be loaded. +# parentSelf: Self from Parrent Function +def config_for_modul(parentSelf, labelDesignNo): + + # Copy so that the Runtime data is not changed. + config = parentSelf.context['config'].copy() # From Netbox Config File + + # Create suffix to read the correct module configuration. + confModulsufix = str() # None if the first standard label + + if(labelDesignNo >= 2): + confModulsufix = '_' + str(labelDesignNo) + + # Collect the QR code plugin configuration for the specific object such as device, rack etc. + # and overwrite the default configuration fields. + obj_cfg = config.get(parentSelf.model.replace('dcim.', '') + confModulsufix) # get spezific object settings + + print(obj_cfg) + + if obj_cfg is None: + return '' # Abort if no config data. + + config.update(obj_cfg) # Ovverride default confiv Values + + return config + +################################## +# Create QR-Code +# -------------------------------- +# Parameter: +# text: Text for QR-Code +# config: From the Netbox configuration file +def create_QRCode(text, config): + + # Collect the configuration entries that begin with "qr_. + # These are required to generate the QR code. + qr_args = {} + for k, v in config.items(): + if k.startswith('qr_'): + qr_args[k.replace('qr_', '')] = v + + # Create a QR code + qrCode = get_qr(text, **qr_args) + return get_img_b64(qrCode) + + +################################## +# Create URL for QR code +# -------------------------------- +# Parameter: +# config: From the Netbox configuration file +# obj: Data from the model (e.g. device, rack, etc.) +# request: HTML Request Information +def create_url(parentSelf, config, obj): + + request = parentSelf.context['request'] # HTML Request Informations + + if config.get('url_template'): + # A user-defined design specification of the URL is provided in ninja2 format. + django_engine = engines["django"] + template = django_engine.from_string(config.get('url_template')) # Custom template for URL design. + return template.render({'obj': obj}) # Replace placeholder + else: + return request.build_absolute_uri(obj.get_absolute_url()) # URL to the requested page + +################################## +# Create text for label +# -------------------------------- +# Parameter: +# config: From the Netbox configuration file +# obj: Data from the model (e.g. device, rack, etc.) +# qrCode: QR-Code Image +def create_text(config, obj, qrCode): + + text = str() + + if config.get('with_text'): + if config.get('text_template'): + return get_text_template(config, obj, qrCode) # Create text content based on the Ninja2 template from the user + else: + return get_text_fields(config, obj) # Use the list of variables from the Config. + +################################## +# A user-defined design specification of the text is provided in ninja2 format. +# -------------------------------- +# Parameter: +# config: From the Netbox configuration file +# obj: Data from the model (e.g. device, rack, etc.) +# qrCode: QR-Code Image (To create a freely defined label with QR code.) +def get_text_template(config, obj, qrCode): + + django_engine = engines["django"] + template = django_engine.from_string(config.get('text_template')) # Get Custom Template + logo = config.get('logo') + return template.render({'obj': obj, + 'logo': logo, + 'qrCode': qrCode}) # Replace placeholder + +################################## +# Retrieves all values from the object (e.g. device, rack, etc.) +# depending on the configuration parameter that are to be displayed in list form and prepares them. +# -------------------------------- +# Parameter: +# config: From the Netbox configuration file +# obj: Data from the model (e.g. device, rack, etc.) +def get_text_fields(config, obj): + + text = [] + + for text_field in config.get('text_fields', []): + cfn = None + if '.' in text_field: + try: + text_field, cfn = text_field.split('.') + except ValueError: + cfn = None + if getattr(obj, text_field, None): + if cfn: + try: + if getattr(obj, text_field).get(cfn): + text.append('{}'.format(getattr(obj, text_field).get(cfn))) + except AttributeError: + # fix for nb3.3: trying to get cable termination and device in same way as custom field + if type(getattr(obj, text_field)) is list: + first_element = next(iter(getattr(obj, text_field)), None) + if first_element and getattr(first_element, cfn, None): + text.append('{}'.format(getattr(first_element, cfn))) + else: + text.append('{}'.format(getattr(obj, text_field))) + + # Append user-defined text to the end. + custom_text = config.get('custom_text') + + if custom_text: + text.append(custom_text) + + # Convert text list to string with line breaks. + return '
'.join(text) \ No newline at end of file diff --git a/netbox_qrcode/templates/netbox_qrcode/qrcode3.html b/netbox_qrcode/templates/netbox_qrcode/qrcode3.html index 88d4629..ec6cdae 100644 --- a/netbox_qrcode/templates/netbox_qrcode/qrcode3.html +++ b/netbox_qrcode/templates/netbox_qrcode/qrcode3.html @@ -1,19 +1,149 @@ + + +
- QR Code + OR-Code (H/W: {{label_height}} x {{label_width}}) {% if title %} - {{title}}{% endif %}
- +
+
+ + {# Only Text label #} + {% if with_text is True and with_qr is False %} + + + + +
+ {{ text|safe|escape }} +
+ {% endif %} + + {# Text and QR-Code #} + {% if with_text is True and with_qr is True %} + + {# Horizontal label #} + {% if text_location == "right" or text_location == "left" %} + + + + {% if text_location == "right" %} + + {% endif %} + + + + {% if text_location == "left" %} + + {% endif %} + + +
+ {% include "netbox_qrcode/qrcode3_sub_qrcode.html" %} + +
+ + {{ text|safe|escape }} + +
+
+ {% include "netbox_qrcode/qrcode3_sub_qrcode.html" %} +
+ {% endif %} + + {# Vertical label #} + {% if with_qr and text_location == "up" or text_location == "down" %} + + + {% if text_location == "down" %} + + + + {% endif %} + + + + + + {% if text_location == "up" %} + + + + {% endif %} + +
+ {% include "netbox_qrcode/qrcode3_sub_qrcode.html" %} +
+
+ + {{ text|safe|escape }} + +
+
+ {% include "netbox_qrcode/qrcode3_sub_qrcode.html" %} +
+ {% endif %} + {% endif %} + + {# Only QR-Code label #} + {% if with_text is False and with_qr is True %} +
+ {% include "netbox_qrcode/qrcode3_sub_qrcode.html" %} +
+ {% endif %} +
+
+ diff --git a/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html b/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html new file mode 100644 index 0000000..1c14039 --- /dev/null +++ b/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html @@ -0,0 +1,13 @@ +
+ +
\ No newline at end of file diff --git a/netbox_qrcode/utilities.py b/netbox_qrcode/utilities.py index c1e2f7c..e0cd376 100644 --- a/netbox_qrcode/utilities.py +++ b/netbox_qrcode/utilities.py @@ -1,18 +1,17 @@ import base64 import qrcode - from io import BytesIO -from PIL import Image, ImageFont, ImageDraw - -from pkg_resources import resource_stream - - -def get_qr_with_text(qr, descr): - dsi = get_qr_text(qr.size, descr) - resimg = get_concat(qr, dsi) - return get_img_b64(resimg) +# ****************************************************************************************** +# Includes useful tools to create the content. +# ****************************************************************************************** +################################## +# Creates a QR code as an image.: https://pypi.org/project/qrcode/3.0/ +# -------------------------------- +# Parameter: +# text: Text to be included in the QR code. +# **kwargs: List of parameters which properties the QR code should have. (e.g. version, box_size, error_correction, border etc.) def get_qr(text, **kwargs): qr = qrcode.QRCode(**kwargs) qr.add_data(text) @@ -21,94 +20,12 @@ def get_qr(text, **kwargs): img = img.get_image() return img - +################################## +# Converts an image to Base64 +# -------------------------------- +# Parameter: +# img: Image file def get_img_b64(img): stream = BytesIO() img.save(stream, format='png') - return str(base64.b64encode(stream.getvalue()), encoding='ascii') - - -def get_qr_text(max_size, text, font='TahomaBold', font_size=0): - tmpimg = Image.new('P', max_size, 'white') - - if font_size == 0: - text_too_large = True - font_size = 56 - while text_too_large: - file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) - try: - fnt = ImageFont.truetype(file_path, font_size) - except Exception: - fnt = ImageFont.load_default() - - draw = ImageDraw.Draw(tmpimg) - left, top, w, h = draw.textbbox((0, 0), text=text, font=fnt) - if w < max_size[0] - 4 and h < max_size[1] - 4: - text_too_large = False - font_size -= 1 - else: - file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) - try: - fnt = ImageFont.truetype(file_path, font_size) - except Exception: - fnt = ImageFont.load_default() - draw = ImageDraw.Draw(tmpimg) - left, top, w, h = draw.textbbox((0, 0), text=text, font=fnt) - - img = Image.new('P', (w, h), 'white') - draw = ImageDraw.Draw(img) - draw.text((0, 0), text, font=fnt, fill='black') - return img - - -def get_concat(im1, im2, direction='right'): - if direction == 'right' or direction == 'left': - width = im1.width + im2.width - height = max(im1.height, im2.height) - elif direction == 'down' or direction == 'up': - width = max(im1.width, im2.width) - height = im1.height + im2.height - else: - raise ValueError( - 'Invalid direction "{}" (must be one of "left", "right", "up", or "down")'.format(direction) - ) - - dst = Image.new('L', (width, height), 'white') - - if direction == 'right' or direction == 'left': - if im1.height > im2.height: - im1_y = 0 - im2_y = abs(im1.height-im2.height) // 2 - else: - im1_y = abs(im1.height-im2.height) // 2 - im2_y = 0 - - if direction == 'right': - im1_x = 0 - im2_x = im1.width - else: - im1_x = im2.width - im2_x = 0 - elif direction == 'up' or direction == 'down': - if im1.width > im2.width: - im1_x = 0 - im2_x = abs(im1.width-im2.width) // 2 - else: - im1_x = abs(im1.width-im2.width) // 2 - im2_x = 0 - - if direction == 'down': - im1_y = 0 - im2_y = im1.height - else: - im1_y = im2.height - im2_y = 0 - else: - raise ValueError( - 'Invalid direction "{}" (must be one of "left", "right", "up", or "down")'.format(direction) - ) - - dst.paste(im1, (im1_x, im1_y)) - dst.paste(im2, (im2_x, im2_y)) - - return dst + return str(base64.b64encode(stream.getvalue()), encoding='ascii') \ No newline at end of file From ffba5b444db438ee6705538b622b6a60bb355440 Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Tue, 13 Aug 2024 16:22:09 +0200 Subject: [PATCH 10/65] Support font_weight --- netbox_qrcode/__init__.py | 3 +- netbox_qrcode/template_content.py | 1 + .../templates/netbox_qrcode/qrcode3.html | 60 +++++++++---------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index 3975ba4..7f1f283 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -15,8 +15,9 @@ class QRCodeConfig(PluginConfig): 'with_text': True, 'with_qr': True, 'text_fields': ['name', 'serial'], - 'font': '\'Trebuchet MS\', sans-serif', + 'font': 'TahomaBold', 'font_size': '3mm', + 'font_weight': 'normal', 'custom_text': None, 'text_location': 'right', diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index 0d6073b..96d282e 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -54,6 +54,7 @@ def Create_SubPluginContent(self, labelDesignNo): 'text_location': config.get('text_location'), 'font': config.get('font'), 'font_size': config.get('font_size'), + 'font_weight': config.get('font_weight'), 'with_qr': config.get('with_qr'), 'label_qr_width': config.get('label_qr_width'), 'label_qr_height': config.get('label_qr_height'), diff --git a/netbox_qrcode/templates/netbox_qrcode/qrcode3.html b/netbox_qrcode/templates/netbox_qrcode/qrcode3.html index ec6cdae..0001c03 100644 --- a/netbox_qrcode/templates/netbox_qrcode/qrcode3.html +++ b/netbox_qrcode/templates/netbox_qrcode/qrcode3.html @@ -15,10 +15,10 @@ '; + var printContent = document.getElementById(areaID).innerHTML; var originalContent = document.body.innerHTML; + document.body.innerHTML = printContent; + //document.body.innerHTML += css; window.print(); document.body.innerHTML = originalContent; } @@ -13,6 +27,7 @@ ' + '' + '' + '' + '' ``` \ No newline at end of file diff --git a/docs/img/Configuration_Label_Example_12.png b/docs/img/Configuration_Label_Example_12.png new file mode 100644 index 0000000000000000000000000000000000000000..8585935cb5684d8dee6a05f5ccddd591af2207bc GIT binary patch literal 11811 zcmdUV2UJsA^REiTLIgyRDAk4{EsB6hM-UZ3iWrm@LE%cr&@n{dqSCQZr3=y|A`)r{ zQHoxg3ZVr8(hVd5A%v3hPEhatzPHwE>;JyD);nvREyb;On}0w1{N);8~=TU)j$)?75yzZ&E~$@KOR?ayQc#|LdyPO=GqbmAJsa;N!U z=M7F@I>Eam#xdvozM8Lx_h5t<^n?t9MQROmg^0WKbL=ORji~1iZat*%!dTB>vF^=| zm<6xm`IqoI&^mmp_@z}>wu0d2tQlt43l&C3Edq_c2qK5-N`F5*%}h(voS0i>WTu6x zOyWPZ?b^dVwvcd#}c7irr zqO$lcTrv0jYF~irZSqw7uaH;EBJd;p=E&^B z-e#zk>cp)Z=Kj+C=&4mce_j6u-q3&&Y3z+slBOJbs*e1`xu@;<3)hVgEUmNUq~NL> ziX$j{do8({m*@OP>8)tLjHO=Rmbp+rd;gCZGY2Y1RkubW8~q?rv$tjYr^V9Dx7|&` z-7THei7(6sXO{TsDeSR;wHa%im{JzgP<0kj<<@hIJkSzGvQ4DUW`8%Mld4x76h>lk zH}YgVS$b6?m!y>m8`_e=Glp7y&WQ#27qLIc!i0NYs7rJgf~ZG+>X(gDTYLHj+AkgX z&i~m0yGplul@9LpuG2Ym$Z3CiFtlc{(af}DJ?j9S(OB7=xMKIQv%ba(`vg+mHCkL} zvSa&EigT00HB&-}CP9bSXyid1{U>R)hEh#uX$oA2dlr9ouH(pb{_^s-7 zpG42smEoZNhlD~}RJQ+g-**qor+%X^1?v&WI}U4his^-n>-6;WWwQI1we%H?+hWW& z9(n)l<&EUKN?yovw~_8cG#d_xa>oowo9d!V5?1=kia63GgjL#Ks|wm-$7&^KMkd?{ zjN4tORUNk1UsV1+RdU_UI(Kap6G$W+LbBU7XtA$8V`mH;2Lm+H_ktoh7DKd>N{Zqj z>56s6Tzvsba8g^tl<{2HZ62jE-mhYg`@Rln+wDIvcsQGXmvbxNg?9whT>AIXVN!5u(Z_GMyp~m(svKhX955wCY2J`+D zu!}d+Sdh?(E-%es6E;qp-sEUYnhnoM+d!dFoKn*-)&mU@jyN^My&LMr;-Uz2ap2jz z#EX>^uEHQULZKAVHFm=8p^u?OuMkjrT&;NCd{BS+- zuHB6J`e%- zS(ZUEL2UqqNIAl>&RipPya5 zaMPn)i}>cp6M+OdSYEz?L-q$!(Q!eZOCovuid|@4G4gDfWG7vNMnl;qe)kFZN*~M~ zNz{$+st!fth$16ZkDVWz6BmouAG^b_<)pbc=nuRpwBiD3wc?bKObAP7_RP9|xi-a= z{utI|jMhgl&Do9WDINe1yT{N3B&nq|Y52~Fg09&-95DlCV3=^Z9( z@Qt5!A*bt^(rlJkD|9GHm zt8)KMJ+UOQD{4lRUSqYd4k^30@T;|TyBm9{9hBPxZv1MLM=c%6mbc8`MsVO$i%L-% zdYKwGBMB>gxuX)crp&e!3*d{;|Dzh)^XAQP=)h20feh;5Nhy!V5_nfDMJZv zX8SU^@3oZ)s%+GC)c#q$u-E9^kJ&!sk$&sIcH$R}KEwethd!FTq)q`P;?I%(qBT>p z4ehr7{JCIk9Sd2{&Z_RDT^xUWDs+3%sPSJhvt8|99oJ~q;<6@Skg&}GojLQiU1krL+jU*+*c za0pXMwXHioM=(kg=`xbK@_jW;#GZNrnG4T})e3!hlosQcH}I}awOZrEHY|OlyLMKT z5@aXFw7guX-Rg`DvD7)za9uUKq1=n?Oj%zwMd&z?u1>OUJG#4B$ia$J8p|C%A3U$) zEJK?TbHTn&H`@*?nJ_q`1;dB7Wr_vTb`hn zkd)vH%(CX?{`u`hb1@NTI?aX2=0BY%>_5lI;Z$B=vWJ4%nZk^X4cySgXn*OC+8x?n zVa7kY4g+w;1p;Qf={BWXUar#}!cT}Qcc6$O@>SDhiZEebRH2Q8R?9uO!V>6l43Z|f zZk=Cw8ui4{d~nL}V75ZQk6md4@63foF10?})-)>v?93`eC4dkZz|zvO76jeC|_wh(~L(m+ha(@_nC9Dsr^KPQMy`ucHWA zow;=%KlbVMmCm&%o=WRwKH=#!zN$Or#A>9LJafO8+&xWFil#-Cn^m=BkN(Z46741- z;-t_DpPP;{keVeDZT-fE!zUE_PN=R$6`S^@)VpMw1W&-st|^{`yljM(Q5knyG5qAz zKQCb=9^9xEKlRD}$m+v>!jYwqNv!JwOXkG!k)3C;gALi8wZ6C|x%CNN9z(c{WEiRQjVo z%(*A4^a`%dqpFQw78$-4XH1wQi|$NQSq{F)Qurw8KR;}&! zt(R-9JPQRYzCw=ZCtbKlI)awzlz|LrM7PC){*c4Vh{Z9X&&6rFkb$M;3)zU;$_do! z0ErGwEF8?X_uC`0AG}`gY`!2dnF%;K!;aIC)S1_vDnmWJ^4^rIirH1A(+sh+2)|y2 zdZ_@cYzlC9(hx<<)s}t4hf37phmoE6O4KwuNq}%mr#bz{{zyh*DV5|!@W?XmBl@%? zgvGO6!Q-y!qi+JWw{==`43mzZgrp)KvpWrHw+-XYIq4oR+h84pB%Kl3om9`c63+U|Bv_!acIhoyXGjFugh&@Sdsk;_&J4#%U;l=l?`{v% z)0jatI>WOoP}jxc8I+MY13vgFg7Yy9bEGlEQx`3i8OvWX=Nw8M9#OLI^-A9W02vvsz%540kv4nzw4uB0ARTb1g zH387N_VQS6-Qb`Ea>GUlJXyJ)CV|aS3JCk!?Ft^AQJJ6d`nAu*!Oh#ndX2paC1M@Y z{=Vty@BCho7jOqQsZs2@-rtCA*tRC6{8BzUc9QxKKv-w(*5eKqx==r zY?JT|_BUsB-76fME3~VzE|ZM~cY22fd$%$=wNRgpUa*4`P`J;c-LAf@BB;5;iHD1Qu!Z!TY0D4!RWFlg|di(ptaFQ zsy?wG9#4jHzUJNOz$umhOVeRe^;N1E_}!O>WBGcnLyJ1^)?eV39#ki23!2ytbJPvy zP;*HTc2|3M9}8#tm7Nj!=#kAg7iq8)B{2R~0@615jo=!UacDG6d4h#hbzQ}-7weE8 zl}{>rOE;Y)x(-FY=iI?MzK6xF%-JJ#z9!K1A0y5jr_iu_kuFE+oa~%#qoEK6PKi0P zKDhs$HW^-x8-^vkhY7dRYlOjfM(0d5|AKD%InZS2GfG4`*jF2zs}Ir7KYG>7A6 zxB=tg|1J>w6-~PRKOo!OfUZuov~R|RHm%7bU?Aq+vL&bCAY}7m3;yTdX1os2|4Zak zV9>b-VEtxq3xMf;g-@p;!#I@m9vt6JZZ;rEm6xYkUuN@#Zy&(#pV@JQD}!F2))M^l z@UTSa`tqG6S}kIxW1!r={<5Z>@iR7sSlu?aE_K%LxC5y^(3=#}MXdHdHuTCY^U}La zc1RRP6w?yQptiJsJ6j(}OGqIO4UI4QIOGNlCAJ} zdzR_T%gxB~aGvesQ+<8cR9;n9_Pp!P(smyTU73%bkZ_C(oDBHqBU;6 z^Uz+|%NNSz@$l)6G`F=MQ;TPv+Y+p8F&-!JO`RtEAXQb>5)4Mo>q?f^_1Cu5N+*qn z8-fuHk~={r$qE*T>5hvM#1iY$@wNQ(@s_sLUZx)B%1TRPXX@dfdwXA=Pn7aZK4$i! z=zzHR-nmEOY98hjZHbA*STSWaBIK-J(VZ*VI@+Z2+3k^!4;!aX$ypU1nZ5u`Rrhwt zy8;X&ey-}VzsYDgfr(N!si707uzmXd=9bH+VI>73L}0;!~U?nY5NR5 zmfX{~=s)qT!GQ9{k=y*>%s;b1T-Y!Y12b#cZ@mX-L*?Dxu}YAdfFX&Upqq|zb?`9; zFjce^9e|T#QODyv`(%6Qg^a^^nfVm>SoQ6!9-7#Qj^R!aI~A9u{Bu`Lr)}pkl&JZs zZ3iN@j>{y$fw|an{Tx(Jf>)S3FEZ{sK#wBzBDMn8w|3)m?3j5L*1L->Uf*#x(#}8N z=r@{bRd4`zAtSOINP7M3B(vAE0t;Q=b-BVnSA3c{RgrC@R0Lj?cvf*&2jH;f?Vs?ZXv^WgqV%Y8^Zuwe4&`#CaG{PX z^SA+_(NiP$4c#y7^546#j$;8GyoLJYcK!6xs5x_5IHM?e6uB zUn9dg2TW~Fw(X~jiZ3OODAYqIG6f^_f9>Z35&E*zyS(MX^0%prfuBSLhdri>_2p8@ zp=R}7hju(7vFEMVUf$esK@jwfJ)HSqRuv<=D~M4S8(#~Tp&07PKt!y&v;nd7l7+x; zKjWTA&u!;HucqW_TZoA51Qo6_Vghx+%VOXNO5f;*%yIt5+T%%kJ|Ywi-2!=Eu>nMn_oe|@iP9X76+4I zffeWJ2OCErfBvl!*N?Y&xj{eTVzO3c%2iLSBJIC<^jEN~>G8E~Ngj*Ywr%nqMxg~d zY9zD>*#TG4d_QTxrGmxh9NkZ*$p_!-8V@f#^=yeNO43o@$QfB!ONHMrv)`=+IjyeV zmZgInVgg`ELj~jBuhb*Zzo!7Tu|ip_YkVjm8I!`Gj$~A4Ev-<9(JRGPuY^5c;T&>3 zqdXT$*wDqhHC@z^`iUtF1{0+90#+os5^Ha7zj!yuNG$S)`XQuCMN7Ylrr|W6Tfk?bf)r`sTZ)@3`UcOoQKd%RI%U7h$;Bm4hpE`q5S< z{oX$#y@0E6YT30Hd}~k;^x?toc3WeKlTLr`1Q9WwvDF959dRUHo_>Kye2wqKWzKLd z!gO(d`n3ag2%rWCNb3xjsr!?!R=IShH)E!|v%RO|_N%nMJ9arW$p#jI??_W2{FrK= z@NZ3!EX8;XIz@zMr0fH`xw&2S^z`)x z!2@&BZ`QmASh;2+5q~-^BDd<@c5<~x_W?@hZrCX-U0e5y9pZ#^#Ai67CR#KLqkK$czM?P z`FQ%J%O?__JLS?}2CWcTe9d zqOT%Yw@2TMR@rBd?z0C5|4u^~{JzoB53udPRE;Vl0ZxtPX@`yK73$$hYQ_=z1IqX& z?{=+Mqsn#2N5I{aC~e1|rnpl@5cKGb&;J}w34w*ShyQi~`n9V@`6UM|^KbOD#)O<# zZJcd9`RbBt*kiTr+0}n{k{xmiy}2x!>NIB{*I*()eA~;KmDXDkq2mwr!IY$jgLo>t zx8>#yqzY$;$`+0)V#J(<+sl*=f8=yv$}?tNzEQW zRa6u$bVqGPl`FK{oPG#TdJ5lMTfkTu%K7*o9M1c2U!-Z;ktimHBi+-cj+!3d2hP(J zl!FlqR>LYRs`_9p&e5Jn%EunUz8m56Xrn4`rD+L|?hq;a#8ui#9B1}x?vo4+YT%7D z*D&5>Z2+)V=AGSEOBmKja#BCh@Zr4%c-zs@lW%S|i42x61QJc{3Ifq>C$PxY}=AjYgvU4K@#*2Uv zLm?q7Yq0dk#ScPBQsDQjTFu>G+&xT17O4JW;t0InQ1pF0=<$*lp@3MMZYLLYRJTtM z;2ktRwDgUl*JcsD)P0L`0Vs^9}i0ze-8|r3*N&<3C;d z19(ytU|9b!i!WK+DC3I^mn%WE;A+VZ#~~kw$4=(k%V@7)6Nhne6MSVN>s|y=-?+44BH%iIkh{S_~G$$Lfm~9@E>$XSjes#$*Sm+q68!`TH;LkrfR6!^1iVhlGTT>nt{f^DN$VcVFHg z*VSd#qZ7JPc84}tc`OUDx^S~6aJ)IXraSoi*8)S^X$@A`7s|Q$xjB=#wYjx_Mw%4n z+Y%!x3;Fm~8vJ0lcye{Z8K>qQd%+k_;y&+lcDOJs0Cy!pk-ihHX>1^vNgp5Vw8e&0 z0aOYQCDdH^CifP*b*8IEdv<2)j&|%2SOn&;r# z7G558WkpDDZ*S4i&`^zb@Q+(nUL$qQ6242*or?iSAPU#t8g{0s9P{-kz4kh0Ch>sk zCC`yM)r#E}<>g9Qv zg}}-!2i50EgU=^O+(~|QIqga6VLY7F5FD)o3?m+Z-WcA<_4V}{%66XqnO|rB=g)8; zDA@os9kA^PCVXtYnU8-T7t_18wY_hIFBX*S)kR<0HS zm=I1hi`WW0zG;SvPVWF=C`TYd;FqSGAn;P;f4;*++V)5w6tvk1`hn&LmFwEINz1nw z%w1fZuRaP{{f$5@BEoIWY}@+dA1>{x`Vhu#3M4ANpm~3wiP=LJX8s8)0nk2l2XO@Q zC$eBRB778>DxBl`5y&bs_mpu=!1VyUjAsIG1_7gjd{pZK*#&UhI8HZDCo2K2*1Yp?7wqtY=&!M1;Ut{v zEnSl2o*oQQQOza68rP)~pm26&A!sKk_ih4Ll+94WVaI2_yQQ@VnHI0BK@X!45Z+5b zp6du23b{gNPv?SA@Zi-c=0%%ej$pmP3wT%gVl(C_`c1C*93 z-%zj`ziZeIdST!6no*sj&BMA;-)FM!AMqy zooL(FU*6;Yzx>Rs3M4Z#vk3T(LX5rh1LRxe5Uf=AcuLUO2m=44`E06ea zpuax_veduy&q!rh<1wQ0m!V0wbs=DJH3e@;-{wbqWFa0zj3VhlO&BW#fV-Mt5MUQo zScsV})t1VP>JYjcKqUfEf=?x>x7do1uIesKOxHym@#)Tt3Cabi&D&ei=_;;f z;{(@T-w^GVR&{$eROj#RB<=)+KU$-Fk%fh_sv*mNRB8dS31MI$)y8=E&CSRIx?%T* z9U6iv23tSpo-+k9Yu^_GjC3J!5*l9>uB4qhdeS%o-w?bcj2v%`7YCp>kglN9>ApY~ zw!Hh-iO$W%13?J@>EE&7W7DfZ+9sNRp8^-t1Mb~@TBEeIbnnt_>Gotrt4h~yHQ+wl zuJ%+#7CMfrMF1vj!esLD02^h~44K~%0(%fILS-NqGT^}dEzd#!`3|iBUZzke6~f?2 zM=`J+swP4AT1-^m)A2!X6X*i*7Xr!S<*9ywNkEGW&WO4_WO17kQnqJCM;`zP96r`f zPeFbN1JGiaEdbi9dVt*4j*UxS_;_%@RE6P|FG3eK3~ncgP~Of*=x=6kaJ)RY-x1;^ z=tA4S1&A`^O@E`Ze*x6@xLACN+l6>HQ*n9ne!^5K{Y2Uy zv6QGUFnG*s{(=Ri*FGt=)^u0Pwq5JJ_@==UH{dfvhYtUrF;31dj$R&*O71MS-POO{ zG?Nqee2MFexe@#2XXBXj1O>mSI;B79cTKPJMZ&|JI4!Mt?{&pM%-XjK&R--V^Z|zr z^at`r0N!sRMES%D@K|oiKzTaaF+-NMUd%|* zf&l*vbjP!MZA}lhTHUIDKkBLO`<#T8s&{Z8(w|F#nf-sfkJ0EVLJy>(=3NG(_S5bO zfq(ox!>3Ndf=m|0MQdu|NeUfHv3|HW0{*a3#E}19eZA!sSM*=UcAwJ=f$m_;P){48 zD^x9->ig|II{0|*9NV@HpHp;~$`y<2cB0tNnKrtRpcb46B|5L>0u3i`8IEtaATF=@Dla{M%b*e7`UdG3qtz*xA! zh?aWoiG%`^-`?Y20L4Ekdf6VJ%55|Ycw)=-C)c@MfoJhN_|1zgzdjh~{iJW%{eOdO zdJ#XCD5lK)nhaE0*;+z_ex%^I4WMQ6tA_303-K)eFOr+}Z;wW{kvlmoO?sg2+ST2` zNOiX?$mO+QX!N}?dM zo_z!&1IRs|v%szR!)>(n^*TWie`!2HJP#0vz4+^yZ~1LkEctJCz`Cr5G!%YS?QM_Z zjRagT_i?AeOVH!?KZl(B`hQIZ_B(vdyO)Vte|;mB>`RZFlAF8N9_G4~Le6HeuOW}! zR^b2n&B+D>#P$PhNAlb=ijmG$PM!Ac%+I64UA7Do>mm6EihWUWPTfWVrij99xS5G7 zPaae?eweaGqg>f9uk^Ewu)Pl#1^1zKR})fDZ^P)&q1O0za{N1gtr=!^JGuFLrJLDb zHaA0iQ$jWzQHvYTD#m|SPgZy!>)<{YI@elg)*~ckLfzH+Ua})IBmGHT`K#R^M|LZ1 z6y4{q+s8^IL~C1&29h+TbhAzuSV&pNU?|G%4a|zMuKU5v?@!2IHkfrA9u1vA4bs1d zC2$(TgmdB#1@1DCE*Yh8OvwRp05HO#=wf%pzU8j}xFR?o;9crjhqCqdx;|}0(V?R= zy&NW|x<-w&&wZ?Kw;0ddE&qFj;h@N(RqALvhb>Lc<}6i&d?h}ulXMP;FSLFydczrE z7fJH*U`HB>6>`Q)FpyT>_udyzIIAd{$TZ7rGRk;hq z@LDcz7o^|aiO-p-{^G35&-t#~TW3|BU%JlVl=Tg^-OGb7*klf5*y(CRmWz|m@XlY; zUG>bSF44e1c86Qpxx{qAk5$yo>ztO@&>LC8I9`H&w4#EUCe+HURExZYitJ^0lDl{( z0CqP&{TZ=b@G{5W$6I`p?B4%bGIANc=ckGta+%kQ)E|)xbsdWlnTv|V_rvBJtnBeM zz)}QD>j?bRsAqY`m>z&w)+$)GW_R` zZhre5ZeGW&mH>)rZFhnITg9zm{XZpJ$BbBT2GVcra5mF0{>`mmfR#-=YZ-g9LvH5L zuUF4a979<+jt1uxHqe8CT@38)$A}{{Cnys8en}U3zE(Jl)rYKFb~IbGdHm`Hzs9k< zURl##;Xli3oAnDs8U>};y8a9jkN*r++rJ+D_Bldk#-Qs{&x38h&4(FK3KRqHSUa64 z2t*-+Q6K)P4PGG7ZOPM=#^v2yI|A4t7#Uf- z!IF@Xjli=wVK0^JYh7efJ)F){+}nrPl;d<(ylvp+ziW<#Za+D)qZSL)7$4+z7$1X? xLyMPs48no0@taat{P?9Bhkma49_dn!;IqWSimQ=OZaMgti$-RK Date: Thu, 31 Oct 2024 00:45:41 +0000 Subject: [PATCH 25/65] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b2f6dc..1dba8d0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | Plugin Version | NetBox Version | Tested on | | ------------- |:-------------| :-----:| | 0.0.11 | >= 3.7.0 | 3.7.x | -| 0.0.13 | >= 4.0.2 | v4.0.2 | +| 0.0.13 | >= 4.0.2 < v4.1.0 | v4.0.2 | ## Installation @@ -122,4 +122,4 @@ Cable QR code ![Cable QR Code](docs/img/qrcode_cable.png) Device QR code via Jinja2 "text_template" Parameter (Multiline and labeled) -![Cable QR Code](docs/img/qrcode_text_template.png) \ No newline at end of file +![Cable QR Code](docs/img/qrcode_text_template.png) From dc76748894256de5c180982127b85938b10111b3 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Thu, 31 Oct 2024 15:51:21 +0000 Subject: [PATCH 26/65] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0de92e9..1f63de1 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def get_version(rel_path): packages=find_packages(), include_package_data=True, min_version='4.0.2', - max_version='4.0.10', + max_version='4.0.11', package_data={ '': ['*.ttf'], '': ['*.html'], From faa6eee808379958bbc63236bd4623d75d142375 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 01:43:14 +0000 Subject: [PATCH 27/65] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1f63de1..5e5e2c0 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.0.2', + min_version='4.0.0', max_version='4.0.11', package_data={ '': ['*.ttf'], From c8ef1633d8083b6d8ca94b6790c72af3cfbcb786 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 01:44:21 +0000 Subject: [PATCH 28/65] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1dba8d0..d2f4836 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an ## Compatibility -| Plugin Version | NetBox Version | Tested on | -| ------------- |:-------------| :-----:| -| 0.0.11 | >= 3.7.0 | 3.7.x | -| 0.0.13 | >= 4.0.2 < v4.1.0 | v4.0.2 | +| Plugin Version | NetBox Version | Tested on | +| ------------- |:-------------| :-----------:| +| 0.0.11 | 3.7.x | 3.7.x | +| 0.0.14 | 4.0.x | v4.0.11 | ## Installation From 69dfb59a1acfb3cc010575c8b501930826f7bd8b Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 01:44:52 +0000 Subject: [PATCH 29/65] Update version.py --- netbox_qrcode/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 4ae81f3..311f216 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.13" +__version__ = "0.0.14" From 3152999dec1a5e2982f4269912d3568112b34987 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 01:46:09 +0000 Subject: [PATCH 30/65] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5e5e2c0..8c6bd60 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ def get_version(rel_path): description='QR Code generation for netbox objects', long_description=long_description, long_description_content_type="text/markdown", - url='https://github.com/k01ek/netbox-qrcode', + url='https://github.com/netbox-community/netbox-qrcode', author='Nikolay Yuzefovich', author_email='mgk.kolek@gmail.com', packages=find_packages(), From aeb79c7b7dc61239990485ee85db239fda87ce47 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 02:02:55 +0000 Subject: [PATCH 31/65] fixes #75 - replace pkg_resources(deprecated) with importlib --- netbox_qrcode/utilities.py | 8 +++----- netbox_qrcode/version.py | 2 +- setup.py | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/netbox_qrcode/utilities.py b/netbox_qrcode/utilities.py index c1e2f7c..75a4906 100644 --- a/netbox_qrcode/utilities.py +++ b/netbox_qrcode/utilities.py @@ -3,9 +3,7 @@ from io import BytesIO from PIL import Image, ImageFont, ImageDraw - -from pkg_resources import resource_stream - +from importlib import resources as importlib_resources def get_qr_with_text(qr, descr): dsi = get_qr_text(qr.size, descr) @@ -35,7 +33,7 @@ def get_qr_text(max_size, text, font='TahomaBold', font_size=0): text_too_large = True font_size = 56 while text_too_large: - file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) + file_path = importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) try: fnt = ImageFont.truetype(file_path, font_size) except Exception: @@ -47,7 +45,7 @@ def get_qr_text(max_size, text, font='TahomaBold', font_size=0): text_too_large = False font_size -= 1 else: - file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) + file_path=importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) try: fnt = ImageFont.truetype(file_path, font_size) except Exception: diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 311f216..6561790 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.14" +__version__ = "0.0.15" diff --git a/setup.py b/setup.py index 8c6bd60..02c8b78 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.0.0', - max_version='4.0.11', + min_version='4.1.0', + max_version='4.1.99', package_data={ '': ['*.ttf'], '': ['*.html'], From 894d3322edcfac4b346059fd5fb65566fa911f70 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 02:06:11 +0000 Subject: [PATCH 32/65] Update setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 02c8b78..b6670df 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.1.0', - max_version='4.1.99', + min_version='4.0.0', + max_version='4.0.99', package_data={ '': ['*.ttf'], '': ['*.html'], From ae558f91e195f3962b1bb1a1af19ad65574a2f7d Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 02:22:18 +0000 Subject: [PATCH 33/65] Update version.py --- netbox_qrcode/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 6561790..311f216 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.15" +__version__ = "0.0.14" From f7ebd6207746ee34e335c3279581915242c07263 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 02:47:05 +0000 Subject: [PATCH 34/65] refactor conditional importlib for python < 3.12 --- netbox_qrcode/utilities.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/netbox_qrcode/utilities.py b/netbox_qrcode/utilities.py index 75a4906..4a08988 100644 --- a/netbox_qrcode/utilities.py +++ b/netbox_qrcode/utilities.py @@ -1,9 +1,13 @@ import base64 import qrcode - +import sys from io import BytesIO from PIL import Image, ImageFont, ImageDraw -from importlib import resources as importlib_resources + +if sys.version_info.major == 3 and sys.version_info.minor < 12: + from pkg_resources import resource_stream +else: + from importlib import resources as importlib_resources def get_qr_with_text(qr, descr): dsi = get_qr_text(qr.size, descr) @@ -33,7 +37,10 @@ def get_qr_text(max_size, text, font='TahomaBold', font_size=0): text_too_large = True font_size = 56 while text_too_large: - file_path = importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) + if sys.version_info.major == 3 and sys.version_info.minor < 12: + file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) + else: + file_path = importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) try: fnt = ImageFont.truetype(file_path, font_size) except Exception: @@ -45,7 +52,10 @@ def get_qr_text(max_size, text, font='TahomaBold', font_size=0): text_too_large = False font_size -= 1 else: - file_path=importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) + if sys.version_info.major == 3 and sys.version_info.minor < 12: + file_path = resource_stream(__name__, 'fonts/{}.ttf'.format(font)) + else: + file_path = importlib_resources.files(__name__).joinpath('fonts/{}.ttf'.format(font)) try: fnt = ImageFont.truetype(file_path, font_size) except Exception: From 6f2475ea42716c38b299dcbdfac05c9f60b022cc Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 02:53:27 +0000 Subject: [PATCH 35/65] initial --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index b6670df..02c8b78 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.0.0', - max_version='4.0.99', + min_version='4.1.0', + max_version='4.1.99', package_data={ '': ['*.ttf'], '': ['*.html'], From 7d032f4c3d5afe82e1b0490cbbf71d83f2d15602 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 21:44:16 +0000 Subject: [PATCH 36/65] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d2f4836..b75f227 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | ------------- |:-------------| :-----------:| | 0.0.11 | 3.7.x | 3.7.x | | 0.0.14 | 4.0.x | v4.0.11 | +| 0.0.15 | 4.1.x | v4.1.6 | + ## Installation From bd11b4807e355d71946c276762ca8982d9e5eb3f Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 17 Nov 2024 22:06:57 +0000 Subject: [PATCH 37/65] update version to v0.0.15 --- netbox_qrcode/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 311f216..6561790 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.14" +__version__ = "0.0.15" From f2086d9635a1d4a5633ea1c8aaf24c75d153727d Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 11 Jan 2025 01:14:36 +0000 Subject: [PATCH 38/65] Updated for NetBox v4.2.x --- .github/workflows/pub-pypi.yml | 4 ++-- Makefile | 2 +- README.md | 1 + develop/Dockerfile | 4 ++-- develop/docker-compose.yml | 2 +- netbox_qrcode/__init__.py | 2 ++ netbox_qrcode/version.py | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pub-pypi.yml b/.github/workflows/pub-pypi.yml index ebabf78..f06738b 100644 --- a/.github/workflows/pub-pypi.yml +++ b/.github/workflows/pub-pypi.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@main - - name: Set up Python 3.7 + - name: Set up Python 3.12 uses: actions/setup-python@v1 with: - python-version: 3.7 + python-version: 3.12 - name: Install pypa/build run: >- python -m diff --git a/Makefile b/Makefile index 414332d..806014b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VER?=3.12 -NETBOX_VER?=v4.0.2 +NETBOX_VER?=v4.2.1 NAME=netbox-qrcode diff --git a/README.md b/README.md index b75f227..d9a3eb7 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | 0.0.11 | 3.7.x | 3.7.x | | 0.0.14 | 4.0.x | v4.0.11 | | 0.0.15 | 4.1.x | v4.1.6 | +| 0.0.16 | 4.2.x | v4.2.1 | ## Installation diff --git a/develop/Dockerfile b/develop/Dockerfile index a2c56d2..7ac1bbb 100644 --- a/develop/Dockerfile +++ b/develop/Dockerfile @@ -1,7 +1,7 @@ ARG python_ver=3.12 FROM python:${python_ver} -ARG netbox_ver=master +ARG netbox_ver=v4.2.1 ENV PYTHONUNBUFFERED 1 RUN mkdir -p /opt @@ -23,6 +23,6 @@ WORKDIR /source COPY . /source #RUN pip install -r requirements.txt -RUN python setup.py develop +RUN python -m pip install -e . WORKDIR /opt/netbox/netbox/ diff --git a/develop/docker-compose.yml b/develop/docker-compose.yml index 4b7e984..b11a5f3 100644 --- a/develop/docker-compose.yml +++ b/develop/docker-compose.yml @@ -33,7 +33,7 @@ services: - ../netbox_qrcode:/source/netbox_qrcode tty: true postgres: - image: postgres:12 + image: postgres:16 env_file: dev.env volumes: - pgdata_netbox_qrcode:/var/lib/postgresql/data diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index 6d16a96..ba36083 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -10,6 +10,8 @@ class QRCodeConfig(PluginConfig): author = 'Nikolay Yuzefovich' author_email = 'mgk.kolek@gmail.com' required_settings = [] + min_version = '4.2.0' + max_version = '4.2.99' default_settings = { 'with_text': True, 'text_fields': ['name', 'serial'], diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 6561790..d62d967 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.15" +__version__ = "0.0.16" From 7ca1da56c671ff6ffb234b27a5352339c7e268bc Mon Sep 17 00:00:00 2001 From: KJ7ICE <85096517+kj7ice@users.noreply.github.com> Date: Tue, 25 Feb 2025 00:52:26 -0800 Subject: [PATCH 39/65] Use new Netbox version identifier. Fixes netbox-community#93 --- netbox_qrcode/template_content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index d4b44ba..acfbbb1 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -67,7 +67,7 @@ def x_page(self): else: img = get_img_b64(qr_img) try: - if version.parse(settings.VERSION).major >= 3: + if version.parse(settings.RELEASE.version).major >= 3: return self.render( 'netbox_qrcode/qrcode3.html', extra_context={'image': img} ) From b3a33be3b52aa9e528f930a7f5159af5acea1134 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Tue, 25 Feb 2025 11:41:23 +0000 Subject: [PATCH 40/65] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9a3eb7..d001ed7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | 0.0.11 | 3.7.x | 3.7.x | | 0.0.14 | 4.0.x | v4.0.11 | | 0.0.15 | 4.1.x | v4.1.6 | -| 0.0.16 | 4.2.x | v4.2.1 | +| 0.0.17 | 4.2.x | v4.2.4 | ## Installation From 37563a4992d0bfd17bc8462bcad2641c8c98f0e1 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Tue, 25 Feb 2025 11:41:50 +0000 Subject: [PATCH 41/65] Update version.py --- netbox_qrcode/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index d62d967..1f658a4 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.16" +__version__ = "0.0.17" From ddec5aa34b5c2dd9044de5c6a6a5e7badbe7ce69 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Mon, 28 Apr 2025 14:12:45 +0100 Subject: [PATCH 42/65] Initial 4.3 commit --- netbox_qrcode/__init__.py | 4 ++-- netbox_qrcode/template_content.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index ba36083..959382e 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -10,8 +10,8 @@ class QRCodeConfig(PluginConfig): author = 'Nikolay Yuzefovich' author_email = 'mgk.kolek@gmail.com' required_settings = [] - min_version = '4.2.0' - max_version = '4.2.99' +# min_version = '4.2.0' +# max_version = '4.2.99' default_settings = { 'with_text': True, 'text_fields': ['name', 'serial'], diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index acfbbb1..fab78f1 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -17,7 +17,7 @@ def x_page(self): request = self.context['request'] url = request.build_absolute_uri(obj.get_absolute_url()) # get object settings - obj_cfg = config.get(self.model.replace('dcim.', '')) + obj_cfg = config.get(self.models[0].replace('dcim.', '')) if obj_cfg is None: return '' # and ovverride default @@ -80,42 +80,42 @@ def x_page(self): class DeviceQRCode(QRCode): - model = 'dcim.device' + models = ('dcim.device',) def right_page(self): return self.x_page() class RackQRCode(QRCode): - model = 'dcim.rack' + models = ('dcim.rack',) def right_page(self): return self.x_page() class CableQRCode(QRCode): - model = 'dcim.cable' + models = ('dcim.cable',) def left_page(self): return self.x_page() class LocationQRCode(QRCode): - model = 'dcim.location' + models = ('dcim.location',) def left_page(self): return self.x_page() class PowerFeedQRCode(QRCode): - model = 'dcim.powerfeed' + models = ('dcim.powerfeed',) def right_page(self): return self.x_page() class PowerPanelQRCode(QRCode): - model = 'dcim.powerpanel' + models = ('dcim.powerpanel',) def right_page(self): return self.x_page() From e464db6f2c97ebe11cdc5886fe52014ac826f1bf Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Sat, 3 May 2025 01:24:43 +0200 Subject: [PATCH 43/65] example 10 and setuptools --- develop/Dockerfile | 1 + develop/Netbox_Icon.png | Bin 0 -> 2408 bytes develop/docker-compose.yml | 1 + .../README_Configuration_ExampleLabelConf.md | 8 ++++++-- docs/img/Configuration_Label_Example_10.png | Bin 12865 -> 13888 bytes docs/img/Netbox_Icon_Example.png | Bin 0 -> 2744 bytes 6 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 develop/Netbox_Icon.png create mode 100644 docs/img/Netbox_Icon_Example.png diff --git a/develop/Dockerfile b/develop/Dockerfile index a2c56d2..5bca7d4 100644 --- a/develop/Dockerfile +++ b/develop/Dockerfile @@ -7,6 +7,7 @@ ENV PYTHONUNBUFFERED 1 RUN mkdir -p /opt RUN pip install --upgrade pip +RUN pip install setuptools # ------------------------------------------------------------------------------------- # Install NetBox diff --git a/develop/Netbox_Icon.png b/develop/Netbox_Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f77561b5c9f81bda90aabbdef077905fb22c5aca GIT binary patch literal 2408 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 literal 0 HcmV?d00001 diff --git a/develop/docker-compose.yml b/develop/docker-compose.yml index 4b7e984..1f218fb 100644 --- a/develop/docker-compose.yml +++ b/develop/docker-compose.yml @@ -17,6 +17,7 @@ services: - ./dev.env volumes: - ./configuration.py:/opt/netbox/netbox/netbox/configuration.py + - ../docs/img/Netbox_Icon_Example.png:/opt/netbox/netbox/media/image-attachments/Netbox_Icon_Example.png - ../netbox_qrcode:/source/netbox_qrcode tty: true worker: diff --git a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md index 4f5aa1c..4205a41 100644 --- a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md +++ b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md @@ -191,13 +191,18 @@ This way, many label design options should be possible. The design of the label ![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) +Save this Netbox icon as an example in the following Netbox folder: +/opt/netbox/netbox/media/image-attachments/Netbox_Icon_Example.png + +![Cable QR Code](/docs/img/Netbox_Icon_Example.png) + ```Python 'device_2': { 'with_qr': False, 'text_align_horizontal': 'center', 'text_align_vertical': 'middle', 'title': 'Example', - 'text_template': '

' + 'text_template': '

' '{{ obj.name }}
' '

' 'Device: {{ obj.id|stringformat:"07d" }}' @@ -208,7 +213,6 @@ This way, many label design options should be possible. The design of the label 'label_edge_top': '0mm', 'label_edge_left': '0mm', 'label_edge_right': '0mm', - }, ``` ## Example 11 - Cable Label vertical writing. diff --git a/docs/img/Configuration_Label_Example_10.png b/docs/img/Configuration_Label_Example_10.png index 33897e477161bc9511b0a77142e2472c9c4634c4..787cf69eb3ab18214a80da6eb1b4d7527194f44c 100644 GIT binary patch literal 13888 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 literal 12865 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 SyC%91-Vao@RPvRc`u#6FD5sAA diff --git a/docs/img/Netbox_Icon_Example.png b/docs/img/Netbox_Icon_Example.png new file mode 100644 index 0000000000000000000000000000000000000000..4e6b6b88981dca210515d9d5e3b59d45b6c1449d GIT binary patch literal 2744 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 y)8DNahGABOhciNH+q=>0000 Date: Sat, 3 May 2025 22:41:34 +0100 Subject: [PATCH 44/65] Initial fixes for NetBox 4.3 --- CODEOWNERS | 2 +- README.md | 11 +++++--- netbox_qrcode/template_content.py | 30 +++++++++++---------- netbox_qrcode/template_content_functions.py | 2 +- netbox_qrcode/utilities.py | 1 + netbox_qrcode/version.py | 2 +- setup.py | 3 +-- 7 files changed, 28 insertions(+), 23 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 1d3b141..712676d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @k01ek @cruse1977 @natm +* @k01ek @cruse1977 diff --git a/README.md b/README.md index 284cd5f..5c3aa0a 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,13 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an ## Compatibility -| Plugin Version | NetBox Version | Tested on | -| ------------- |:-------------| :-----:| -| 0.0.11 | >= 3.7.0 | 3.7.x | -| 0.0.13 | >= 4.0.2 | v4.0.2 | +| Plugin Version | NetBox Version | Tested on | +| ------------- |:-------------| :-----------:| +| 0.0.11 | 3.7.x | 3.7.x | +| 0.0.14 | 4.0.x | v4.0.11 | +| 0.0.15 | 4.1.x | v4.1.6 | +| 0.0.17 | 4.2.x | v4.2.4 | + Netbox version 2 is no longer supported. diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index df9c173..cf49d68 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -42,7 +42,7 @@ def Create_SubPluginContent(self, labelDesignNo): # Create plugin using template try: - if version.parse(settings.VERSION).major >= 3: + if version.parse(settings.RELEASE.version).major >= 3: render = self.render( 'netbox_qrcode/qrcode3.html', extra_context={ @@ -97,7 +97,7 @@ def Create_PluginContent(self): for i in range(2, 11): - configName = self.model.replace('dcim.', '') + '_' + str(i) + configName = self.models[0].replace('dcim.', '') + '_' + str(i) obj_cfg = config.get(configName) # Load configuration for additional label if possible. if(obj_cfg): @@ -112,42 +112,42 @@ def Create_PluginContent(self): # Class for creating a QR code for the model: Device class DeviceQRCode(QRCode): - model = 'dcim.device' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.device',) # Info for Netbox in which model the plugin should be integrated. def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Rack class RackQRCode(QRCode): - model = 'dcim.rack' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.rack',) # Info for Netbox in which model the plugin should be integrated. def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Cable class CableQRCode(QRCode): - model = 'dcim.cable' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.cable',) # Info for Netbox in which model the plugin should be integrated. def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Location class LocationQRCode(QRCode): - model = 'dcim.location' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.location',) # Info for Netbox in which model the plugin should be integrated. def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Feed class PowerFeedQRCode(QRCode): - model = 'dcim.powerfeed' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.powerfeed',) # Info for Netbox in which model the plugin should be integrated. def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Panel class PowerPanelQRCode(QRCode): - model = 'dcim.powerpanel' # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.powerpanel',) # Info for Netbox in which model the plugin should be integrated. def right_page(self): return self.Create_PluginContent() @@ -155,12 +155,14 @@ def right_page(self): ################################## # Other plugins support +# Commenting out (for now) - make this work on core models first. # Class for creating a QR code for the Plugin: Netbox-Inventory (https://github.com/ArnesSI/netbox-inventory) -class Plugin_Netbox_Inventory(QRCode): - model = 'netbox_inventory.asset' # Info for Netbox in which model the plugin should be integrated. - - def right_page(self): - return self.Create_PluginContent() +#class Plugin_Netbox_Inventory(QRCode): +# models = ()'netbox_inventory.asset' # Info for Netbox in which model the plugin should be integrated. +# +# def right_page(self): +# return self.Create_PluginContent() # Connects Netbox Core with the plug-in classes -template_extensions = [DeviceQRCode, RackQRCode, CableQRCode, LocationQRCode, PowerFeedQRCode, PowerPanelQRCode, Plugin_Netbox_Inventory] \ No newline at end of file +# Removed , Plugin_Netbox_Inventory] +template_extensions = [DeviceQRCode, RackQRCode, CableQRCode, LocationQRCode, PowerFeedQRCode, PowerPanelQRCode] \ No newline at end of file diff --git a/netbox_qrcode/template_content_functions.py b/netbox_qrcode/template_content_functions.py index e9edfb9..95774c5 100644 --- a/netbox_qrcode/template_content_functions.py +++ b/netbox_qrcode/template_content_functions.py @@ -24,7 +24,7 @@ def config_for_modul(parentSelf, labelDesignNo): # Collect the QR code plugin configuration for the specific object such as device, rack etc. # and overwrite the default configuration fields. - obj_cfg = config.get(parentSelf.model.replace('dcim.', '') + confModulsufix) # get spezific object settings + obj_cfg = config.get(parentSelf.models[0].replace('dcim.', '') + confModulsufix) # get spezific object settings if obj_cfg is not None: config.update(obj_cfg) # Ovverride default confiv Values diff --git a/netbox_qrcode/utilities.py b/netbox_qrcode/utilities.py index e0cd376..a94fa36 100644 --- a/netbox_qrcode/utilities.py +++ b/netbox_qrcode/utilities.py @@ -2,6 +2,7 @@ import qrcode from io import BytesIO + # ****************************************************************************************** # Includes useful tools to create the content. # ****************************************************************************************** diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 4ae81f3..1f658a4 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.13" +__version__ = "0.0.17" diff --git a/setup.py b/setup.py index 0de92e9..167a918 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,7 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.0.2', - max_version='4.0.10', +# max_version='4.0.10', package_data={ '': ['*.ttf'], '': ['*.html'], From 7b2d3ffc29e0c87604eb92d6081bd96091e9a7b6 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 22:46:32 +0100 Subject: [PATCH 45/65] Minor documentation corrections --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5c3aa0a..4b0673a 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ If the print does not look like the preview in the Netbox, first try to get a pe ![ShowImage](/docs/img/Configuration_Printer_WordPreview.png) -Here is an example of what needs to be considered to print borderless from a Word document. [Go to: Eexample Zebra ZM400 300dpi label printer and a label 56x32mm. >>](/docs/img/Configuration_Printer_ZM400.png) +Here is an example of what needs to be considered to print borderless from a Word document. [Go to: Example Zebra ZM400 300dpi label printer and a label 56x32mm. >>](/docs/img/Configuration_Printer_ZM400.png) ### Browser Print Settings correctly @@ -67,7 +67,8 @@ When you press the “Print” button, there are some print properties that are | Options --> Print backgrounds | disable | #### Chrome: -The worst part was finding the right parameters for Chrome. Because it always turned it differently when printing than the print preview shows. +Chrome can alter settings between printing and the print preview, therefore the below settings are recomended + | Parameter | Value | | --------------------------------------------- | --------------------------- | | Layout | Portrait | @@ -90,7 +91,6 @@ You can customise the label as you wish, even 2 different labels for the ‘Devi ![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) ## Contributing -Developing tools for this project based on [ntc-netbox-plugin-onboarding](https://github.com/networktocode/ntc-netbox-plugin-onboarding) repo. Issues and pull requests are welcomed. From b606edaece994fc4f30bd49da01068dc36aea9de Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:04:10 +0100 Subject: [PATCH 46/65] merge resolutions --- netbox_qrcode/__init__.py | 2 ++ setup.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index d525620..3f914a8 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -10,6 +10,8 @@ class QRCodeConfig(PluginConfig): author = 'Nikolay Yuzefovich' author_email = 'mgk.kolek@gmail.com' required_settings = [] +# min_version = '4.2.0' +# max_version = '4.2.99' default_settings = { ################################## diff --git a/setup.py b/setup.py index 167a918..2e83ba8 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,8 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, -# max_version='4.0.10', + min_version='4.1.0', + max_version='4.1.99', package_data={ '': ['*.ttf'], '': ['*.html'], From 330073d347458c7de9d680ecebf35b652bc136ef Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:09:44 +0100 Subject: [PATCH 47/65] further merge resolutions --- README.md | 8 ++++---- netbox_qrcode/template_content.py | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4b0673a..be1adc7 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,6 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | 0.0.15 | 4.1.x | v4.1.6 | | 0.0.17 | 4.2.x | v4.2.4 | - -Netbox version 2 is no longer supported. - ## Installation If Netbox was installed according to the standard installation instructions. It may be necessary to activate the virtual environment. @@ -103,4 +100,7 @@ Rack QR code ![Rack QR Code](docs/img/qrcode_rack.png) Cable QR code -![Cable QR Code](docs/img/qrcode_cable.png) \ No newline at end of file +![Cable QR Code](docs/img/qrcode_cable.png) + +Device QR code via Jinja2 "text_template" Parameter (Multiline and labeled) +![Cable QR Code](docs/img/qrcode_text_template.png) \ No newline at end of file diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index cf49d68..a51ccfb 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -112,43 +112,43 @@ def Create_PluginContent(self): # Class for creating a QR code for the model: Device class DeviceQRCode(QRCode): - models = ('dcim.device',) # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.device',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Rack class RackQRCode(QRCode): - models = ('dcim.rack',) # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.rack',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Cable class CableQRCode(QRCode): - models = ('dcim.cable',) # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.cable',) def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Location class LocationQRCode(QRCode): - models = ('dcim.location',) # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.location',) def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Feed class PowerFeedQRCode(QRCode): - models = ('dcim.powerfeed',) # Info for Netbox in which model the plugin should be integrated. + models = ('dcim.powerfeed',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Panel class PowerPanelQRCode(QRCode): - models = ('dcim.powerpanel',) # Info for Netbox in which model the plugin should be integrated. - + models = ('dcim.powerpanel',) + def right_page(self): return self.Create_PluginContent() From f78f70d7aaa0e71d05042d892eb2b268624de3fd Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:10:49 +0100 Subject: [PATCH 48/65] further merge resolutions --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index be1adc7..fa71679 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | 0.0.15 | 4.1.x | v4.1.6 | | 0.0.17 | 4.2.x | v4.2.4 | + ## Installation If Netbox was installed according to the standard installation instructions. It may be necessary to activate the virtual environment. From 4da00e751b60e760c1b9a42faddd6c0f3abb5567 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:17:15 +0100 Subject: [PATCH 49/65] whitespace removal --- netbox_qrcode/template_content.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index a51ccfb..511e180 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -112,43 +112,43 @@ def Create_PluginContent(self): # Class for creating a QR code for the model: Device class DeviceQRCode(QRCode): - models = ('dcim.device',) + models = ('dcim.device',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Rack class RackQRCode(QRCode): - models = ('dcim.rack',) + models = ('dcim.rack',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Cable class CableQRCode(QRCode): - models = ('dcim.cable',) + models = ('dcim.cable',) def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Location class LocationQRCode(QRCode): - models = ('dcim.location',) + models = ('dcim.location',) def left_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Feed class PowerFeedQRCode(QRCode): - models = ('dcim.powerfeed',) + models = ('dcim.powerfeed',) def right_page(self): return self.Create_PluginContent() # Class for creating a QR code for the model: Power Panel class PowerPanelQRCode(QRCode): - models = ('dcim.powerpanel',) - + models = ('dcim.powerpanel',) + def right_page(self): return self.Create_PluginContent() From d83143d60b4c2b9c1a8a9c8c5104d6ad1658ecbb Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:33:42 +0100 Subject: [PATCH 50/65] documentation tweaks --- README.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ee90612..71d0be2 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,21 @@ Restart NetBox and add `netbox-qrcode` to your local_requirements.txt ## Configuration -### Set the label printer correctly +### Label Design + +Extensive label customisation is possible, it's also possible to include different labels for each objects, for example 2 lavels for view. + +For advice on configuration please see the two links below: + +- [Go to Configuration >>](docs/README_Subpages/README_Configuration.md) +- [Go to Example label configurations >>](docs/README_Subpages/README_Configuration_ExampleLabelConf.md) + +![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) + + +### Printing + +#### Set the label printer correctly If the print does not look like the preview in the Netbox, first try to get a perfect print using Word. As many printer settings also have an influence on the print result. Borderless printing is possible if the printer (e.g. thermal transfer printer) supports this. @@ -49,11 +63,11 @@ If the print does not look like the preview in the Netbox, first try to get a pe Here is an example of what needs to be considered to print borderless from a Word document. [Go to: Example Zebra ZM400 300dpi label printer and a label 56x32mm. >>](/docs/img/Configuration_Printer_ZM400.png) -### Browser Print Settings correctly +#### Browser Print Settings correctly When you press the “Print” button, there are some print properties that are added by the browser. However, these interfere with the print result. They should therefore be deactivated. -#### Firefox: +##### Firefox: | Parameter | Value | | --------------------------------------------- | --------------------------- | @@ -64,7 +78,7 @@ When you press the “Print” button, there are some print properties that are | Options --> Print headers and footers | disable | | Options --> Print backgrounds | disable | -#### Chrome: +##### Chrome: Chrome can alter settings between printing and the print preview, therefore the below settings are recomended | Parameter | Value | @@ -79,14 +93,7 @@ Chrome can alter settings between printing and the print preview, therefore the ![Image](/docs/img/Configuration_Browser_Print_Settings.png) -### Design your own label - -You can customise the label as you wish, even 2 different labels for the ‘Device’ view, for example, are possible. - -- [Go to Configuration >>](docs/README_Subpages/README_Configuration.md) -- [Go to Example label configurations >>](docs/README_Subpages/README_Configuration_ExampleLabelConf.md) -![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) ## Contributing @@ -102,6 +109,3 @@ Rack QR code Cable QR code ![Cable QR Code](docs/img/qrcode_cable.png) - -Device QR code via Jinja2 "text_template" Parameter (Multiline and labeled) -![Cable QR Code](docs/img/qrcode_text_template.png) From 25f1140b1f0f0901b9b188cef9c8a7bac469c2cd Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sat, 3 May 2025 23:39:00 +0100 Subject: [PATCH 51/65] documentation tweaks --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 71d0be2..76b9f92 100644 --- a/README.md +++ b/README.md @@ -41,19 +41,19 @@ Restart NetBox and add `netbox-qrcode` to your local_requirements.txt ### Label Design -Extensive label customisation is possible, it's also possible to include different labels for each objects, for example 2 lavels for view. +Extensive label customisation is possible, it's also possible to include different labels for each object type, for example 2 labels for the Device view. For advice on configuration please see the two links below: -- [Go to Configuration >>](docs/README_Subpages/README_Configuration.md) -- [Go to Example label configurations >>](docs/README_Subpages/README_Configuration_ExampleLabelConf.md) +- [General Configuration >>](docs/README_Subpages/README_Configuration.md) +- [Configuration Examples >>](docs/README_Subpages/README_Configuration_ExampleLabelConf.md) ![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) ### Printing -#### Set the label printer correctly +#### Setting the label printer If the print does not look like the preview in the Netbox, first try to get a perfect print using Word. As many printer settings also have an influence on the print result. Borderless printing is possible if the printer (e.g. thermal transfer printer) supports this. @@ -63,7 +63,7 @@ If the print does not look like the preview in the Netbox, first try to get a pe Here is an example of what needs to be considered to print borderless from a Word document. [Go to: Example Zebra ZM400 300dpi label printer and a label 56x32mm. >>](/docs/img/Configuration_Printer_ZM400.png) -#### Browser Print Settings correctly +#### Setting Browser Print Settings When you press the “Print” button, there are some print properties that are added by the browser. However, these interfere with the print result. They should therefore be deactivated. From c81508d60dbcdc08a506d693352f00295ff3490b Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 00:29:56 +0100 Subject: [PATCH 52/65] Add Module support --- develop/Dockerfile | 6 +++--- netbox_qrcode/__init__.py | 2 ++ netbox_qrcode/template_content.py | 9 ++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/develop/Dockerfile b/develop/Dockerfile index 8e64740..207d33d 100644 --- a/develop/Dockerfile +++ b/develop/Dockerfile @@ -1,13 +1,13 @@ ARG python_ver=3.12 FROM python:${python_ver} -ARG netbox_ver=v4.2.1 +ARG netbox_ver=v4.3,0 ENV PYTHONUNBUFFERED 1 RUN mkdir -p /opt -RUN pip install --upgrade pip -RUN pip install setuptools +RUN /usr/local/bin/uv pip install --upgrade pip +RUN /usr/local/bin/uv pip install setuptools # ------------------------------------------------------------------------------------- # Install NetBox diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index 3f914a8..f5c6bf5 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -104,6 +104,8 @@ class QRCodeConfig(PluginConfig): 'text_fields': ['name'] }, + 'module': { + }, 'logo': '', } diff --git a/netbox_qrcode/template_content.py b/netbox_qrcode/template_content.py index 2ed8d62..3226782 100644 --- a/netbox_qrcode/template_content.py +++ b/netbox_qrcode/template_content.py @@ -153,6 +153,13 @@ class PowerPanelQRCode(QRCode): def right_page(self): return self.Create_PluginContent() +# Class for dcim.module +class ModuleQRCode(QRCode): + models = ('dcim.module',) + + def right_page(self): + return self.Create_PluginContent() + ################################## # Other plugins support @@ -166,4 +173,4 @@ def right_page(self): # Connects Netbox Core with the plug-in classes # Removed , Plugin_Netbox_Inventory] -template_extensions = [DeviceQRCode, RackQRCode, CableQRCode, LocationQRCode, PowerFeedQRCode, PowerPanelQRCode] \ No newline at end of file +template_extensions = [DeviceQRCode, ModuleQRCode, RackQRCode, CableQRCode, LocationQRCode, PowerFeedQRCode, PowerPanelQRCode] \ No newline at end of file From 70b0b9fd9dbf87254b2e09c8051d27ceaed2c07c Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 00:31:17 +0100 Subject: [PATCH 53/65] Update README with full model list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76b9f92..8cce28d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Netbox QR Code Plugin -[Netbox](https://github.com/netbox-community/netbox) plugin for generate QR codes for objects: Rack, Device, Cable. +[Netbox](https://github.com/netbox-community/netbox) plugin for generate QR codes for objects: Device, Module, Cable, Powerfeed, Powerpanel, Location This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) and [Pillow](https://github.com/python-pillow/Pillow) python library From e2cbbb7f948dda57a1095908359f56925f2cb691 Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Sun, 4 May 2025 01:38:03 +0200 Subject: [PATCH 54/65] - The debug vscode always used Netbox V4.1.11 instead of the specified version. - Always call cbuild automatically when debugging and debug-vscode so that this is not forgotten and you have an incorrect version. --- Makefile | 9 +++++---- develop/docker-compose-debug.yml | 1 - develop/docker-compose.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 743680e..32409b1 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # Parameter PYTHON_VER?=3.12 -NETBOX_VER?=v4.0.2 +NETBOX_VER?=v4.3.0 NAME=netbox-qrcode @@ -22,15 +22,16 @@ cbuild: --build-arg python_ver=${PYTHON_VER} # Start Docker with terminal window output -debug: +debug: cbuild @echo "Starting Netbox .. " docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} up # Start Docker with terminal window output. # Brakepoints in e.g. Python files are supported in VSCode. Changes in Python and HTML are applied after saving. -debug-vscode: +debug-vscode: cbuild @echo "Starting Netbox debug for VSCode.. " - docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} -f ${COMPOSE_FILE_DEBUG} up --build + docker-compose -f ${COMPOSE_FILE} -p ${BUILD_NAME} -f ${COMPOSE_FILE_DEBUG} up +# --build # Start Docker without connecting to the terminal window. (Runs independently of the terminal window.). start: diff --git a/develop/docker-compose-debug.yml b/develop/docker-compose-debug.yml index 3bf98a4..eb5357a 100644 --- a/develop/docker-compose-debug.yml +++ b/develop/docker-compose-debug.yml @@ -4,7 +4,6 @@ --- services: netbox: - # The following commands are executed # - Install Current Version of DebugPy for Debug in Visual Studio Code https://github.com/microsoft/debugpy # - PYDEVD_DISABLE_FILE_VALIDATION=1 --> You’re telling the debugger to skip this validation and proceed with debugging diff --git a/develop/docker-compose.yml b/develop/docker-compose.yml index 02fd00b..fa99c35 100644 --- a/develop/docker-compose.yml +++ b/develop/docker-compose.yml @@ -41,7 +41,7 @@ services: - ../netbox_qrcode:/source/netbox_qrcode tty: true postgres: - image: postgres:12 + image: postgres:14 env_file: dev.env volumes: - pgdata_netbox_qrcode:/var/lib/postgresql/data From d858a7da2d6520e9b8fd1b6125d7c3821c6a2262 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 20:31:25 +0100 Subject: [PATCH 55/65] Update Dockerfile --- develop/Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/develop/Dockerfile b/develop/Dockerfile index 207d33d..3089d3b 100644 --- a/develop/Dockerfile +++ b/develop/Dockerfile @@ -1,13 +1,14 @@ ARG python_ver=3.12 FROM python:${python_ver} -ARG netbox_ver=v4.3,0 +ARG netbox_ver=master ENV PYTHONUNBUFFERED 1 RUN mkdir -p /opt -RUN /usr/local/bin/uv pip install --upgrade pip -RUN /usr/local/bin/uv pip install setuptools +RUN pip install --upgrade pip +RUN pip install setuptools + # ------------------------------------------------------------------------------------- # Install NetBox From 9632facf90d931be43dfa7c3f11d409fec3b9599 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 20:34:00 +0100 Subject: [PATCH 56/65] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 806014b..79bc441 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VER?=3.12 -NETBOX_VER?=v4.2.1 +NETBOX_VER?=v4.3.0 NAME=netbox-qrcode From a143445daada10ce1d1497f576c66fc612be37b0 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 20:44:32 +0100 Subject: [PATCH 57/65] Update Dockerfile --- develop/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop/Dockerfile b/develop/Dockerfile index 3089d3b..49a950a 100644 --- a/develop/Dockerfile +++ b/develop/Dockerfile @@ -1,7 +1,7 @@ ARG python_ver=3.12 FROM python:${python_ver} -ARG netbox_ver=master +ARG netbox_ver=main ENV PYTHONUNBUFFERED 1 RUN mkdir -p /opt From 1a79cc0a3b5c2285493292b8ce43df98ab4d5425 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 4 May 2025 21:50:47 +0100 Subject: [PATCH 58/65] chore: Add Tome to CODEOWNERS --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 712676d..eeda945 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @k01ek @cruse1977 +* @k01ek @cruse1977 @cvitan From 5fbf828912867d9fa6fb9647947c895a960a8ead Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 5 May 2025 14:45:36 +0200 Subject: [PATCH 59/65] - Optics: Logo for label templates made transparent to avoid white box on gray label. - For internal tests: All 12 label examples added to Configuration.py to be able to quickly check functionality. - Documentation: Examples revised to be consistent with Configuration.py and to facilitate future maintenance. Also checked all examples so that the example code matches the image and no brackets are missing. - QR code display corrected, QR code so that it can be displayed again in stretched format (e.g. 2:3). --- develop/configuration.py | 221 +++++++++++++++++- docs/README_Subpages/README_Configuration.md | 25 +- .../README_Configuration_ExampleLabelConf.md | 84 +++---- docs/img/Configuration_Label_Example_10.png | Bin 13888 -> 16380 bytes docs/img/Netbox_Icon_Example.png | Bin 2744 -> 2744 bytes .../netbox_qrcode/qrcode3_sub_qrcode.html | 2 +- 6 files changed, 287 insertions(+), 45 deletions(-) diff --git a/develop/configuration.py b/develop/configuration.py index b564c3f..f953bb6 100644 --- a/develop/configuration.py +++ b/develop/configuration.py @@ -157,7 +157,226 @@ # Plugins configuration settings. These settings are used by various plugins that the user may have installed. # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. -# PLUGINS_CONFIG = {} +PLUGINS_CONFIG = { + + 'netbox_qrcode': { + + ## Example Template for Devices + + 'device_2': { + 'title': 'Example 2 (Template for Device)', + 'text_template': '

{{ obj.name }}
Device: {{ obj.id|stringformat:"07d" }}', + 'font_size': '4mm', + 'label_qr_width': '20mm', + 'label_qr_height': '30mm', + 'label_qr_text_distance': '2mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '2mm', + 'label_edge_right': '2mm', + 'logo': '/media/image-attachments/Netbox_Icon_Example.png', + }, + + 'device_3': { + 'title': 'Example 3 (Template for Device)', + 'font_size': '4mm', + 'label_qr_width': '15mm', + 'label_qr_height': '15mm', + 'label_qr_text_distance': '2mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '2mm', + 'label_edge_right': '2mm', + 'text_location': 'left', + }, + + 'device_4': { + 'title': 'Example 4 (Template for Device)', + 'font_size': '4mm', + 'text_align_horizontal': 'center', + 'text_align_vertical': 'middle', + 'label_qr_width': '20mm', + 'label_qr_height': '20mm', + 'label_qr_text_distance': '0mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '0mm', + 'label_edge_right': '0mm', + 'with_text': True, + 'with_qr': False, + }, + + 'device_5': { + 'title': 'Example 5 (Template for Device)', + 'font_size': '4mm', + 'text_align_horizontal': 'left', + 'text_align_vertical': 'middle', + 'label_qr_width': '20mm', + 'label_qr_height': '20mm', + 'label_qr_text_distance': '0mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '2mm', + 'label_edge_right': '0mm', + 'with_text': True, + 'with_qr': False, + }, + + 'device_6': { + 'title': 'Example 6 (Template for Device)', + 'font_size': '4mm', + 'label_qr_width': '20mm', + 'label_qr_height': '20mm', + 'label_qr_text_distance': '0mm', + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '0mm', + 'label_edge_right': '0mm', + 'with_text': False, + 'with_qr': True, + }, + + 'device_7': { + 'title': 'Example 7 (Template for Device)', + 'font_size': '4mm', + 'label_qr_width': '20mm', + 'label_qr_height': '20mm', + 'label_qr_text_distance': '0mm', + 'label_width': '32mm', + 'label_height': '56mm', + 'label_edge_top': '2mm', + 'label_edge_left': '0mm', + 'label_edge_right': '0mm', + 'text_location': 'up', + 'text_align_horizontal': 'center', + 'text_align_vertical': 'top', + 'label_edge_bottom': '2mm', + }, + + 'device_8': { + 'title': 'Example 8 (Template for Device)', + 'font_size': '4mm', + 'label_qr_width': '20mm', + 'label_qr_height': '20mm', + 'label_qr_text_distance': '2mm', + 'label_width': '32mm', + 'label_height': '56mm', + 'label_edge_top': '2mm', + 'label_edge_left': '0mm', + 'label_edge_right': '0mm', + 'text_location': 'down', + 'text_align_horizontal': 'center', + 'text_align_vertical': 'top', + }, + + 'device_9': { + 'title': 'Example 9 (Template for Device)', + 'font_size': '1mm', + 'label_qr_width': '9mm', + 'label_qr_height': '9mm', + 'label_qr_text_distance': '1mm', + 'label_width': '25mm', + 'label_height': '10mm', + 'label_edge_top': '0.2mm', + 'label_edge_left': '0.2mm', + 'label_edge_right': '0mm', + }, + + 'device_10': { + 'title': 'Example 10 (Template for Device)', + 'with_qr': False, + 'text_align_horizontal': 'center', + 'text_align_vertical': 'middle', + 'title': 'Example', + 'text_template': '

' + '{{ obj.name }}
' + '

' + 'Device: {{ obj.id|stringformat:"07d" }}' + '

😝 My label design 😝

', + + 'label_width': '56mm', + 'label_height': '32mm', + 'label_edge_top': '0mm', + 'label_edge_left': '0mm', + 'label_edge_right': '0mm', + }, + + + ## Example Template for Cables + + 'cable': { + 'title': 'Example 1 (Template for Cable)', + 'with_qr': False, + 'label_edge_left': '0.00mm', + 'label_edge_right': '0.00mm', + 'label_edge_top': '0.00mm', + 'text_align_vertical': 'middle', + 'text_align_horizontal': 'center', + 'text_template': '' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '{{ obj.label }}
' + '
' + }, + + 'cable_2': { + 'title': 'Example 2 (Template for Cable)', + 'with_qr': False, + 'label_edge_left': '0.00mm', + 'label_edge_right': '0.00mm', + 'label_edge_top': '0.00mm', + 'text_align_vertical': 'middle', + 'text_align_horizontal': 'center', + + # QR-Code Image File + 'qr_version': 1, + 'qr_error_correction': 1, + 'qr_box_size': 2, + 'qr_border': 0, + + 'text_template': '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + } + } +} # When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to # prefer IPv4 instead. diff --git a/docs/README_Subpages/README_Configuration.md b/docs/README_Subpages/README_Configuration.md index f24617b..9cd4a71 100644 --- a/docs/README_Subpages/README_Configuration.md +++ b/docs/README_Subpages/README_Configuration.md @@ -336,13 +336,34 @@ The parameters that can be used to design the label are listed below. Enables a logo/image to be inserted in combination with the `text_template` parameter. - **Image File size:** + ### Image File size: - To keep the traffic and performance high, the image should be as small as necessary. An image with 1920x1200px and 30MB is nonsensical if it is to be 1.00x3.00cm in size at the end. The larger the image, the longer the Base64 text. If possible, the logo is completely black and not gray or colored, which makes it even smaller. (Sufficient for thermal transfer printers). + ℹ️ To keep the traffic and performance high, the image should be as small as necessary. An image with 1920x1200px and 30MB is nonsensical if it is to be 1.00x3.00cm in size at the end. The larger the image, the longer the Base64 text. If possible, the logo is completely black and not gray or colored, which makes it even smaller. (Sufficient for thermal transfer printers). Here are a few helpful online tools: Resize image online (in mm): https://image.pi7.org/resize-image-in-mm Image compression (e.g. 100kb to 2kb): https://tinypng.com/ + + ### Version 1: Link to the image (Recommended) + + The media folder to provide the plugin with images is located here: + /opt/netbox/netbox/media/ + + ```Python + 'logo': '/media/image-attachments/Netbox_Icon_Example.png', + ``` + Below is how you can embed the logo in a text. + It is an HTML block that contains the image {{ logo }} as well as the name {{ obj.name }} and the ID of the object {{ obj.id }} in 3 lines. + + ```Python + 'text_template': '

{{ obj.name }}
Device: {{ obj.id }}
', + ``` + ℹ️ The logo link can be inserted directly in the text_template, but this is not recommended as the logo parameter may be included in the standard layout in future versions. + + ℹ️ You can specify a link to the image file in Netbox or an external http:// or https:// link as long as the client can reach this image. + + ### Version 2: Embedded Base64 Image (not recommended due to complexity) + Convert image to Base64 string.: https://www.base64-image.de/ Your image should have the following text format at the end. diff --git a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md index 4205a41..55c35fb 100644 --- a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md +++ b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md @@ -1,19 +1,19 @@ # Example label configurations -## Example 1 - Default +## Example 1 (Device) - Default Standard label without customization ![Cable QR Code](/docs/img/Configuration_Label_Example_01.png) -## Example 2 +## Example 2 (Device) QR code Higher than wide, with own logo Device name and Device ID filled with zeros. ![Cable QR Code](/docs/img/Configuration_Label_Example_02.png) ```Python 'device_2': { - 'title': 'Example', - 'text_template': '

{{ obj.name }}
Device: {{ obj.id|stringformat:"07d" }}', + 'title': 'Example 2 (Template for Device)', + 'text_template': '

{{ obj.name }}
Device: {{ obj.id|stringformat:"07d" }}', 'font_size': '4mm', 'label_qr_width': '20mm', 'label_qr_height': '30mm', @@ -23,17 +23,17 @@ QR code Higher than wide, with own logo Device name and Device ID filled with ze 'label_edge_top': '0mm', 'label_edge_left': '2mm', 'label_edge_right': '2mm', - 'logo': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMoAAAA5CAMAAABAvUQtAAAC8VBMVEUAAAAaGhsWGyAYGx4QFBhVVVVtbm4BAwUUFhlMTk8GEh47OzzBwcEWGBu3vcPExskiJisFBgYcHR4FChBAREkYJzgOIDQFBgccHyEuNTw1Njhvb292hZUKCwwMDg8UFRYSFBYgISQtLzI+P0BodYTR1NYBAQINDxEaHB4lKCwiIyRFSEs/SFJpb3aFj5iBhIecnqBwcnWGiIswQ1l1gpCfpKnNzs/EyMvS1NcICQkNDg4ICQoREhMeHyAuLzAUFxsWGRwbHyRfYWRwdHl5foNmaWxydXh5foMGDx18ipmNjo+nqayNlJuqr7QAAQIMDQ4NDg8QERIcICQVFxkdIygpLC8vLzAxNjwDCA5CREdQU1ZZXF9DRUdscHVeX2BTVVZiY2RhY2V0dneJjI9dZW0fMUY8TFwwQ1d0fYextruVlpehoaFBVmzMz9GCjZo8TF0XFxcoKSsTFRgPERQrLC4aHR40NzoDCA01NTY9P0E+QUQYGh06PkM5Ojs4PUNDQ0RJTE8/QEFITFBQUlRFR0pAQ0YQFBhfYGNQUlY6Q0xJSktkaG02ODwdIit9f4JcYmmAhIc3QUtqa22mqKqUl5pncXtXY3Gmqa67vL1mcn+RmKBXZHO0u8FrfZCcnZ7M1+EKCgoTExIHCgwECQ4XHCAlJigeIyhGR0gtMTVAQkQfIylERkhOTU1QUVQ4PkQPEhZWWVwcKDQsMjgyPUhIUlw6Qkp9fn8VGBt3en6HiYstNT1jZGVtcXREUFx+hYxdYmdNUleFiIwqOkxfYWRATFmpqqtKVmIIGjBKV2V0eX4nPFOWnKNNXnGioqKampoACBy5vcCOk5lGW3CQkpUAAAMAAAA5OTkYICgAAAMIDRM1OT4pMjoGCQw0NjcnLDIfKzdmZWQNGyoaJjJVVVZZXWEAChYIEx+Bf31iYWAOFh8fLz9VZHRPWGFDRkpUVlmssLYMGip1dnaytbaTorMqMTiYqb09WXgAAAABAQIEBAQCAwT+4jRdAAAA93RSTlMA36OYtoaE8eWKDK4rtAYJyPnc1adTJv3QxLKDHPXw6ebZya06Evzu39HKqI5dUE5NTEI9JSQgHRn38/Ps2by1qKGLdG1pXVdXOTgWFA/39OXh4ODLxMK6uq+llouBfnt5c2dkXkM4MTAvLikfFg0I4tnXz83Jv7q4t7OzsLCrqaekopycnJeVkpGLiYd+bWpoX1VHR0VCPDc1MysjIRwO/vTv3NXS0cXCvby5t6ugoJyXk46Dfn19eHNvbmtmZGReWFhUUk5OSEVAPDk2LywqJyQSEQ/Vx8a4tbGuqaijnJubmZCOiIaFhYB5d3ZvaWdjVUQ8NjQqqSQgkwAABqJJREFUaN7d2nVYFFEQAPCxz7oDDBQVUEGxUBEVMcFE7O7u7u7u7u7u7u7u7u7WAf5yZ997xy7rHqff4bf6+4c3+x2wcy9mlgPUyt6vXHX4ZbAl77DKnv39wOhKI3EtC/rKhiNpAsYWioy7GfRY0iHTFgytIXK5Qc9n5BKCod1DbjvoGYecGxjaFuQ+gJ4pyG0AQyuL3ALQ4xe5Bo1tOMoagJ42HshkBKMLLojo0Rj0nHeWskiHGPgUHCvn482bn7QEh+pr62xqS5lkCz2duxw42COUfAKHSoo4RjeTgjG2suJTKinAoYoi1rOVSWIeGDOV0IM737eJJpUc4/ccDlRUE2OmktOdH1r6C+y8i+PrYosykisOTeUCorI/LBYW1lDTdxVAWdhAcJzVKGnu0FR6IRMWFFsSVCkioktstaCuyEQsAMdJjpI8Dk2lAtqv1P+TSklwnCEoaenQVGogVzSupGjm8PDKt+OqFF2KXBu79vPkA8faQRT+J0pOPJYWFLKhZIaI0lBURhrMnrzv0GzQ8Jq8b/8MRWEWP0p1RbSQjfi2j4jQdGDlwu3ouzquqFKlynjwTUWvDL/7AxSKZ0D5ak1fFu8xmUzyUWLykUYtRCotYVcqJIXrg9Kc4SYk6e6UBsacrYDJ5OnSngUZPE2mTPQtTZAMjWxc4kFU0+RHx15gSyJ6yZsQyppk+grCzPRoNRLIc1Q6yRdYphx1UEjfGqzGYqTe7UGWRI4yAKlNw0Bv+ThuuHGcn+3GZUEhxMFgO5UEiJjaHYXFC4E5TpE6l9eqS81FKosUF53zAVcblTwDQDZVjiZIo6byiF6u7cESglZhxMZ2pEIC+dP/W+X7ly3HjByD5FEuAHihSSWNCFwr8nmtCsxQXggqLlJfD+YZ5EfSFOxOJRatO3tSWXPOu9wR+W5ugkxeXdMi30kX2qJeXiE1Kdgf4uXl1SEylfSl05aff8BDHpdgq1MeFyzhXz5tTrbjgoHpJi849iUbOD6VlUA+0rCiRd5mNHwJzA4KzirebX7SiVT6sGChfM+ZgfSgoU+AOPWINwvaO1OQRT49IAZS+c4C+i3h88VK9wAhnB+VokSesdYV1R15V6RwjjTKq94InZWFOhcKrWMglUI8oPcq7CKNqtItbtuaXTY6kxQN0FR7MSu7QdhE4UTqyGmwHmRiiycXUV1kdkEMpNKVB2solZRUbQpiVE66jctpEJpROFoavKPBQRAuh/EfIN4xshZiIpVkPLglUrmm7YuS6abSEoRTFD6UBttpcASEK65SmEpESTzZWREQk6nUss7KEjpgUWmpbip+2llpzFaa4E9hdRFlFvU0JlPpKVIBH/pV+ZNw7S8lSTJPd9vvAKGOaF5LqVfQbgqHaGpn3b8xKzCIKrcZtNajpIVq2wdaeJTPlZ9gvADOirwNCW/EJtC4QB1Wd2N8VkSJWw1Ma49nIDxQ3UJ81lOxkjHPJ7Ku9KGhOzttLXKRCVuo6CKKQ2+5FwgArb4OnhWQy92AvNTBNo1AdJun/Pv0yrxgDmnHUiHuxVv5h9TPxPorccdkrJd/qxKd+N3L5C3fTTytZwGtYogNHZpKAD+Ch/QzoWhcSBN2vYoHppAXmEBVVPm6serr4qYHyRPkbW0sg0GjO7XAtlPJPX7nUTsXGJnljApLvgCTX9MZe3bGSIUC1IVQWG4BMkmxRYL5WM37BtWg/uZfpfKKD/ujJGsoRHGNLifgwXUKLvLgUg+0qpkfhPrK55WRNEhbB4VViqa9RGa02qR8cKirqpSzNL08WQdRdbY2+etQFssCah1rpk6degMPtqaWtAPhcL9C8kbu10z1bLksjHasz1x6l6s5Oa2Q7rp6ONWhVZNAKXRbNVea5ljDeCtqHrDMyalaH+DyV5e+u8so7aeqpFgclRFxFiN2HTFCGhb7sz9ZdJjtm/tcB4hi7gnf6XNBxf+Ur19a0Jg/3Xd6K/gNQWi/IDC0OGi/jWBoOZErMjCxUsaM6RBdMrq5uSUugtxeMDKzuM8aEJWL9ZGpBj8sy4ORpRJzYtGtKyLfwnnByCiT6s3qJZwSTYk8Wm/cITA0yiQrEL1U/hVZbWUCPv9IKh1aXbVoM9Fue+Mb5o7uleiZ2Qx6RjkjuuQEo1uOTHdzdJ+MGT2XBsh4gK4cyDgbu5ZAZWTSXY3+06QpYGgVkPumf0z/G80KFEauHOhx+0f+capU9P8V5YdMJzC4geyp0NaWHoXEtQ0YXcnulboktNieOqdKnQYbu4ME+AnhAIW2MwgdrwAAAABJRU5ErkJggg==', + 'logo': '/media/image-attachments/Netbox_Icon_Example.png', }, ``` -## Example 3 +## Example 3 (Device) Text left QR code right with distance to the edge. ![Cable QR Code](/docs/img/Configuration_Label_Example_03.png) ```Python 'device_2': { - 'title': 'Example', + 'title': 'Example 3 (Template for Device)', 'font_size': '4mm', 'label_qr_width': '15mm', 'label_qr_height': '15mm', @@ -47,14 +47,14 @@ Text left QR code right with distance to the edge. }, ``` -## Example 4 +## Example 4 (Device) Center text only ![Cable QR Code](/docs/img/Configuration_Label_Example_04.png) ```Python 'device_2': { - 'title': 'Example', + 'title': 'Example 4 (Template for Device)', 'font_size': '4mm', 'text_align_horizontal': 'center', 'text_align_vertical': 'middle', @@ -71,14 +71,14 @@ Center text only }, ``` -## Example 5 +## Example 5 (Device) Text links only ![Cable QR Code](/docs/img/Configuration_Label_Example_05.png) ```Python 'device_2': { - 'title': 'Example', + 'title': 'Example 5 (Template for Device)', 'font_size': '4mm', 'text_align_horizontal': 'left', 'text_align_vertical': 'middle', @@ -95,14 +95,14 @@ Text links only }, ``` -## Example 6 +## Example 6 (Device) QR code only ![Cable QR Code](/docs/img/Configuration_Label_Example_06.png) ```Python 'device_2': { - 'title': 'Example', + 'title': 'Example 6 (Template for Device)', 'font_size': '4mm', 'label_qr_width': '20mm', 'label_qr_height': '20mm', @@ -117,14 +117,14 @@ QR code only }, ``` -## Example 7 +## Example 7 (Device) Upright, large font with line break and serial number ![Cable QR Code](/docs/img/Configuration_Label_Example_07.png) ```Python - 'device_2': { - 'title': 'Example', + 'device_7': { + 'title': 'Example 7 (Template for Device)', 'font_size': '4mm', 'label_qr_width': '20mm', 'label_qr_height': '20mm', @@ -141,14 +141,14 @@ Upright, large font with line break and serial number }, ``` -## Example 8 +## Example 8 (Device) Upright, large font with line break and serial number ![Cable QR Code](/docs/img/Configuration_Label_Example_08.png) ```Python - 'device_2': { - 'title': 'Example', + 'device_8': { + 'title': 'Example 8 (Template for Device)', 'font_size': '4mm', 'label_qr_width': '20mm', 'label_qr_height': '20mm', @@ -164,28 +164,27 @@ Upright, large font with line break and serial number }, ``` -## Example 9 +## Example 9 (Device) Small label ![Cable QR Code](/docs/img/Configuration_Label_Example_09.png) ```Python - 'device_2': { - 'title': 'Example', - 'font_size': '4mm', - 'label_qr_width': '20mm', - 'label_qr_height': '20mm', - 'label_qr_text_distance': '2mm', - 'label_width': '32mm', - 'label_height': '56mm', - 'label_edge_top': '2mm', - 'label_edge_left': '0mm', + 'device_9': { + 'title': 'Example 9 (Template for Device)', + 'font_size': '1mm', + 'label_qr_width': '9mm', + 'label_qr_height': '9mm', + 'label_qr_text_distance': '1mm', + 'label_width': '25mm', + 'label_height': '10mm', + 'label_edge_top': '0.2mm', + 'label_edge_left': '0.2mm', 'label_edge_right': '0mm', - 'text_location': 'down', }, ``` -## Example 10 - Completely self-designed +## Example 10 (Device) - Completely self-designed This way, many label design options should be possible. The design of the label content is completely specified by the user via this method. @@ -197,14 +196,15 @@ Save this Netbox icon as an example in the following Netbox folder: ![Cable QR Code](/docs/img/Netbox_Icon_Example.png) ```Python - 'device_2': { + 'device_10': { + 'title': 'Example 10 (Template for Device)', 'with_qr': False, 'text_align_horizontal': 'center', 'text_align_vertical': 'middle', 'title': 'Example', - 'text_template': '

' + 'text_template': '

' '{{ obj.name }}
' - '

' + '

' 'Device: {{ obj.id|stringformat:"07d" }}' '

😝 My label design 😝

', @@ -213,9 +213,10 @@ Save this Netbox icon as an example in the following Netbox folder: 'label_edge_top': '0mm', 'label_edge_left': '0mm', 'label_edge_right': '0mm', + }, ``` -## Example 11 - Cable Label vertical writing. +## Example 1 (Cable) - Cable Label vertical writing. This example shows how to write vertically, e.g. for cable labeling on a wide but not so high single label. @@ -223,14 +224,14 @@ This example shows how to write vertically, e.g. for cable labeling on a wide bu ```Python 'cable': { - 'title': 'Für Kabel', + 'title': 'Example 1 (Template for Cable)', 'with_qr': False, 'label_edge_left': '0.00mm', 'label_edge_right': '0.00mm', 'label_edge_top': '0.00mm', 'text_align_vertical': 'middle', 'text_align_horizontal': 'center', - 'text_template': '' + 'text_template': '' '{{ obj.label }}
' '{{ obj.label }}
' '{{ obj.label }}
' @@ -248,15 +249,15 @@ This example shows how to write vertically, e.g. for cable labeling on a wide bu }, ``` -## Example 12 - Cable Label vertical writing. +## Example 2 (Cable) - Cable Label vertical writing. This example shows the creation of a Cabel label with barcode 128. Please note that an online function is used here (internet may be necessary). Barcode 128 is not created on the Netbox server. ![Cable QR Code](/docs/img/Configuration_Label_Example_12.png) ```Python - 'cable_2': { - 'title': 'Für Kabel', + 'cable_2': { + 'title': 'Example 2 (Template for Cable)', 'with_qr': False, 'label_edge_left': '0.00mm', 'label_edge_right': '0.00mm', @@ -295,4 +296,5 @@ This example shows the creation of a Cabel label with barcode 128. Please note t ' height: 15' ' });' '' + } ``` \ No newline at end of file diff --git a/docs/img/Configuration_Label_Example_10.png b/docs/img/Configuration_Label_Example_10.png index 787cf69eb3ab18214a80da6eb1b4d7527194f44c..ee48583f79b89e6c9f41df6575246ddfe4c0f887 100644 GIT binary patch literal 16380 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 a)7S@()ynbBvJy7?G}Lq-l;5*?{yzXJIrNwS literal 13888 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 diff --git a/docs/img/Netbox_Icon_Example.png b/docs/img/Netbox_Icon_Example.png index 4e6b6b88981dca210515d9d5e3b59d45b6c1449d..aaca2d2df236114ad82116d3d65c456e321e8521 100644 GIT binary patch delta 2653 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 LNkvXXu0mjfd%zW) delta 2653 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 LNkvXXu0mjfI(rjx diff --git a/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html b/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html index 1c14039..eaf7204 100644 --- a/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html +++ b/netbox_qrcode/templates/netbox_qrcode/qrcode3_sub_qrcode.html @@ -9,5 +9,5 @@ {% endif %} "> - +
\ No newline at end of file From 95022c1f33c93c47500a615730b9798a38ffb31d Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 5 May 2025 14:55:24 +0200 Subject: [PATCH 60/65] Documentation: Fixed broken link and added more. --- docs/README_Subpages/README_Configuration.md | 2 +- .../README_Configuration_ExampleLabelConf.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/README_Subpages/README_Configuration.md b/docs/README_Subpages/README_Configuration.md index 9cd4a71..f3aa9d2 100644 --- a/docs/README_Subpages/README_Configuration.md +++ b/docs/README_Subpages/README_Configuration.md @@ -654,7 +654,7 @@ PLUGINS_CONFIG = { ``` ## Example label configurations -[Go to Example label configurations >>](docs/README_Subpages/README_Configuration_ExampleLabelConf.md) +[Go to Example label configurations >>](README_Configuration_ExampleLabelConf.md) ![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) diff --git a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md index 55c35fb..66d7866 100644 --- a/docs/README_Subpages/README_Configuration_ExampleLabelConf.md +++ b/docs/README_Subpages/README_Configuration_ExampleLabelConf.md @@ -1,3 +1,5 @@ +[<< Back to General Configuration](README_Configuration.md) + # Example label configurations ## Example 1 (Device) - Default @@ -297,4 +299,6 @@ This example shows the creation of a Cabel label with barcode 128. Please note t ' });' '' } -``` \ No newline at end of file +``` + +[<< Back to General Configuration](README_Configuration.md) \ No newline at end of file From a2476407acda5a64c8a178766ee54cef9d9594a6 Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Mon, 5 May 2025 15:23:22 +0200 Subject: [PATCH 61/65] Example 10 unfortunately had 2 title parameters, so the heading was not correct. --- develop/configuration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/develop/configuration.py b/develop/configuration.py index f953bb6..fdc1217 100644 --- a/develop/configuration.py +++ b/develop/configuration.py @@ -292,7 +292,6 @@ 'with_qr': False, 'text_align_horizontal': 'center', 'text_align_vertical': 'middle', - 'title': 'Example', 'text_template': '

' '{{ obj.name }}
' '

' From 31b43ceb93283b363fe652da0b64ed0bc329b49c Mon Sep 17 00:00:00 2001 From: LHBL2003 Date: Tue, 6 May 2025 09:01:47 +0200 Subject: [PATCH 62/65] Improvement of the logo / image description. --- docs/README_Subpages/README_Configuration.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/README_Subpages/README_Configuration.md b/docs/README_Subpages/README_Configuration.md index f3aa9d2..a631db0 100644 --- a/docs/README_Subpages/README_Configuration.md +++ b/docs/README_Subpages/README_Configuration.md @@ -338,31 +338,32 @@ The parameters that can be used to design the label are listed below. ### Image File size: - ℹ️ To keep the traffic and performance high, the image should be as small as necessary. An image with 1920x1200px and 30MB is nonsensical if it is to be 1.00x3.00cm in size at the end. The larger the image, the longer the Base64 text. If possible, the logo is completely black and not gray or colored, which makes it even smaller. (Sufficient for thermal transfer printers). + To keep the traffic and performance high, the image should be as small as necessary. An image with 1920x1200px and 30MB is nonsensical if it is to be 1.00x3.00cm in size at the end. The larger the image, the longer the Base64 text. If possible, the logo is completely black and not gray or colored, which makes it even smaller. (Sufficient for thermal transfer printers). Here are a few helpful online tools: Resize image online (in mm): https://image.pi7.org/resize-image-in-mm Image compression (e.g. 100kb to 2kb): https://tinypng.com/ - ### Version 1: Link to the image (Recommended) - - The media folder to provide the plugin with images is located here: - /opt/netbox/netbox/media/ + ### Version 1: Link to the image (Simpler) + This variant shows how you can link a logo. + ```Python 'logo': '/media/image-attachments/Netbox_Icon_Example.png', ``` + Below is how you can embed the logo in a text. It is an HTML block that contains the image {{ logo }} as well as the name {{ obj.name }} and the ID of the object {{ obj.id }} in 3 lines. ```Python 'text_template': '

{{ obj.name }}
Device: {{ obj.id }}
', ``` - ℹ️ The logo link can be inserted directly in the text_template, but this is not recommended as the logo parameter may be included in the standard layout in future versions. - ℹ️ You can specify a link to the image file in Netbox or an external http:// or https:// link as long as the client can reach this image. + ℹ️ The image links used in the examples are merely placeholders and are not predefined by the plugin. You can therefore freely adapt them to suit your environment. Links with http:// or https:// should also work as long as the client can access them. The NetBox media link used in the example is only one possible variant that has been successfully tested in a classic NetBox installation. As a guide, the media path used in the example is usually located in the following directory: /opt/netbox/netbox/media/ + + ℹ️ The logo link can be inserted directly in the text_template, but this is not recommended as the logo parameter may be included in the standard layout in future versions. - ### Version 2: Embedded Base64 Image (not recommended due to complexity) + ### Version 2: Embedded Base64 Image (Complex) Convert image to Base64 string.: https://www.base64-image.de/ From 8bc1cfa1d4e91c635c7d626d849eab468ba08f25 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 25 May 2025 19:23:22 +0100 Subject: [PATCH 63/65] Tidy documentation --- docs/README_Subpages/README_Configuration.md | 31 -------------------- 1 file changed, 31 deletions(-) diff --git a/docs/README_Subpages/README_Configuration.md b/docs/README_Subpages/README_Configuration.md index a631db0..e9b9c41 100644 --- a/docs/README_Subpages/README_Configuration.md +++ b/docs/README_Subpages/README_Configuration.md @@ -659,35 +659,4 @@ PLUGINS_CONFIG = { ![Cable QR Code](/docs/img/Configuration_Label_Example_10.png) - -# Other plugin support - -The following plugins are currently supported. - -### netbox-inventory - -A Netbox plugin for hardware inventory. (https://github.com/ArnesSI/netbox-inventory) - -* `netbox_inventory.asset`: - - ```Python - PLUGINS_CONFIG = { - 'netbox_qrcode': { - 'netbox_inventory.asset': { - 'title': 'My Litle Label 1', - }, - } - } - ``` - - ```Python - PLUGINS_CONFIG = { - 'netbox_qrcode': { - 'netbox_inventory.asset_2': { - 'title': 'My Litle Label 2', - }, - } - } - ``` - [<< Back to README start](/README.md) \ No newline at end of file From 1c5890a19be74158ae2fffe9d8cc0f35a8516231 Mon Sep 17 00:00:00 2001 From: Chris Russell Date: Sun, 25 May 2025 19:37:49 +0100 Subject: [PATCH 64/65] Release - 0.18 - 4.3.x --- Makefile | 2 +- netbox_qrcode/__init__.py | 4 ++-- netbox_qrcode/version.py | 2 +- setup.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 79bc441..bf1c009 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VER?=3.12 -NETBOX_VER?=v4.3.0 +NETBOX_VER?=v4.3.1 NAME=netbox-qrcode diff --git a/netbox_qrcode/__init__.py b/netbox_qrcode/__init__.py index f5c6bf5..baf6d37 100644 --- a/netbox_qrcode/__init__.py +++ b/netbox_qrcode/__init__.py @@ -10,8 +10,8 @@ class QRCodeConfig(PluginConfig): author = 'Nikolay Yuzefovich' author_email = 'mgk.kolek@gmail.com' required_settings = [] -# min_version = '4.2.0' -# max_version = '4.2.99' + min_version = '4.3.0' + max_version = '4.3.99' default_settings = { ################################## diff --git a/netbox_qrcode/version.py b/netbox_qrcode/version.py index 1f658a4..f18e5d0 100644 --- a/netbox_qrcode/version.py +++ b/netbox_qrcode/version.py @@ -1 +1 @@ -__version__ = "0.0.17" +__version__ = "0.0.18" diff --git a/setup.py b/setup.py index 02c8b78..a1c6140 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ def get_version(rel_path): author_email='mgk.kolek@gmail.com', packages=find_packages(), include_package_data=True, - min_version='4.1.0', - max_version='4.1.99', + min_version='4.3.0', + max_version='4.3.99', package_data={ '': ['*.ttf'], '': ['*.html'], From e17111b14b98e7d5c801ac3d6434646d9f142ae4 Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 4 Jun 2025 11:44:28 +0100 Subject: [PATCH 65/65] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8cce28d..d9f2a53 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,10 @@ This plugin depends on [qrcode](https://github.com/lincolnloop/python-qrcode) an | Plugin Version | NetBox Version | Tested on | | ------------- |:-------------| :-----------:| | 0.0.11 | 3.7.x | 3.7.x | -| 0.0.14 | 4.0.x | v4.0.11 | -| 0.0.15 | 4.1.x | v4.1.6 | -| 0.0.17 | 4.2.x | v4.2.4 | +| 0.0.14 | 4.0.x | 4.0.11 | +| 0.0.15 | 4.1.x | 4.1.6 | +| 0.0.17 | 4.2.x | 4.2.4 | +| 0.0.18 | 4.3.x | 4.3.1 | ## Installation