Compare commits
18 Commits
dev
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
d3b1246cb7 | ||
|
84638d3ed3 | ||
|
5db511afdf | ||
|
a8de26f88a | ||
|
f753f6dc46 | ||
|
1f5086164b | ||
|
b4047e73bb | ||
|
4263d2196c | ||
|
ac780134fb | ||
|
1d9482acc3 | ||
|
caa3c13edd | ||
|
24e863b298 | ||
|
0c9d615f56 | ||
|
4c49da9ece | ||
|
90561857e8 | ||
|
fc68f55d1a | ||
|
6a8ec182fa | ||
|
69541546ea |
@ -32,7 +32,7 @@ RUN set -eux \
|
|||||||
# Purpose: Installs s6-overlay and rootfs
|
# Purpose: Installs s6-overlay and rootfs
|
||||||
# Comments:
|
# Comments:
|
||||||
# - Don't leave anything extra in here either
|
# - Don't leave anything extra in here either
|
||||||
FROM ghcr.io/astral-sh/uv:0.6.5-python3.12-bookworm-slim AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.6.9-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
|
@ -565,19 +565,15 @@ document.
|
|||||||
|
|
||||||
### Managing encryption {#encryption}
|
### Managing encryption {#encryption}
|
||||||
|
|
||||||
Documents can be stored in Paperless using GnuPG encryption.
|
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
|
|
||||||
Encryption is deprecated since [paperless-ng 0.9](changelog.md#paperless-ng-090) and doesn't really
|
Encryption was removed in [paperless-ng 0.9](changelog.md#paperless-ng-090)
|
||||||
provide any additional security, since you have to store the passphrase
|
because it did not really provide any additional security, the passphrase
|
||||||
in a configuration file on the same system as the encrypted documents
|
was stored in a configuration file on the same system as the documents.
|
||||||
for paperless to work. Furthermore, the entire text content of the
|
Furthermore, the entire text content of the documents is stored plain in
|
||||||
documents is stored plain in the database, even if your documents are
|
the database, even if your documents are encrypted. Filenames are not
|
||||||
encrypted. Filenames are not encrypted as well.
|
encrypted as well. Finally, the web server provides transparent access to
|
||||||
|
your encrypted documents.
|
||||||
Also, the web server provides transparent access to your encrypted
|
|
||||||
documents.
|
|
||||||
|
|
||||||
Consider running paperless on an encrypted filesystem instead, which
|
Consider running paperless on an encrypted filesystem instead, which
|
||||||
will then at least provide security against physical hardware theft.
|
will then at least provide security against physical hardware theft.
|
||||||
|
@ -270,7 +270,7 @@ The following methods are supported:
|
|||||||
- `remove_tag`
|
- `remove_tag`
|
||||||
- Requires `parameters`: `{ "tag": TAG_ID }`
|
- Requires `parameters`: `{ "tag": TAG_ID }`
|
||||||
- `modify_tags`
|
- `modify_tags`
|
||||||
- Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and / or `{ "remove_tags": [LIST_OF_TAG_IDS] }`
|
- Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and `{ "remove_tags": [LIST_OF_TAG_IDS] }`
|
||||||
- `delete`
|
- `delete`
|
||||||
- No `parameters` required
|
- No `parameters` required
|
||||||
- `reprocess`
|
- `reprocess`
|
||||||
|
@ -708,7 +708,8 @@ Paperless runs on Raspberry Pi. However, some things are rather slow on
|
|||||||
the Pi and configuring some options in paperless can help improve
|
the Pi and configuring some options in paperless can help improve
|
||||||
performance immensely:
|
performance immensely:
|
||||||
|
|
||||||
- Stick with SQLite to save some resources.
|
- Stick with SQLite to save some resources. See [troubleshooting](troubleshooting.md#log-reports-creating-paperlesstask-failed)
|
||||||
|
if you encounter issues with SQLite locking.
|
||||||
- If you do not need the filesystem-based consumer, consider disabling it
|
- If you do not need the filesystem-based consumer, consider disabling it
|
||||||
entirely by setting [`PAPERLESS_CONSUMER_DISABLE`](configuration.md#PAPERLESS_CONSUMER_DISABLE) to `true`.
|
entirely by setting [`PAPERLESS_CONSUMER_DISABLE`](configuration.md#PAPERLESS_CONSUMER_DISABLE) to `true`.
|
||||||
- Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that paperless will
|
- Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that paperless will
|
||||||
|
@ -292,7 +292,9 @@ many workers attempting to access the database simultaneously.
|
|||||||
Consider changing to the PostgreSQL database if you will be processing
|
Consider changing to the PostgreSQL database if you will be processing
|
||||||
many documents at once often. Otherwise, try tweaking the
|
many documents at once often. Otherwise, try tweaking the
|
||||||
[`PAPERLESS_DB_TIMEOUT`](configuration.md#PAPERLESS_DB_TIMEOUT) setting to allow more time for the database to
|
[`PAPERLESS_DB_TIMEOUT`](configuration.md#PAPERLESS_DB_TIMEOUT) setting to allow more time for the database to
|
||||||
unlock. This may have minor performance implications.
|
unlock. Additionally, you can change your SQLite database to use ["Write-Ahead Logging"](https://sqlite.org/wal.html).
|
||||||
|
These changes may have minor performance implications but can help
|
||||||
|
prevent database locking issues.
|
||||||
|
|
||||||
## granian fails to start with "is not a valid port number"
|
## granian fails to start with "is not a valid port number"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "paperless-ngx"
|
name = "paperless-ngx"
|
||||||
version = "2.14.7"
|
version = "2.15.0"
|
||||||
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<div class="ms-2 ms-md-3 d-inline-block" [class.d-md-none]="slimSidebarEnabled">
|
<div class="ms-2 ms-md-3 d-inline-block" [class.d-md-none]="slimSidebarEnabled">
|
||||||
@if (customAppTitle?.length) {
|
@if (customAppTitle?.length) {
|
||||||
<div class="d-flex flex-column align-items-start">
|
<div class="d-flex flex-column align-items-start custom-title">
|
||||||
<span class="title">{{customAppTitle}}</span>
|
<span class="title">{{customAppTitle}}</span>
|
||||||
<span class="byline text-uppercase font-monospace" i18n>by Paperless-ngx</span>
|
<span class="byline text-uppercase font-monospace" i18n>by Paperless-ngx</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -244,7 +244,7 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (min-width: 366px) and (max-width: 768px) {
|
||||||
.navbar-toggler {
|
.navbar-toggler {
|
||||||
// compensate for 2 buttons on the right
|
// compensate for 2 buttons on the right
|
||||||
margin-right: 45px;
|
margin-right: 45px;
|
||||||
@ -257,6 +257,13 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 345px) {
|
||||||
|
.custom-title {
|
||||||
|
max-width: 110px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:host ::ng-deep .dropdown.show .dropdown-toggle,
|
:host ::ng-deep .dropdown.show .dropdown-toggle,
|
||||||
:host ::ng-deep .dropdown-toggle:hover {
|
:host ::ng-deep .dropdown-toggle:hover {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
@ -492,11 +492,9 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
|||||||
component.selectionModel.items = items
|
component.selectionModel.items = items
|
||||||
component.selectionModel = selectionModel
|
component.selectionModel = selectionModel
|
||||||
component.selectionModel.intersection = Intersection.Include
|
component.selectionModel.intersection = Intersection.Include
|
||||||
console.log(component.selectionModel.items[0])
|
|
||||||
component.selectionModel.set(null, ToggleableItemState.Selected)
|
component.selectionModel.set(null, ToggleableItemState.Selected)
|
||||||
component.selectionModel.intersection = Intersection.Exclude
|
component.selectionModel.intersection = Intersection.Exclude
|
||||||
component.selectionModel.toggleIntersection()
|
component.selectionModel.toggleIntersection()
|
||||||
console.log(component.selectionModel)
|
|
||||||
expect(component.selectionModel.getExcludedItems()).toEqual([
|
expect(component.selectionModel.getExcludedItems()).toEqual([
|
||||||
negativeNullItem,
|
negativeNullItem,
|
||||||
])
|
])
|
||||||
|
@ -825,10 +825,6 @@ export class DocumentDetailComponent
|
|||||||
error: (error) => {
|
error: (error) => {
|
||||||
this.networkActive = false
|
this.networkActive = false
|
||||||
const canEdit =
|
const canEdit =
|
||||||
this.permissionsService.currentUserCan(
|
|
||||||
PermissionAction.Change,
|
|
||||||
PermissionType.Document
|
|
||||||
) &&
|
|
||||||
this.permissionsService.currentUserHasObjectPermissions(
|
this.permissionsService.currentUserHasObjectPermissions(
|
||||||
PermissionAction.Change,
|
PermissionAction.Change,
|
||||||
this.document
|
this.document
|
||||||
|
@ -156,6 +156,72 @@ describe(`Additional service tests for SavedViewService`, () => {
|
|||||||
httpTestingController.verify() // no reload
|
httpTestingController.verify() // no reload
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should reload after create, delete, patch and patchMany', () => {
|
||||||
|
const reloadSpy = jest.spyOn(service, 'reload')
|
||||||
|
service
|
||||||
|
.create({
|
||||||
|
name: 'New Saved View',
|
||||||
|
show_on_dashboard: true,
|
||||||
|
show_in_sidebar: true,
|
||||||
|
sort_field: 'name',
|
||||||
|
sort_reverse: true,
|
||||||
|
filter_rules: [],
|
||||||
|
})
|
||||||
|
.subscribe()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(`${environment.apiBaseUrl}${endpoint}/`)
|
||||||
|
.flush({})
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
reloadSpy.mockClear()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
|
||||||
|
)
|
||||||
|
.flush({
|
||||||
|
results: saved_views,
|
||||||
|
})
|
||||||
|
service.delete(saved_views[0]).subscribe()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(`${environment.apiBaseUrl}${endpoint}/1/`)
|
||||||
|
.flush({})
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
reloadSpy.mockClear()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
|
||||||
|
)
|
||||||
|
.flush({
|
||||||
|
results: saved_views,
|
||||||
|
})
|
||||||
|
service.patch(saved_views[0], true).subscribe()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(`${environment.apiBaseUrl}${endpoint}/1/`)
|
||||||
|
.flush({})
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
|
||||||
|
)
|
||||||
|
.flush({
|
||||||
|
results: saved_views,
|
||||||
|
})
|
||||||
|
service.patchMany(saved_views).subscribe()
|
||||||
|
saved_views.forEach((saved_view) => {
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/${saved_view.id}/`
|
||||||
|
)
|
||||||
|
req.flush({})
|
||||||
|
})
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
|
||||||
|
)
|
||||||
|
.flush({
|
||||||
|
results: saved_views,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Dont need to setup again
|
// Dont need to setup again
|
||||||
|
|
||||||
|
@ -602,7 +602,6 @@ export class SettingsService {
|
|||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.toastService.showError(errorMessage)
|
this.toastService.showError(errorMessage)
|
||||||
console.log(error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storeSettings()
|
this.storeSettings()
|
||||||
@ -614,7 +613,6 @@ export class SettingsService {
|
|||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.toastService.showError(errorMessage)
|
this.toastService.showError(errorMessage)
|
||||||
console.log(e)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -636,7 +634,6 @@ export class SettingsService {
|
|||||||
this.toastService.showError(
|
this.toastService.showError(
|
||||||
'Error migrating update checking setting'
|
'Error migrating update checking setting'
|
||||||
)
|
)
|
||||||
console.log(e)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ export const environment = {
|
|||||||
apiBaseUrl: document.baseURI + 'api/',
|
apiBaseUrl: document.baseURI + 'api/',
|
||||||
apiVersion: '7',
|
apiVersion: '7',
|
||||||
appTitle: 'Paperless-ngx',
|
appTitle: 'Paperless-ngx',
|
||||||
version: '2.14.7',
|
version: '2.15.0',
|
||||||
webSocketHost: window.location.host,
|
webSocketHost: window.location.host,
|
||||||
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
|
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
|
||||||
webSocketBaseUrl: base_url.pathname + 'ws/',
|
webSocketBaseUrl: base_url.pathname + 'ws/',
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
--pngx-bg-alt2: var(--bs-gray-200); // #e9ecef
|
--pngx-bg-alt2: var(--bs-gray-200); // #e9ecef
|
||||||
--pngx-bg-disabled: #f7f7f7;
|
--pngx-bg-disabled: #f7f7f7;
|
||||||
--pngx-focus-alpha: 0.3;
|
--pngx-focus-alpha: 0.3;
|
||||||
--pngx-toast-max-width: 360px;
|
--pngx-toast-max-width: 340px;
|
||||||
--bs-info: var(--pngx-bg-alt2);
|
--bs-info: var(--pngx-bg-alt2);
|
||||||
--bs-info-rgb: 233, 236, 239;
|
--bs-info-rgb: 233, 236, 239;
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
|
@ -870,7 +870,7 @@ class BasicUserSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class NotesSerializer(serializers.ModelSerializer):
|
class NotesSerializer(serializers.ModelSerializer):
|
||||||
user = BasicUserSerializer()
|
user = BasicUserSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Note
|
model = Note
|
||||||
@ -893,7 +893,7 @@ class DocumentSerializer(
|
|||||||
created_date = serializers.DateField(required=False)
|
created_date = serializers.DateField(required=False)
|
||||||
page_count = SerializerMethodField()
|
page_count = SerializerMethodField()
|
||||||
|
|
||||||
notes = NotesSerializer(many=True, required=False)
|
notes = NotesSerializer(many=True, required=False, read_only=True)
|
||||||
|
|
||||||
custom_fields = CustomFieldInstanceSerializer(
|
custom_fields = CustomFieldInstanceSerializer(
|
||||||
many=True,
|
many=True,
|
||||||
|
@ -784,10 +784,10 @@ def run_workflows(
|
|||||||
field=field,
|
field=field,
|
||||||
document=document,
|
document=document,
|
||||||
).first()
|
).first()
|
||||||
if instance:
|
if instance and args[value_field_name] is not None:
|
||||||
setattr(instance, value_field_name, args[value_field_name])
|
setattr(instance, value_field_name, args[value_field_name])
|
||||||
instance.save()
|
instance.save()
|
||||||
else:
|
elif not instance:
|
||||||
CustomFieldInstance.objects.create(
|
CustomFieldInstance.objects.create(
|
||||||
**args,
|
**args,
|
||||||
field=field,
|
field=field,
|
||||||
|
@ -211,7 +211,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
|
|||||||
def test_api_modify_tags_not_provided(self, m):
|
def test_api_modify_tags_not_provided(self, m):
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
- API data to modify tags is missing modify_tags field
|
- API data to modify tags is missing remove_tags field
|
||||||
WHEN:
|
WHEN:
|
||||||
- API to edit tags is called
|
- API to edit tags is called
|
||||||
THEN:
|
THEN:
|
||||||
|
@ -549,6 +549,9 @@ def _parse_remote_user_settings() -> str:
|
|||||||
|
|
||||||
HTTP_REMOTE_USER_HEADER_NAME = _parse_remote_user_settings()
|
HTTP_REMOTE_USER_HEADER_NAME = _parse_remote_user_settings()
|
||||||
|
|
||||||
|
# X-Frame options for embedded PDF display:
|
||||||
|
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||||
|
|
||||||
# The next 3 settings can also be set using just PAPERLESS_URL
|
# The next 3 settings can also be set using just PAPERLESS_URL
|
||||||
CSRF_TRUSTED_ORIGINS = __get_list("PAPERLESS_CSRF_TRUSTED_ORIGINS")
|
CSRF_TRUSTED_ORIGINS = __get_list("PAPERLESS_CSRF_TRUSTED_ORIGINS")
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
__version__: Final[tuple[int, int, int]] = (2, 14, 7)
|
__version__: Final[tuple[int, int, int]] = (2, 15, 0)
|
||||||
# Version string like X.Y.Z
|
# Version string like X.Y.Z
|
||||||
__full_version_str__: Final[str] = ".".join(map(str, __version__))
|
__full_version_str__: Final[str] = ".".join(map(str, __version__))
|
||||||
# Version string like X.Y
|
# Version string like X.Y
|
||||||
|
Loading…
x
Reference in New Issue
Block a user