diff --git a/Pipfile b/Pipfile index 2e86f2a42..48759307c 100644 --- a/Pipfile +++ b/Pipfile @@ -19,6 +19,7 @@ django-extensions = "*" django-filter = "~=2.4.0" django-q = "~=1.3.4" djangorestframework = "~=3.12.2" +filelock = "*" fuzzywuzzy = "*" gunicorn = "*" imap-tools = "*" @@ -26,6 +27,7 @@ langdetect = "*" pdftotext = "*" pathvalidate = "*" pillow = "*" +pikepdf = "*" python-gnupg = "*" python-dotenv = "*" python-dateutil = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 6158a70e0..1cfccb8ff 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b10db53eb22d917723aa6107ff0970dc4e2aa886ee03d3ae08a994a856d57986" + "sha256": "3d576f289958226a7583e4c471c7f8c11bff6933bf093185f623cfb381a92412" }, "pipfile-spec": 6, "requires": { @@ -197,6 +197,14 @@ "index": "pypi", "version": "==3.12.2" }, + "filelock": { + "hashes": [ + "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", + "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + ], + "index": "pypi", + "version": "==3.0.12" + }, "fuzzywuzzy": { "hashes": [ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", @@ -425,7 +433,7 @@ "sha256:fe0ca120e3347c851c34a91041d574f3c588d832023906d8ae18d66d042e8a52", "sha256:fe8e0152672f24d8bfdecc725f97e9013f2de1b41849150959526ca3562bd3ef" ], - "markers": "python_version < '3.9'", + "index": "pypi", "version": "==2.2.0" }, "pillow": { @@ -858,10 +866,10 @@ }, "certifi": { "hashes": [ - "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd", - "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4" + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" ], - "version": "==2020.11.8" + "version": "==2020.12.5" }, "chardet": { "hashes": [ @@ -961,17 +969,18 @@ }, "faker": { "hashes": [ - "sha256:7bca5b074299ac6532be2f72979e6793f1a2403ca8105cb4cf0b385a964469c4", - "sha256:fb21a76064847561033d8cab1cfd11af436ddf2c6fe72eb51b3cda51dff86bdc" + "sha256:1fcb415562ee6e2395b041e85fa6901d4708d30b84d54015226fa754ed0822c3", + "sha256:e8beccb398ee9b8cc1a91d9295121d66512b6753b4846eb1e7370545d46b3311" ], - "markers": "python_version >= '3.5'", - "version": "==5.0.0" + "markers": "python_version >= '3.6'", + "version": "==5.0.1" }, "filelock": { "hashes": [ "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" ], + "index": "pypi", "version": "==3.0.12" }, "idna": { @@ -1100,11 +1109,11 @@ }, "pygments": { "hashes": [ - "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0", - "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773" + "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716", + "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08" ], "markers": "python_version >= '3.5'", - "version": "==2.7.2" + "version": "==2.7.3" }, "pyparsing": { "hashes": [ @@ -1313,11 +1322,11 @@ }, "virtualenv": { "hashes": [ - "sha256:07cff122e9d343140366055f31be4dcd61fd598c69d11cd33a9d9c8df4546dd7", - "sha256:e0aac7525e880a429764cefd3aaaff54afb5d9f25c82627563603f5d7de5a6e5" + "sha256:54b05fc737ea9c9ee9f8340f579e5da5b09fb64fd010ab5757eb90268616907c", + "sha256:b7a8ec323ee02fb2312f098b6b4c9de99559b462775bc8fe3627a73706603c1b" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.2.1" + "version": "==20.2.2" }, "zipp": { "hashes": [ diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index dfa7cfc65..13a0ba035 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -23,8 +23,14 @@ wait_for_postgres() { echo "Waiting for PostgreSQL to start..." host="${PAPERLESS_DBHOST}" + port="${PAPERLESS_DBPORT}" - while !/download/``: Download the original document. -* ``/api/documents//thumb/``: Download the PNG thumbnail of a document. -* ``/api/documents//preview/``: Display the original document inline, +* ``/api/documents//download/``: Download the document. +* ``/api/documents//preview/``: Display the document inline, without downloading it. +* ``/api/documents//thumb/``: Download the PNG thumbnail of a document. + +Paperless generates archived PDF/A documents from consumed files and stores both +the original files as well as the archived files. By default, the endpoints +for previews and downloads serve the archived file, if it is available. +Otherwise, the original file is served. +Some document cannot be archived. + +The endpoints correctly serve the response header fields ``Content-Disposition`` +and ``Content-Type`` to indicate the filename for download and the type of content of +the document. + +In order to download or preview the original document when an archied document is available, +supply the query parameter ``original=true``. .. hint:: @@ -38,13 +70,43 @@ individual documents: are in place. However, if you use these old URLs to access documents, you should update your app or script to use the new URLs. -.. note:: - The document endpoint provides tags, document types and correspondents as - ids in their corresponding fields. These are writeable. Paperless also - offers read-only objects for assigned tags, types and correspondents, - however, these might be removed in the future. As for now, the front end - requires them. +Getting document metadata +######################### + +The api also has an endpoint to retrieve read-only metadata about specific documents. this +information is not served along with the document objects, since it requires reading +files and would therefore slow down document lists considerably. + +Access the metadata of a document with an ID ``id`` at ``/api/documents//metadata/``. + +The endpoint reports the following data: + +* ``original_checksum``: MD5 checksum of the original document. +* ``original_size``: Size of the original document, in bytes. +* ``original_mime_type``: Mime type of the original document. +* ``media_filename``: Current filename of the document, under which it is stored inside the media directory. +* ``has_archive_version``: True, if this document is archived, false otherwise. +* ``original_metadata``: A list of metadata associated with the original document. See below. +* ``archive_checksum``: MD5 checksum of the archived document, or null. +* ``archive_size``: Size of the archived document in bytes, or null. +* ``archive_metadata``: Metadata associated with the archived document, or null. See below. + +File metadata is reported as a list of objects in the following form: + +.. code:: json + + [ + { + "namespace": "http://ns.adobe.com/pdf/1.3/", + "prefix": "pdf", + "key": "Producer", + "value": "SparklePDF, Fancy edition" + }, + ] + +``namespace`` and ``prefix`` can be null. The actual metadata reported depends on the file type and the metadata +available in that specific document. Paperless only reports PDF metadata at this point. Authorization ############# @@ -54,11 +116,11 @@ The REST api provides three different forms of authentication. 1. Basic authentication Authorize by providing a HTTP header in the form - + .. code:: Authorization: Basic - + where ``credentials`` is a base64-encoded string of ``:`` 2. Session authentication @@ -79,7 +141,7 @@ The REST api provides three different forms of authentication. .. code:: Authorization: Token - + Tokens can be managed and revoked in the paperless admin. Searching for documents @@ -111,7 +173,7 @@ Result list object returned by the endpoint: "page_count": 1, "corrected_query": "", "results": [ - + ] } @@ -131,12 +193,12 @@ Result object: { "id": 1, "highlights": [ - + ], "score": 6.34234, "rank": 23, "document": { - + } } @@ -168,7 +230,7 @@ Each fragment contains a list of strings, and some of them are marked as a highl {"text": " fragment with a highlight."} ] ] - + When ``term`` is present within a string, the word within ``text`` should be highlighted. diff --git a/docs/changelog.rst b/docs/changelog.rst index 116c2e07c..a50fc31d5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,47 @@ Changelog ********* +paperless-ng 0.9.6 +################## + +This release focusses primarily on many small issues with the UI. + +* Front end + + * Paperless now has proper window titles. + * Fixed an issue with the small cards when more than 7 tags were used. + * Navigation of the "Show all" links adjusted. They navigate to the saved view now, if available in the sidebar. + * Some indication on the document lists that a filter is active was added. + * There's a new filter to filter for documents that do *not* have a certain tag. + * The file upload box now shows upload progress. + * The document edit page was reorganized. + * The document edit page shows various information about a document. + * An issue with the height of the preview was fixed. + * Table issues with too long document titles fixed. + +* API + + * The API now serves file names with documents. + * The API now serves various metadata about documents. + * API documentation updated. + +* Other + + * Fixed an issue with the docker image when a non-standard PostgreSQL port was used. + * The docker image was trying check for installed languages before actually installing them. + * ``FILENAME_FORMAT`` placeholder for document types. + * The filename formatter is now less restrictive with file names and tries to + conserve the original correspondents, types and titles as much as possible. + * The filename formatter does not include the document ID in filenames anymore. It will + rather append ``_01``, ``_02``, etc when it detects duplicate filenames. + +.. note:: + + The changes to the filename format will apply to newly added documents and changed documents. + If you want all files to reflect these changes, execute the ``document_renamer`` management + command. + + paperless-ng 0.9.5 ################## diff --git a/docs/configuration.rst b/docs/configuration.rst index 2ec34f803..d3f47215b 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -152,6 +152,16 @@ PAPERLESS_AUTO_LOGIN_USERNAME= Defaults to none, which disables this feature. + +PAPERLESS_COOKIE_PREFIX= + Specify a prefix that is added to the cookies used by paperless to identify + the currently logged in user. This is useful for when you're running two + instances of paperless on the same host. + + After changing this, you will have to login again. + + Defaults to ``""``, which does not alter the cookie names. + .. _configuration-ocr: OCR settings diff --git a/docs/usage_overview.rst b/docs/usage_overview.rst index db50d5706..bb9ecd452 100644 --- a/docs/usage_overview.rst +++ b/docs/usage_overview.rst @@ -57,7 +57,7 @@ Adding documents to paperless ############################# Once you've got Paperless setup, you need to start feeding documents into it. -Currently, there are three options: the consumption directory, IMAP (email), and +Currently, there are four options: the consumption directory, the dashboard, IMAP (email), and HTTP POST. When adding documents to paperless, it will perform the following operations on @@ -82,8 +82,7 @@ your documents: No matter which options you choose, Paperless will always store the original document that it found in the consumption directory or in the mail and will never overwrite that document. Archived versions are stored alongside the - digital versions. - + original versions. The consumption directory @@ -107,6 +106,12 @@ files from the scanner. Typically, you're looking at an FTP server like .. TODO: hyperref to configuration of the location of this magic folder. +Dashboard upload +================ + +The dashboard has a file drop field to upload documents to paperless. Simply drag a file +onto this field or select a file with the file dialog. Multiple files are supported. + .. _usage-email: IMAP (Email) @@ -183,6 +188,63 @@ You can also submit a document using the REST API, see :ref:`api-file_uploads` f .. _basic-searching: + +Best practices +############## + +Paperless offers a couple tools that help you organize your document collection. However, +it is up to you to use them in a way that helps you organize documents and find specific +documents when you need them. This section offers a couple ideas for managing your collection. + +Document types allow you to classify documents according to what they are. You can define +types such as "Receipt", "Invoice", or "Contract". If you used to collect all your receipts +in a single binder, you can recreate that system in paperless by defining a document type, +assigning documents to that type and then filtering by that type to only see all receipts. + +Not all documents need document types. Sometimes its hard to determine what the type of a +document is or it is hard to justify creating a document type that you only need once or twice. +This is okay. As long as the types you define help you organize your collection in the way +you want, paperless is doing its job. + +Tags can be used in many different ways. Think of tags are more versatile folders or binders. +If you have a binder for documents related to university / your car or health care, you can +create these binders in paperless by creating tags and assigning them to relevant documents. +Just as with documents, you can filter the document list by tags and only see documents of +a certain topic. + +With physical documents, you'll often need to decide which folder the document belongs to. +The advantage of tags over folders and binders is that a single document can have multiple +tags. A physical document cannot magically appear in two different folders, but with tags, +this is entirely possible. + +.. hint:: + + This can be used in many different ways. One example: Imagine you're working on a particular + task, such as signing up for university. Usually you'll need to collect a bunch of different + documents that are already sorted into various folders. With the tag system of paperless, + you can create a new group of documents that are relevant to this task without destroying + the already existing organization. When you're done with the task, you could delete the + tag again, which would be equal to sorting documents back into the folder they belong into. + Or keep the tag, up to you. + +All of the logic above applies to correspondents as well. Attach them to documents if you +feel that they help you organize your collection. + +When you've started organizing your documents, create a couple saved views for document collections +you regularly access. This is equal to having labeled physical binders on your desk, except +that these saved views are dynamic and simply update themselves as you add documents to the system. + +Here are a couple examples of tags and types that you could use in your collection. + +* An ``inbox`` tag for newly added documents that you haven't manually edited yet. +* A tag ``car`` for everything car related (repairs, registration, insurance, etc) +* A tag ``todo`` for documents that you still need to do something with, such as reply, or + perform some task online. +* A tag ``bank account x`` for all bank statement related to that account. +* A tag ``mail`` for anything that you added to paperless via its mail processing capabilities. +* A tag ``missing_metadata`` when you still need to add some metadata to a document, but can't + or don't want to do this right now. + Searching ######### diff --git a/paperless.conf.example b/paperless.conf.example index 32c0e56b4..910fc22a0 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -30,6 +30,7 @@ #PAPERLESS_FORCE_SCRIPT_NAME= #PAPERLESS_STATIC_URL=/static/ #PAPERLESS_AUTO_LOGIN_USERNAME= +#PAPERLESS_COOKIE_PREFIX= # OCR settings diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index 40c0991e7..0ee36b478 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -45,6 +45,10 @@ import { StatisticsWidgetComponent } from './components/dashboard/widgets/statis import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'; import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; import { PdfViewerModule } from 'ng2-pdf-viewer'; +import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; +import { YesNoPipe } from './pipes/yes-no.pipe'; +import { FileSizePipe } from './pipes/file-size.pipe'; +import { DocumentTitlePipe } from './pipes/document-title.pipe'; @NgModule({ declarations: [ @@ -81,7 +85,11 @@ import { PdfViewerModule } from 'ng2-pdf-viewer'; SavedViewWidgetComponent, StatisticsWidgetComponent, UploadFileWidgetComponent, - WidgetFrameComponent + WidgetFrameComponent, + WelcomeWidgetComponent, + YesNoPipe, + FileSizePipe, + DocumentTitlePipe ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/common/input/date-time/date-time.component.html b/src-ui/src/app/components/common/input/date-time/date-time.component.html index eaed0e185..7c002db1b 100644 --- a/src-ui/src/app/components/common/input/date-time/date-time.component.html +++ b/src-ui/src/app/components/common/input/date-time/date-time.component.html @@ -3,11 +3,10 @@ -
+
-
diff --git a/src-ui/src/app/components/common/input/date-time/date-time.component.ts b/src-ui/src/app/components/common/input/date-time/date-time.component.ts index 07238e94f..6a04c5b27 100644 --- a/src-ui/src/app/components/common/input/date-time/date-time.component.ts +++ b/src-ui/src/app/components/common/input/date-time/date-time.component.ts @@ -40,7 +40,7 @@ export class DateTimeComponent implements OnInit,ControlValueAccessor { titleDate: string = "Date" @Input() - titleTime: string = "Time" + titleTime: string @Input() disabled: boolean = false 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 b2ad0944f..8029dd860 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 @@ -8,7 +8,7 @@
-
+
diff --git a/src-ui/src/app/components/common/input/text/text.component.ts b/src-ui/src/app/components/common/input/text/text.component.ts index ffb8c0c3d..0a1a05749 100644 --- a/src-ui/src/app/components/common/input/text/text.component.ts +++ b/src-ui/src/app/components/common/input/text/text.component.ts @@ -1,6 +1,5 @@ -import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { v4 as uuidv4 } from 'uuid'; +import { Component, forwardRef } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { AbstractInputComponent } from '../abstract-input'; @Component({ diff --git a/src-ui/src/app/components/dashboard/dashboard.component.html b/src-ui/src/app/components/dashboard/dashboard.component.html index 3e6438181..627e7ff22 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.html +++ b/src-ui/src/app/components/dashboard/dashboard.component.html @@ -4,11 +4,7 @@
- -

This space is reserved to display your saved views. Go to your documents and save a view - to have it displayed - here!

-
+ @@ -22,4 +18,4 @@
-
\ No newline at end of file +
diff --git a/src-ui/src/app/components/dashboard/dashboard.component.ts b/src-ui/src/app/components/dashboard/dashboard.component.ts index aa2426179..c7410c3f2 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.ts +++ b/src-ui/src/app/components/dashboard/dashboard.component.ts @@ -1,5 +1,7 @@ import { Component, OnInit } from '@angular/core'; +import { Title } from '@angular/platform-browser'; import { SavedViewConfigService } from 'src/app/services/saved-view-config.service'; +import { environment } from 'src/environments/environment'; @Component({ @@ -10,13 +12,15 @@ import { SavedViewConfigService } from 'src/app/services/saved-view-config.servi export class DashboardComponent implements OnInit { constructor( - public savedViewConfigService: SavedViewConfigService) { } + public savedViewConfigService: SavedViewConfigService, + private titleService: Title) { } savedViews = [] ngOnInit(): void { this.savedViews = this.savedViewConfigService.getDashboardConfigs() + this.titleService.setTitle(`Dashboard - ${environment.appTitle}`) } } diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 413df0ae4..a55bf57fc 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -29,8 +29,12 @@ export class SavedViewWidgetComponent implements OnInit { } showAll() { - this.list.load(this.savedView) - this.router.navigate(["documents"]) + if (this.savedView.showInSideBar) { + this.router.navigate(['view', this.savedView.id]) + } else { + this.list.load(this.savedView) + this.router.navigate(["documents"]) + } } } diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html index cb114e49e..013486a47 100644 --- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html @@ -1,15 +1,18 @@ -
- +
+ + - - + + +
+

Uploading {{uploadStatus.length}} file(s)

+ + +
+
\ No newline at end of file diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts index a95d5f4db..2ea4825f1 100644 --- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts @@ -1,8 +1,15 @@ +import { HttpEventType } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'; import { DocumentService } from 'src/app/services/rest/document.service'; import { Toast, ToastService } from 'src/app/services/toast.service'; + +interface UploadStatus { + loaded: number + total: number +} + @Component({ selector: 'app-upload-file-widget', templateUrl: './upload-file-widget.component.html', @@ -16,26 +23,59 @@ export class UploadFileWidgetComponent implements OnInit { } public fileOver(event){ - console.log(event); } - + public fileLeave(event){ - console.log(event); } - + + uploadStatus: UploadStatus[] = [] + completedFiles = 0 + + uploadVisible = false + + get loadedSum() { + return this.uploadStatus.map(s => s.loaded).reduce((a,b) => a+b, this.completedFiles > 0 ? 1 : 0) + } + + get totalSum() { + return this.uploadStatus.map(s => s.total).reduce((a,b) => a+b, 1) + } + public dropped(files: NgxFileDropEntry[]) { for (const droppedFile of files) { if (droppedFile.fileEntry.isFile) { - const fileEntry = droppedFile.fileEntry as FileSystemFileEntry; - console.log(fileEntry) + let uploadStatusObject: UploadStatus = {loaded: 0, total: 1} + this.uploadStatus.push(uploadStatusObject) + this.uploadVisible = true + + const fileEntry = droppedFile.fileEntry as FileSystemFileEntry; fileEntry.file((file: File) => { - console.log(file) - const formData = new FormData() + let formData = new FormData() formData.append('document', file, file.name) - this.documentService.uploadDocument(formData).subscribe(result => { - this.toastService.showToast(Toast.make("Information", "The document has been uploaded and will be processed by the consumer shortly.")) + + this.documentService.uploadDocument(formData).subscribe(event => { + if (event.type == HttpEventType.UploadProgress) { + uploadStatusObject.loaded = event.loaded + uploadStatusObject.total = event.total + } else if (event.type == HttpEventType.Response) { + this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1) + this.completedFiles += 1 + this.toastService.showToast(Toast.make("Information", "The document has been uploaded and will be processed by the consumer shortly.")) + } + }, error => { - this.toastService.showToast(Toast.makeError("An error has occured while uploading the document. Sorry!")) + this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1) + this.completedFiles += 1 + switch (error.status) { + case 400: { + this.toastService.showToast(Toast.makeError(`There was an error while uploading the document: ${error.error.document}`)) + break; + } + default: { + this.toastService.showToast(Toast.makeError("An error has occurred while uploading the document. Sorry!")) + break; + } + } }) }); } diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html new file mode 100644 index 000000000..0caf55f11 --- /dev/null +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html @@ -0,0 +1,16 @@ + + + + +

Paperless is running! :)

+

You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. + After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and have them displayed on the dashboard instead of this message.

+

Paperless offers some more features that try to make your life easier, such as:

+
    +
  • Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.
  • +
  • You can configure paperless to read your mails and add documents from attached files.
  • +
+

Consult the documentation on how to use these features. The section on basic usage also has some information on how to use paperless in general.

+
+ +
\ No newline at end of file diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts new file mode 100644 index 000000000..5e8c2494b --- /dev/null +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WelcomeWidgetComponent } from './welcome-widget.component'; + +describe('WelcomeWidgetComponent', () => { + let component: WelcomeWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ WelcomeWidgetComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WelcomeWidgetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts new file mode 100644 index 000000000..71a87189c --- /dev/null +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-welcome-widget', + templateUrl: './welcome-widget.component.html', + styleUrls: ['./welcome-widget.component.scss'] +}) +export class WelcomeWidgetComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html index d0f637935..1d7d2d906 100644 --- a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html +++ b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html @@ -1,4 +1,4 @@ -
+
{{title}}
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index 93aec64bc..bec8a59a2 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -15,14 +15,14 @@ Download -
- - + -
+
+ Original document metadata + + +
+ + + + + + + +
{{m.prefix}}:{{m.key}}{{m.value}}
+
+ +
+ + Archived document metadata +
+ +
+ + + + + + + +
{{m.prefix}}:{{m.key}}{{m.value}}
+
+ + + + + +
  -   +    
@@ -70,4 +176,4 @@
-
+
\ No newline at end of file diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 253833792..329077693 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; +import { Title } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; @@ -11,6 +12,7 @@ import { OpenDocumentsService } from 'src/app/services/open-documents.service'; import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; import { DocumentService } from 'src/app/services/rest/document.service'; +import { environment } from 'src/environments/environment'; import { DeleteDialogComponent } from '../common/delete-dialog/delete-dialog.component'; import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'; import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'; @@ -22,6 +24,9 @@ import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/do }) export class DocumentDetailComponent implements OnInit { + public expandOriginalMetadata = false; + public expandArchivedMetadata = false; + documentId: number document: PaperlessDocument metadata: PaperlessDocumentMetadata @@ -51,7 +56,8 @@ export class DocumentDetailComponent implements OnInit { private router: Router, private modalService: NgbModal, private openDocumentService: OpenDocumentsService, - private documentListViewService: DocumentListViewService) { } + private documentListViewService: DocumentListViewService, + private titleService: Title) { } ngOnInit(): void { this.documentForm.valueChanges.subscribe(wow => { @@ -80,6 +86,7 @@ export class DocumentDetailComponent implements OnInit { updateComponent(doc: PaperlessDocument) { this.document = doc + this.titleService.setTitle(`${doc.title} - ${environment.appTitle}`) this.documentsService.getMetadata(doc.id).subscribe(result => { this.metadata = result }) diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index bfc59b526..8f3fced66 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -12,7 +12,7 @@ {{(document.correspondent$ | async)?.name}} {{(document.correspondent$ | async)?.name}}: - {{document.title}} + {{document.title | documentTitle}}
#{{document.archive_serial_number}}
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index 71a7fb01a..86e28442c 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -1,8 +1,14 @@
-
-
- +
+ +
+
+ +
+
+ + {{moreTags}} +
@@ -11,7 +17,7 @@ {{(document.correspondent$ | async)?.name}}: - {{document.title}} + {{document.title | documentTitle}}