From 742c1367732f303f1d2735782f6ae4f122c2ae2c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:49:21 -0800 Subject: [PATCH 1/6] Fix: use explicit order field for workflow actions (#11781) --- .../workflow-edit-dialog.component.spec.ts | 4 +-- .../workflow-edit-dialog.component.ts | 5 ---- .../migrations/1076_workflowaction_order.py | 28 +++++++++++++++++++ src/documents/models.py | 2 ++ src/documents/serialisers.py | 13 ++++++++- src/documents/signals/handlers.py | 2 +- src/documents/workflows/utils.py | 20 ++++++++++--- 7 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 src/documents/migrations/1076_workflowaction_order.py diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts index aa52592b1..fafc9e876 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts @@ -252,7 +252,7 @@ describe('WorkflowEditDialogComponent', () => { expect(component.object.actions.length).toEqual(2) }) - it('should update order and remove ids from actions on drag n drop', () => { + it('should update order on drag n drop', () => { const action1 = workflow.actions[0] const action2 = workflow.actions[1] component.object = workflow @@ -261,8 +261,6 @@ describe('WorkflowEditDialogComponent', () => { WorkflowAction[] >) expect(component.object.actions).toEqual([action2, action1]) - expect(action1.id).toBeNull() - expect(action2.id).toBeNull() }) it('should not include auto matching in algorithms', () => { diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts index f6d9e60f5..74221e3f0 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts @@ -1283,11 +1283,6 @@ export class WorkflowEditDialogComponent const actionField = this.actionFields.at(event.previousIndex) this.actionFields.removeAt(event.previousIndex) this.actionFields.insert(event.currentIndex, actionField) - // removing id will effectively re-create the actions in this order - this.object.actions.forEach((a) => (a.id = null)) - this.actionFields.controls.forEach((c) => - c.get('id').setValue(null, { emitEvent: false }) - ) } save(): void { diff --git a/src/documents/migrations/1076_workflowaction_order.py b/src/documents/migrations/1076_workflowaction_order.py new file mode 100644 index 000000000..5c9f7ff52 --- /dev/null +++ b/src/documents/migrations/1076_workflowaction_order.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-01-14 16:53 + +from django.db import migrations +from django.db import models +from django.db.models import F + + +def populate_action_order(apps, schema_editor): + WorkflowAction = apps.get_model("documents", "WorkflowAction") + WorkflowAction.objects.all().update(order=F("id")) + + +class Migration(migrations.Migration): + dependencies = [ + ("documents", "1075_alter_paperlesstask_task_name"), + ] + + operations = [ + migrations.AddField( + model_name="workflowaction", + name="order", + field=models.PositiveIntegerField(default=0, verbose_name="order"), + ), + migrations.RunPython( + populate_action_order, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/src/documents/models.py b/src/documents/models.py index 12dab2b6d..c7c082a7b 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -1294,6 +1294,8 @@ class WorkflowAction(models.Model): default=WorkflowActionType.ASSIGNMENT, ) + order = models.PositiveIntegerField(_("order"), default=0) + assign_title = models.TextField( _("assign title"), null=True, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5c71de9a9..0648aa0b3 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -2562,7 +2562,8 @@ class WorkflowSerializer(serializers.ModelSerializer): set_triggers.append(trigger_instance) if actions is not None and actions is not serializers.empty: - for action in actions: + for index, action in enumerate(actions): + action["order"] = index assign_tags = action.pop("assign_tags", None) assign_view_users = action.pop("assign_view_users", None) assign_view_groups = action.pop("assign_view_groups", None) @@ -2689,6 +2690,16 @@ class WorkflowSerializer(serializers.ModelSerializer): return instance + def to_representation(self, instance): + data = super().to_representation(instance) + actions = instance.actions.order_by("order", "pk") + data["actions"] = WorkflowActionSerializer( + actions, + many=True, + context=self.context, + ).data + return data + class TrashSerializer(SerializerWithPerms): documents = serializers.ListField( diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 4ec00258a..d6edb523a 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -769,7 +769,7 @@ def run_workflows( if matching.document_matches_workflow(document, workflow, trigger_type): action: WorkflowAction - for action in workflow.actions.all(): + for action in workflow.actions.order_by("order", "pk"): message = f"Applying {action} from {workflow}" if not use_overrides: logger.info(message, extra={"group": logging_group}) diff --git a/src/documents/workflows/utils.py b/src/documents/workflows/utils.py index 553622252..0a644b0eb 100644 --- a/src/documents/workflows/utils.py +++ b/src/documents/workflows/utils.py @@ -20,9 +20,6 @@ def get_workflows_for_trigger( wrap it in a list; otherwise fetch enabled workflows for the trigger with the prefetches used by the runner. """ - if workflow_to_run is not None: - return [workflow_to_run] - annotated_actions = ( WorkflowAction.objects.select_related( "assign_correspondent", @@ -105,10 +102,25 @@ def get_workflows_for_trigger( ) ) + action_prefetch = Prefetch( + "actions", + queryset=annotated_actions.order_by("order", "pk"), + ) + + if workflow_to_run is not None: + return ( + Workflow.objects.filter(pk=workflow_to_run.pk) + .prefetch_related( + action_prefetch, + "triggers", + ) + .distinct() + ) + return ( Workflow.objects.filter(enabled=True, triggers__type=trigger_type) .prefetch_related( - Prefetch("actions", queryset=annotated_actions), + action_prefetch, "triggers", ) .order_by("order") From 2f1cd31e31cd890360fa9a54c8d88a26c5d19591 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Fri, 16 Jan 2026 07:42:54 -0800 Subject: [PATCH 2/6] Adds the release-drafter commitish filtering to perhaps generate the release notes better --- .github/release-drafter.yml | 1 + .github/workflows/ci.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 89c8a96ea..2b8169f24 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -44,6 +44,7 @@ include-labels: - 'notable' exclude-labels: - 'skip-changelog' +filter-by-commitish: true category-template: '### $TITLE' change-template: '- $TITLE @$AUTHOR ([#$NUMBER]($URL))' change-title-escapes: '\<*_&#@' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8eb6c832..6abd71dfd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -617,6 +617,7 @@ jobs: version: ${{ steps.get_version.outputs.version }} prerelease: ${{ steps.get_version.outputs.prerelease }} publish: true # ensures release is not marked as draft + committish: ${{ github.sha }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload release archive From 37477d391ef2a28686468cc526a996d3e9e8b517 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 18 Jan 2026 08:21:20 -0800 Subject: [PATCH 3/6] Fix: ensure horizontal scroll for long tag names in list, wrap tags without parent (#11811) --- src-ui/src/app/components/common/input/tags/tags.component.html | 2 +- src-ui/src/app/components/common/input/tags/tags.component.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/app/components/common/input/tags/tags.component.html b/src-ui/src/app/components/common/input/tags/tags.component.html index f04863f40..960245984 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.html +++ b/src-ui/src/app/components/common/input/tags/tags.component.html @@ -28,7 +28,7 @@ -
+
@if (item.id && tags) { @if (getTag(item.id)?.parent) { diff --git a/src-ui/src/app/components/common/input/tags/tags.component.scss b/src-ui/src/app/components/common/input/tags/tags.component.scss index 52292d5cb..c86792728 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.scss +++ b/src-ui/src/app/components/common/input/tags/tags.component.scss @@ -23,7 +23,7 @@ // Dropdown hierarchy reveal for ng-select options ::ng-deep .ng-dropdown-panel .ng-option { - overflow-x: scroll; + overflow-x: scroll !important; .tag-option-row { font-size: 1rem; From ecfeff505453d8ca54d1c5f40c13102ba984a38c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:20:54 -0800 Subject: [PATCH 4/6] Chore: reverse migration order (#11813) --- ...076_workflowaction_order.py => 1075_workflowaction_order.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/documents/migrations/{1076_workflowaction_order.py => 1075_workflowaction_order.py} (89%) diff --git a/src/documents/migrations/1076_workflowaction_order.py b/src/documents/migrations/1075_workflowaction_order.py similarity index 89% rename from src/documents/migrations/1076_workflowaction_order.py rename to src/documents/migrations/1075_workflowaction_order.py index 5c9f7ff52..f7101bf7e 100644 --- a/src/documents/migrations/1076_workflowaction_order.py +++ b/src/documents/migrations/1075_workflowaction_order.py @@ -12,7 +12,7 @@ def populate_action_order(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("documents", "1075_alter_paperlesstask_task_name"), + ("documents", "1074_workflowrun_deleted_at_workflowrun_restored_at_and_more"), ] operations = [ From 771f3f150a745fa6c8ab4c1392fffd2f59104d15 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:18:23 -0800 Subject: [PATCH 5/6] Bump version to 2.20.5 --- pyproject.toml | 2 +- src-ui/package.json | 2 +- src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- uv.lock | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 64df97c17..d4d24a7e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "paperless-ngx" -version = "2.20.4" +version = "2.20.5" description = "A community-supported supercharged document management system: scan, index and archive all your physical documents" readme = "README.md" requires-python = ">=3.10" diff --git a/src-ui/package.json b/src-ui/package.json index 9690e86c0..6d9046f65 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -1,6 +1,6 @@ { "name": "paperless-ngx-ui", - "version": "2.20.4", + "version": "2.20.5", "scripts": { "preinstall": "npx only-allow pnpm", "ng": "ng", diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index d27ab9966..9ebf29d16 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -6,7 +6,7 @@ export const environment = { apiVersion: '9', // match src/paperless/settings.py appTitle: 'Paperless-ngx', tag: 'prod', - version: '2.20.4', + version: '2.20.5', webSocketHost: window.location.host, webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', webSocketBaseUrl: base_url.pathname + 'ws/', diff --git a/src/paperless/version.py b/src/paperless/version.py index 0ce227357..aeeee68e0 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1,6 +1,6 @@ from typing import Final -__version__: Final[tuple[int, int, int]] = (2, 20, 4) +__version__: Final[tuple[int, int, int]] = (2, 20, 5) # Version string like X.Y.Z __full_version_str__: Final[str] = ".".join(map(str, __version__)) # Version string like X.Y diff --git a/uv.lock b/uv.lock index fccc00ada..ac7763525 100644 --- a/uv.lock +++ b/uv.lock @@ -2115,7 +2115,7 @@ wheels = [ [[package]] name = "paperless-ngx" -version = "2.20.4" +version = "2.20.5" source = { virtual = "." } dependencies = [ { name = "babel", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, From 245d9fb4a1989c5b9d11376b75cdba9822acff76 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 10:57:26 -0800 Subject: [PATCH 6/6] Documentation: Add v2.20.5 changelog (#11824) --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/changelog.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 29f955256..f222a7305 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,21 @@ # Changelog +## paperless-ngx 2.20.5 + +### Bug Fixes + +- Fix: ensure horizontal scroll for long tag names in list, wrap tags without parent [@shamoon](https://github.com/shamoon) ([#11811](https://github.com/paperless-ngx/paperless-ngx/pull/11811)) +- Fix: use explicit order field for workflow actions [@shamoon](https://github.com/shamoon) [@stumpylog](https://github.com/stumpylog) ([#11781](https://github.com/paperless-ngx/paperless-ngx/pull/11781)) + +### All App Changes + +
+2 changes + +- Fix: ensure horizontal scroll for long tag names in list, wrap tags without parent [@shamoon](https://github.com/shamoon) ([#11811](https://github.com/paperless-ngx/paperless-ngx/pull/11811)) +- Fix: use explicit order field for workflow actions [@shamoon](https://github.com/shamoon) [@stumpylog](https://github.com/stumpylog) ([#11781](https://github.com/paperless-ngx/paperless-ngx/pull/11781)) +
+ ## paperless-ngx 2.20.4 ### Security