Skip to content
Draft
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
eff83e7
Install packages for blocknote
SchrodingersGat May 18, 2026
01bc34e
Add new notes editor using blocknote
SchrodingersGat May 18, 2026
71d51ff
Add new generic "notes" model
SchrodingersGat May 18, 2026
176733d
Add basic tab group for switching notes
SchrodingersGat May 18, 2026
1078475
API / serializer updates
SchrodingersGat May 18, 2026
19c497f
Merge commit '99358eb4e7e4b051fd35c0b72c5d75e11ea5f2ec' into block-notes
SchrodingersGat May 19, 2026
8815861
Add notes fetch
SchrodingersGat May 20, 2026
6df13fe
Add note mixin support for existing models
SchrodingersGat May 20, 2026
51d8468
Fix serializer
SchrodingersGat May 20, 2026
a0e528a
Create new notes
SchrodingersGat May 20, 2026
bc197f9
Omit migration tests from code coverage
SchrodingersGat May 20, 2026
ac8e8d9
Add "no notes" item
SchrodingersGat May 20, 2026
ab0da25
Add "primary" field to Note model
SchrodingersGat May 21, 2026
c18fd25
Update serializer
SchrodingersGat May 21, 2026
d77be24
Add unit test
SchrodingersGat May 21, 2026
c7d11dc
Mark content field as 'safe'
SchrodingersGat May 21, 2026
9e8764d
Fix conflict (for now)
SchrodingersGat May 21, 2026
fd3d64d
Merge branch 'master' into block-notes
SchrodingersGat May 21, 2026
df340e3
Load notes into editor
SchrodingersGat May 21, 2026
689bd1f
Refactor save UX
SchrodingersGat May 21, 2026
36e81c4
Sanitize before uploading
SchrodingersGat May 21, 2026
136c925
remove old editor
SchrodingersGat May 21, 2026
0c1aa65
Remove old deps
SchrodingersGat May 21, 2026
7315c34
Merge branch 'block-notes' of github.com:SchrodingersGat/InvenTree in…
SchrodingersGat May 21, 2026
5cd7bfa
Merge commit '27ca0836e7c7e46ebb41ea98d507972ae7a6b018' into block-notes
SchrodingersGat May 23, 2026
38e3c86
Merge commit '749c4715ee022bdb39b500f5c3776c1ee18411e3' into block-notes
SchrodingersGat May 23, 2026
4338920
Merge commit 'dd1edce2e59172c86e85fdd09d6ed01d3fcfefe4' into block-notes
SchrodingersGat May 24, 2026
754a4c2
Fix migration conflict
SchrodingersGat May 24, 2026
8377f53
Run nh3 cleaner over backend notes
SchrodingersGat May 24, 2026
9e91663
Update note accessors
SchrodingersGat May 24, 2026
5813c4b
edit / delete existing notes from the UI
SchrodingersGat May 24, 2026
d798aac
Layout tweaks
SchrodingersGat May 24, 2026
407ba3f
Display note info
SchrodingersGat May 24, 2026
d99487b
Support user locale
SchrodingersGat May 25, 2026
488f558
Add unit testing for HTML content
SchrodingersGat May 25, 2026
29202f6
Observe color mode
SchrodingersGat May 25, 2026
8b99c33
Merge branch 'master' into block-notes
SchrodingersGat May 25, 2026
4fff7ed
Add link between Note and NotesImage
SchrodingersGat May 25, 2026
0df677d
Ensure image file is deleted when NotesImage is deleted
SchrodingersGat May 25, 2026
8f36287
Add support for image upload in editor
SchrodingersGat May 25, 2026
d305ec2
Skeleton for data migration
SchrodingersGat May 25, 2026
1260de6
Updates
SchrodingersGat May 25, 2026
24e8fad
Update data migration
SchrodingersGat May 25, 2026
c2e1893
Remove validator
SchrodingersGat May 25, 2026
d93d5f9
Updated API endpoints for NotesImage model
SchrodingersGat May 25, 2026
725ef04
Update server side sanitizing
SchrodingersGat May 25, 2026
c58b7b8
Specify max field length
SchrodingersGat May 25, 2026
618e368
Remove old fields from NotesImage model
SchrodingersGat May 25, 2026
cdd1b1d
Refactor clean_string
SchrodingersGat May 25, 2026
be132bf
Remove obsolete task
SchrodingersGat May 25, 2026
256cae2
Remove legacy "notes" field from older models
SchrodingersGat May 25, 2026
9f77ae2
Adjust search params when switching notes
SchrodingersGat May 25, 2026
50b67ff
Remove NotesFieldMixin
SchrodingersGat May 25, 2026
36bc444
Change editor
SchrodingersGat May 26, 2026
c37c1e6
Resizable image support
SchrodingersGat May 26, 2026
5c4f082
Support tables
SchrodingersGat May 26, 2026
799e57c
Add table style
SchrodingersGat May 26, 2026
277e22a
Adjust header actions
SchrodingersGat May 26, 2026
8396a9d
Add data migration for SalesOrderShipment notes
SchrodingersGat May 26, 2026
73a3218
Adjust back-end sanitizing
SchrodingersGat May 26, 2026
f754444
Adjust
SchrodingersGat May 26, 2026
2b57413
Use subtle editor variant
SchrodingersGat May 26, 2026
664eed1
Move undo/redo
SchrodingersGat May 26, 2026
36006c6
Enhance editing logic
SchrodingersGat May 26, 2026
d1b3746
Merge branch 'master' into block-notes
SchrodingersGat May 26, 2026
6a5e606
Add report tags for notes
SchrodingersGat May 26, 2026
dc9e23c
Add unit test for note image cleanup
SchrodingersGat May 26, 2026
9ecaf2c
Render note to HTML
SchrodingersGat May 26, 2026
c0deb92
Merge commit 'bc03f349085582e99606b94ff9c4cded905aeb1b' into block-notes
SchrodingersGat May 26, 2026
42931fd
Fix migration order
SchrodingersGat May 26, 2026
48cd60b
Adjust migration text
SchrodingersGat May 26, 2026
09ab375
Merge commit '00ad041e1b65754460c9fb9b6242f6e4e9295d2c' into block-notes
SchrodingersGat May 27, 2026
8d4c201
Fix "dirty" trigger on notes
SchrodingersGat May 27, 2026
55a39b2
Prevent navigate from dirty notes
SchrodingersGat May 27, 2026
bf675f1
Add documentation
SchrodingersGat May 27, 2026
b4bd688
Prevent image clicking if not in editing mode
SchrodingersGat May 27, 2026
2d8fd18
Merge branch 'master' into block-notes
SchrodingersGat May 27, 2026
40c2775
Merge commit '956468eb847754e701d207bae2a51a6a00ca7d56' into block-notes
SchrodingersGat Jun 1, 2026
4f41ebb
Merge branch 'master' into block-notes
SchrodingersGat Jun 1, 2026
8bf2152
Merge commit 'd38af61ba2fb3c87143da57ddd92bc4845f85a7c' into block-notes
SchrodingersGat Jun 1, 2026
d7af743
Merge branch 'block-notes' of github.com:SchrodingersGat/InvenTree in…
SchrodingersGat Jun 1, 2026
38d8965
Merge commit '47c6906a74f41eed5b56f4dc43ea0d4e20415237' into block-notes
SchrodingersGat Jun 2, 2026
a7ae8c0
Update API
SchrodingersGat Jun 2, 2026
8bd9aa7
Update migration files
SchrodingersGat Jun 2, 2026
11e55fc
Merge branch 'master' into block-notes
SchrodingersGat Jun 3, 2026
05514ec
Fix migrations
SchrodingersGat Jun 3, 2026
b2cece1
Merge branch 'block-notes' of github.com:SchrodingersGat/InvenTree in…
SchrodingersGat Jun 3, 2026
2456101
remove notes from test fixtures
matmair Jun 3, 2026
5d49051
remove notes field that does not exsist anymore
matmair Jun 3, 2026
4bc9913
add missing ruleset
matmair Jun 3, 2026
1366da4
fix assertation
matmair Jun 3, 2026
856389b
fix assertation
matmair Jun 3, 2026
bb0b82d
Update docs/docs/concepts/notes.md
SchrodingersGat Jun 4, 2026
df9786f
Remove blocknote deps
SchrodingersGat Jun 4, 2026
09c1aaa
Merge branch 'master' into block-notes
SchrodingersGat Jun 4, 2026
550a7ab
Move old helper functions
SchrodingersGat Jun 4, 2026
f6cde30
Revert change
SchrodingersGat Jun 4, 2026
a46bb39
Fix note image URL
SchrodingersGat Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions docs/docs/concepts/notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: Notes
---

