id: DocumentSource.MailFetch,
name: $localize`Mail Fetch`,
},
+ {
+ id: DocumentSource.WebUI,
+ name: $localize`Web UI`,
+ },
]
export const SCHEDULE_DATE_FIELD_OPTIONS = [
ConsumeFolder = 1,
ApiUpload = 2,
MailFetch = 3,
+ WebUI = 4,
}
export enum WorkflowTriggerType {
private uploadFile(file: File) {
let formData = new FormData()
formData.append('document', file, file.name)
+ formData.append('from_webui', 'true')
let status = this.websocketStatusService.newFileUpload(file.name)
status.message = $localize`Connecting...`
ConsumeFolder = 1
ApiUpload = 2
MailFetch = 3
+ WebUI = 4
@dataclasses.dataclass
+++ /dev/null
-# Generated by Django 5.1.6 on 2025-02-16 16:31
-
-from django.db import migrations
-from django.db import models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("documents", "1062_alter_savedviewfilterrule_rule_type"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="workflowactionwebhook",
- name="url",
- field=models.CharField(
- help_text="The destination URL for the notification.",
- max_length=256,
- verbose_name="webhook url",
- ),
- ),
- ]
--- /dev/null
+# Generated by Django 5.1.6 on 2025-02-20 04:55
+
+import multiselectfield.db.fields
+from django.db import migrations
+from django.db import models
+
+
+# WebUI source was added, so all existing APIUpload sources should be updated to include WebUI
+def update_workflow_sources(apps, schema_editor):
+ WorkflowTrigger = apps.get_model("documents", "WorkflowTrigger")
+ for trigger in WorkflowTrigger.objects.all():
+ sources = list(trigger.sources)
+ if 2 in sources:
+ sources.append(4)
+ trigger.sources = sources
+ trigger.save()
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("documents", "1062_alter_savedviewfilterrule_rule_type"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="workflowactionwebhook",
+ name="url",
+ field=models.CharField(
+ help_text="The destination URL for the notification.",
+ max_length=256,
+ verbose_name="webhook url",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="workflowtrigger",
+ name="sources",
+ field=multiselectfield.db.fields.MultiSelectField(
+ choices=[
+ (1, "Consume Folder"),
+ (2, "Api Upload"),
+ (3, "Mail Fetch"),
+ (4, "Web UI"),
+ ],
+ default="1,2,3,4",
+ max_length=7,
+ ),
+ ),
+ migrations.RunPython(
+ code=update_workflow_sources,
+ reverse_code=migrations.RunPython.noop,
+ ),
+ ]
CONSUME_FOLDER = DocumentSource.ConsumeFolder.value, _("Consume Folder")
API_UPLOAD = DocumentSource.ApiUpload.value, _("Api Upload")
MAIL_FETCH = DocumentSource.MailFetch.value, _("Mail Fetch")
+ WEB_UI = DocumentSource.WebUI.value, _("Web UI")
class ScheduleDateField(models.TextChoices):
ADDED = "added", _("Added")
)
sources = MultiSelectField(
- max_length=5,
+ max_length=7,
choices=DocumentSourceChoices.choices,
- default=f"{DocumentSource.ConsumeFolder},{DocumentSource.ApiUpload},{DocumentSource.MailFetch}",
+ default=f"{DocumentSource.ConsumeFolder},{DocumentSource.ApiUpload},{DocumentSource.MailFetch},{DocumentSource.WebUI}",
)
filter_path = models.CharField(
required=False,
)
+ from_webui = serializers.BooleanField(
+ label="Documents are from Paperless-ngx WebUI",
+ write_only=True,
+ required=False,
+ )
+
def validate_document(self, document):
document_data = document.file.read()
mime_type = magic.from_buffer(document_data, mime=True)
from documents.models import ShareLink
from documents.models import StoragePath
from documents.models import Tag
+from documents.models import WorkflowTrigger
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import DocumentConsumeDelayMixin
self.assertEqual(overrides.filename, "simple.pdf")
self.assertEqual(overrides.custom_field_ids, [custom_field.id])
+ def test_upload_with_webui_source(self):
+ """
+ GIVEN: A document with a source file
+ WHEN: Upload the document with 'from_webui' flag
+ THEN: Consume is called with the source set as WebUI
+ """
+ self.consume_file_mock.return_value = celery.result.AsyncResult(
+ id=str(uuid.uuid4()),
+ )
+
+ with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
+ response = self.client.post(
+ "/api/documents/post_document/",
+ {"document": f, "from_webui": True},
+ )
+
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ self.consume_file_mock.assert_called_once()
+
+ input_doc, overrides = self.get_last_consume_delay_call_args()
+
+ self.assertEqual(input_doc.source, WorkflowTrigger.DocumentSourceChoices.WEB_UI)
+
def test_upload_invalid_pdf(self):
"""
GIVEN: Invalid PDF named "*.pdf" that mime_type is in settings.CONSUMER_PDF_RECOVERABLE_MIME_TYPES
created = serializer.validated_data.get("created")
archive_serial_number = serializer.validated_data.get("archive_serial_number")
custom_field_ids = serializer.validated_data.get("custom_fields")
+ from_webui = serializer.validated_data.get("from_webui")
t = int(mktime(datetime.now().timetuple()))
os.utime(temp_file_path, times=(t, t))
input_doc = ConsumableDocument(
- source=DocumentSource.ApiUpload,
+ source=DocumentSource.WebUI if from_webui else DocumentSource.ApiUpload,
original_file=temp_file_path,
)
input_doc_overrides = DocumentMetadataOverrides(