mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Merge branch 'dev' into feature-remote-ocr-2
This commit is contained in:
		@@ -2,9 +2,11 @@
 | 
			
		||||
 | 
			
		||||
If you feel like contributing to the project, please do! Bug fixes and improvements are always welcome.
 | 
			
		||||
 | 
			
		||||
⚠️ Please note: Pull requests that implement a new feature or enhancement _should almost always target an existing feature request_ with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. Pull requests that are opened without meeting this requirement may not be merged.
 | 
			
		||||
 | 
			
		||||
If you want to implement something big:
 | 
			
		||||
 | 
			
		||||
- Please start a discussion about that in the issues! Maybe something similar is already in development and we can make it happen together.
 | 
			
		||||
- As above, please start with a discussion! Maybe something similar is already in development and we can make it happen together.
 | 
			
		||||
- When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
 | 
			
		||||
- Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
 | 
			
		||||
- Please see the [paperless-ngx merge process](#merging-prs) below.
 | 
			
		||||
 
 | 
			
		||||
@@ -1759,6 +1759,11 @@ started by the container.
 | 
			
		||||
 | 
			
		||||
: Path to an image file in the /media/logo directory, must include 'logo', e.g. `/logo/Atari_logo.svg`
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    The logo file will be viewable by anyone with access to the Paperless instance login page,
 | 
			
		||||
    so consider your choice of logo carefully and removing exif data from images before uploading.
 | 
			
		||||
 | 
			
		||||
#### [`PAPERLESS_ENABLE_UPDATE_CHECK=<bool>`](#PAPERLESS_ENABLE_UPDATE_CHECK) {#PAPERLESS_ENABLE_UPDATE_CHECK}
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 
 | 
			
		||||
@@ -71,4 +71,20 @@ describe('TagListComponent', () => {
 | 
			
		||||
      'Do you really want to delete the tag "Tag1"?'
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('should filter out child tags if name filter is empty, otherwise show all', () => {
 | 
			
		||||
    const tags = [
 | 
			
		||||
      { id: 1, name: 'Tag1', parent: null },
 | 
			
		||||
      { id: 2, name: 'Tag2', parent: 1 },
 | 
			
		||||
      { id: 3, name: 'Tag3', parent: null },
 | 
			
		||||
    ]
 | 
			
		||||
    component['_nameFilter'] = null // Simulate empty name filter
 | 
			
		||||
    const filtered = component.filterData(tags as any)
 | 
			
		||||
    expect(filtered.length).toBe(2)
 | 
			
		||||
    expect(filtered.find((t) => t.id === 2)).toBeUndefined()
 | 
			
		||||
 | 
			
		||||
    component['_nameFilter'] = 'Tag2' // Simulate non-empty name filter
 | 
			
		||||
    const filteredWithName = component.filterData(tags as any)
 | 
			
		||||
    expect(filteredWithName.length).toBe(3)
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,8 @@ export class TagListComponent extends ManagementListComponent<Tag> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  filterData(data: Tag[]) {
 | 
			
		||||
    return data.filter((tag) => !tag.parent)
 | 
			
		||||
    return this.nameFilter?.length
 | 
			
		||||
      ? [...data]
 | 
			
		||||
      : data.filter((tag) => !tag.parent)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -82,6 +82,13 @@ def _is_ignored(filepath: Path) -> bool:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _consume(filepath: Path) -> None:
 | 
			
		||||
    # Check permissions early
 | 
			
		||||
    try:
 | 
			
		||||
        filepath.stat()
 | 
			
		||||
    except (PermissionError, OSError):
 | 
			
		||||
        logger.warning(f"Not consuming file {filepath}: Permission denied.")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if filepath.is_dir() or _is_ignored(filepath):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
@@ -323,7 +330,12 @@ class Command(BaseCommand):
 | 
			
		||||
 | 
			
		||||
                        # Also make sure the file exists still, some scanners might write a
 | 
			
		||||
                        # temporary file first
 | 
			
		||||
                        file_still_exists = filepath.exists() and filepath.is_file()
 | 
			
		||||
                        try:
 | 
			
		||||
                            file_still_exists = filepath.exists() and filepath.is_file()
 | 
			
		||||
                        except (PermissionError, OSError):  # pragma: no cover
 | 
			
		||||
                            # If we can't check, let it fail in the _consume function
 | 
			
		||||
                            file_still_exists = True
 | 
			
		||||
                            continue
 | 
			
		||||
 | 
			
		||||
                        if waited_long_enough and file_still_exists:
 | 
			
		||||
                            _consume(filepath)
 | 
			
		||||
 
 | 
			
		||||
@@ -209,6 +209,26 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
 | 
			
		||||
        # assert that we have an error logged with this invalid file.
 | 
			
		||||
        error_logger.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    @mock.patch("documents.management.commands.document_consumer.logger.warning")
 | 
			
		||||
    def test_permission_error_on_prechecks(self, warning_logger):
 | 
			
		||||
        filepath = Path(self.dirs.consumption_dir) / "selinux.txt"
 | 
			
		||||
        filepath.touch()
 | 
			
		||||
 | 
			
		||||
        original_stat = Path.stat
 | 
			
		||||
 | 
			
		||||
        def raising_stat(self, *args, **kwargs):
 | 
			
		||||
            if self == filepath:
 | 
			
		||||
                raise PermissionError("Permission denied")
 | 
			
		||||
            return original_stat(self, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        with mock.patch("pathlib.Path.stat", new=raising_stat):
 | 
			
		||||
            document_consumer._consume(filepath)
 | 
			
		||||
 | 
			
		||||
        warning_logger.assert_called_once()
 | 
			
		||||
        (args, _) = warning_logger.call_args
 | 
			
		||||
        self.assertIn("Permission denied", args[0])
 | 
			
		||||
        self.consume_file_mock.assert_not_called()
 | 
			
		||||
 | 
			
		||||
    @override_settings(CONSUMPTION_DIR="does_not_exist")
 | 
			
		||||
    def test_consumption_directory_invalid(self):
 | 
			
		||||
        self.assertRaises(CommandError, call_command, "document_consumer", "--oneshot")
 | 
			
		||||
 
 | 
			
		||||
@@ -923,7 +923,7 @@ CELERY_ACCEPT_CONTENT = ["application/json", "application/x-python-serialize"]
 | 
			
		||||
CELERY_BEAT_SCHEDULE = _parse_beat_schedule()
 | 
			
		||||
 | 
			
		||||
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-schedule-filename
 | 
			
		||||
CELERY_BEAT_SCHEDULE_FILENAME = DATA_DIR / "celerybeat-schedule.db"
 | 
			
		||||
CELERY_BEAT_SCHEDULE_FILENAME = str(DATA_DIR / "celerybeat-schedule.db")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Cachalot: Database read cache.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user