## Notes

*Notes* allow free-form rich-text content to be written and stored against a specific object within InvenTree. Notes can be used to record observations, instructions, historical context, or any other information associated with a model instance.

!!! note "Business Logic"
Notes are not used for any core business logic within InvenTree. They are intended to provide supplementary documentation and context for objects, which can be useful for reference, communication, or reporting purposes.
Comment thread
SchrodingersGat marked this conversation as resolved.
Outdated

Notes can be associated with various InvenTree models, and each model can have multiple notes associated with it. The user interface provides a dedicated "Notes" tab on the detail page of any model that supports notes, allowing users to easily view and manage notes for that object.

### Notes Tab

Any model which supports notes will have a "Notes" tab on its detail page. This tab displays the content of the currently selected note, along with a sidebar listing all notes for that object by title:

{{ image("concepts/notes-tab.png", "Notes Tab Example") }}

## Note Fields

Each note has the following attributes:

| Field | Description |
| --- | --- |
| Title | A short title for the note (*required*) |
| Description | An optional brief description of the note's purpose |
| Content | The rich-text body of the note |
| Primary | Marks this note as the default note for the object |

## Primary Note

When a model has multiple notes, one may be designated as the *primary* note. The primary note is indicated by a {{ icon("star") }} icon in the note sidebar.

- When the first note is created for a model instance, it is automatically set as the primary note.
- Only one note per model instance can be marked as primary at any time.
- The primary note is opened by default when navigating to the Notes tab.

## Rich Text Editing

Note content is edited using a rich-text (WYSIWYG) editor. The following formatting options are available:

- **Text formatting**: Bold, italic, underline, strikethrough, inline code, code blocks
- **Headings**: H1 through H4
- **Structure**: Blockquotes, horizontal rules
- **Lists**: Bullet lists and ordered lists
- **Links**: Insert and remove hyperlinks
- **Tables**: Insert tables; add/remove rows and columns; toggle header rows
- **Images**: Embed images uploaded directly into the note

### Inserting Images

Images can be embedded in note content in the following ways:

- Click the {{ icon("photo") }} button in the editor toolbar to select a file from your device
- Paste an image directly from the clipboard
- Drag and drop an image file into the editor

Uploaded images are stored on the server and linked to the note. If a note is edited or deleted, any images that are no longer referenced by any note are automatically removed.

## Adding a Note

To add a note to an object:

1. Navigate to the object's detail page
2. Click on the **Notes** tab
3. Click the **Add Note** button
4. Fill in the `Title` (required) and optional `Description` fields
5. Click **Submit**

The new note will appear in the sidebar ready for editing.

## Editing Note Content

Note content is shown in read-only mode by default. To make changes:

1. Click the {{ icon("pencil") }} icon in the note header to enter edit mode
2. Use the toolbar to format content, insert images, or add tables
3. Click the {{ icon("device-floppy") }} icon, or press **Ctrl+S** / **Cmd+S**, to save changes
4. Click the {{ icon("check") }} icon to exit edit mode once all changes are saved

!!! warning "Unsaved Changes"
If you navigate away from the Notes panel or leave the page while in edit mode with unsaved changes, InvenTree will prompt you to confirm before proceeding.

### Resetting Changes

While in edit mode, clicking the {{ icon("reload") }} icon discards any unsaved changes and reloads the last saved version of the note.

## Editing Note Properties

To change a note's title or description, open the actions menu in the note header and select **Edit Note**.

## Deleting a Note

To delete a note, open the actions menu in the note header and select **Delete Note**.

!!! danger "Permanent Action"
Deleting a note is permanent and cannot be undone. Any images embedded in the note that are not referenced elsewhere will also be removed.
2 changes: 1 addition & 1 deletion docs/docs/concepts/terminology.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Evolves around manufacturing of parts out of other parts. It keeps track of stoc

### Part Library Management *(PLM)*
Keeps track of BOMs, part variants, possible substitutions, versions, IPNs and further part parameters.
PLM can also mean product lifecycle management those systems manage all stages from design through manufacturing up to customer support and recycling.
PLM can also mean product lifecycle management - those systems manage all stages from design through manufacturing up to customer support and recycling.
Comment thread
SchrodingersGat marked this conversation as resolved.
Outdated

A similar system is [Partkeepr](https://partkeepr.org/) (seems mostly inactive - there is a 3rd party importer).

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/plugins/develop.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Consider the use-case for your plugin and define the exact function of the plugi
- Do you need to run in the background ([ScheduleMixin](./mixins/schedule.md)) or when things in InvenTree change ([EventMixin](./mixins/event.md))?
- Does the plugin need configuration that should be user changeable ([SettingsMixin](./mixins/settings.md)) or static (just use a yaml in the config dir)?
- You want to receive webhooks? Do not code your own untested function, use the WebhookEndpoint model as a base and override the perform_action method.
- Do you need the full power of Django with custom models and all the complexity that comes with that welcome to the danger zone and [AppMixin](./mixins/app.md). The plugin will be treated as a app by django and can maybe rack the whole instance.
- Do you need the full power of Django with custom models and all the complexity that comes with that - welcome to the danger zone and [AppMixin](./mixins/app.md). The plugin will be treated as a app by django and can maybe rack the whole instance.

### Define Metadata

Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ nav:
- Project Codes: concepts/project_codes.md
- Attachments: concepts/attachments.md
- Parameters: concepts/parameters.md
- Notes: concepts/notes.md
- Barcodes:
- Barcode Support: barcodes/index.md
- Internal Barcodes: barcodes/internal.md
Expand Down
7 changes: 6 additions & 1 deletion src/backend/InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
"""InvenTree API version information."""

# InvenTree API version
INVENTREE_API_VERSION = 499
INVENTREE_API_VERSION = 500
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""

INVENTREE_API_TEXT = """

v500 -> 2026-06-02 : https://github.com/inventree/InvenTree/pull/11971
- Removes direct "notes" field from any models which previously supported markdown notes
- Adds a generic "Note" model which can be attached to any model type via a generic foreign key relationship
- Allow multiple notes to be attached to a single object, and for notes to be created / edited / deleted via the API

v499 -> 2026-06-01 : https://github.com/inventree/InvenTree/pull/12057
- Fixes search field issues on the BarcodeScanHistory API endpoint

Expand Down
5 changes: 3 additions & 2 deletions src/backend/InvenTree/InvenTree/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ def ready(self):
def remove_obsolete_tasks(self):
"""Delete any obsolete scheduled tasks in the database."""
obsolete = [
'common.tasks.delete_old_notes_images',
'data_exporter.tasks.cleanup_old_export_outputs',
'InvenTree.tasks.delete_expired_sessions',
'stock.tasks.delete_old_stock_items',
'label.tasks.cleanup_old_label_outputs',
'report.tasks.cleanup_old_report_outputs',
'data_exporter.tasks.cleanup_old_export_outputs',
'stock.tasks.delete_old_stock_items',
]

try:
Expand Down
37 changes: 25 additions & 12 deletions src/backend/InvenTree/InvenTree/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from common.currency import currency_code_default
from InvenTree.sanitizer import (
DEAFAULT_ATTRS,
DEFAULT_ATTRS,
DEFAULT_CSS,
DEFAULT_PROTOCOLS,
DEFAULT_TAGS,
Expand Down Expand Up @@ -939,33 +939,46 @@ def remove_non_printable_characters(value: str, remove_newline=True) -> str:
return cleaned


def clean_markdown(value: str) -> str:
"""Clean a markdown string.
def get_markdownify_settings() -> dict:
"""Return the settings for markdownify, or an empty dict if not defined."""
try:
return settings.MARKDOWNIFY['default']
except (AttributeError, KeyError):
return {}


def markdown_to_html(value: str) -> str:
"""Convert a markdown string to HTML.

This function will remove javascript and other potentially harmful content from the markdown string.
"""
import markdown

try:
markdownify_settings = settings.MARKDOWNIFY['default']
except (AttributeError, KeyError):
markdownify_settings = {}

markdownify_settings = get_markdownify_settings()
extensions = markdownify_settings.get('MARKDOWN_EXTENSIONS', [])
extension_configs = markdownify_settings.get('MARKDOWN_EXTENSION_CONFIGS', {})

# Generate raw HTML from provided markdown (without sanitizing)
# Note: The 'html' output_format is required to generate self closing tags, e.g. <tag> instead of <tag />
html = markdown.markdown(
value or '',
extensions=extensions,
extension_configs=extension_configs,
output_format='html',
)

# nh3 sanitizer settings
return html


def clean_markdown(value: str) -> str:
Comment thread
SchrodingersGat marked this conversation as resolved.
Outdated
"""Clean a markdown string.

This function will remove javascript and other potentially harmful content from the markdown string.
"""
html = markdown_to_html(value)

markdownify_settings = get_markdownify_settings()

whitelist_tags = markdownify_settings.get('WHITELIST_TAGS', DEFAULT_TAGS)
whitelist_attrs = markdownify_settings.get('WHITELIST_ATTRS', DEAFAULT_ATTRS)
whitelist_attrs = markdownify_settings.get('WHITELIST_ATTRS', DEFAULT_ATTRS)
whitelist_styles = markdownify_settings.get('WHITELIST_STYLES', DEFAULT_CSS)
whitelist_protocols = markdownify_settings.get(
'WHITELIST_PROTOCOLS', DEFAULT_PROTOCOLS
Expand Down
40 changes: 3 additions & 37 deletions src/backend/InvenTree/InvenTree/mixins.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
"""Mixins for (API) views in the whole project."""

from django.core.exceptions import FieldDoesNotExist

from rest_framework import generics, mixins, status
from rest_framework.response import Response

import data_exporter.mixins
import importer.mixins
from InvenTree.fields import InvenTreeNotesField, OutputConfiguration
from InvenTree.helpers import (
clean_markdown,
remove_non_printable_characters,
strip_html_tags,
)
from InvenTree.fields import OutputConfiguration
from InvenTree.helpers import remove_non_printable_characters, strip_html_tags
from InvenTree.schema import schema_for_view_output_options
from InvenTree.serializers import FilterableSerializerMixin

Expand Down Expand Up @@ -54,38 +48,10 @@ def clean_string(self, field: str, data: str) -> str:
"""Clean / sanitize a single input string."""
cleaned = data

# By default, newline characters are removed
remove_newline = True
is_markdown = False

try:
if hasattr(self, 'serializer_class'):
model = self.serializer_class.Meta.model
field_base = model._meta.get_field(field)

# The following field types allow newline characters
allow_newline = [(InvenTreeNotesField, True)]

for field_type in allow_newline:
if issubclass(type(field_base), field_type[0]):
remove_newline = False
is_markdown = field_type[1]
break

except AttributeError:
pass
except FieldDoesNotExist:
pass

cleaned = remove_non_printable_characters(
cleaned, remove_newline=remove_newline
)
cleaned = remove_non_printable_characters(cleaned, remove_newline=True)

cleaned = strip_html_tags(cleaned, field_name=field)

if is_markdown:
cleaned = clean_markdown(cleaned)

return cleaned

def clean_data(self, data: dict) -> dict:
Expand Down
Loading
Loading