mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Feature: option for auto-remove inbox tags on save (#5562)
This commit is contained in:
		@@ -1967,7 +1967,7 @@
 | 
				
			|||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">733</context>
 | 
					          <context context-type="linenumber">735</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
				
			||||||
@@ -2006,7 +2006,7 @@
 | 
				
			|||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">735</context>
 | 
					          <context context-type="linenumber">737</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
				
			||||||
@@ -4856,78 +4856,78 @@
 | 
				
			|||||||
        <source>An error occurred loading content: <x id="PH" equiv-text="err.message ?? err.toString()"/></source>
 | 
					        <source>An error occurred loading content: <x id="PH" equiv-text="err.message ?? err.toString()"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">298,300</context>
 | 
					          <context context-type="linenumber">300,302</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="3200733026060976258" datatype="html">
 | 
					      <trans-unit id="3200733026060976258" datatype="html">
 | 
				
			||||||
        <source>Document changes detected</source>
 | 
					        <source>Document changes detected</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">321</context>
 | 
					          <context context-type="linenumber">323</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="2887155916749964" datatype="html">
 | 
					      <trans-unit id="2887155916749964" datatype="html">
 | 
				
			||||||
        <source>The version of this document in your browser session appears older than the existing version.</source>
 | 
					        <source>The version of this document in your browser session appears older than the existing version.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">322</context>
 | 
					          <context context-type="linenumber">324</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="237142428785956348" datatype="html">
 | 
					      <trans-unit id="237142428785956348" datatype="html">
 | 
				
			||||||
        <source>Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document.</source>
 | 
					        <source>Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">323</context>
 | 
					          <context context-type="linenumber">325</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8720977247725652816" datatype="html">
 | 
					      <trans-unit id="8720977247725652816" datatype="html">
 | 
				
			||||||
        <source>Ok</source>
 | 
					        <source>Ok</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">325</context>
 | 
					          <context context-type="linenumber">327</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="5758784066858623886" datatype="html">
 | 
					      <trans-unit id="5758784066858623886" datatype="html">
 | 
				
			||||||
        <source>Error retrieving metadata</source>
 | 
					        <source>Error retrieving metadata</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">465</context>
 | 
					          <context context-type="linenumber">467</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="3456881259945295697" datatype="html">
 | 
					      <trans-unit id="3456881259945295697" datatype="html">
 | 
				
			||||||
        <source>Error retrieving suggestions.</source>
 | 
					        <source>Error retrieving suggestions.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">490</context>
 | 
					          <context context-type="linenumber">492</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8348337312757497317" datatype="html">
 | 
					      <trans-unit id="8348337312757497317" datatype="html">
 | 
				
			||||||
        <source>Document saved successfully.</source>
 | 
					        <source>Document saved successfully.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">608</context>
 | 
					          <context context-type="linenumber">610</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">617</context>
 | 
					          <context context-type="linenumber">619</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="448882439049417053" datatype="html">
 | 
					      <trans-unit id="448882439049417053" datatype="html">
 | 
				
			||||||
        <source>Error saving document</source>
 | 
					        <source>Error saving document</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">621</context>
 | 
					          <context context-type="linenumber">623</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">662</context>
 | 
					          <context context-type="linenumber">664</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="9021887951960049161" datatype="html">
 | 
					      <trans-unit id="9021887951960049161" datatype="html">
 | 
				
			||||||
        <source>Confirm delete</source>
 | 
					        <source>Confirm delete</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">688</context>
 | 
					          <context context-type="linenumber">690</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
 | 
				
			||||||
@@ -4938,35 +4938,35 @@
 | 
				
			|||||||
        <source>Do you really want to delete document "<x id="PH" equiv-text="this.document.title"/>"?</source>
 | 
					        <source>Do you really want to delete document "<x id="PH" equiv-text="this.document.title"/>"?</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">689</context>
 | 
					          <context context-type="linenumber">691</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="6691075929777935948" datatype="html">
 | 
					      <trans-unit id="6691075929777935948" datatype="html">
 | 
				
			||||||
        <source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
 | 
					        <source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">690</context>
 | 
					          <context context-type="linenumber">692</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="719892092227206532" datatype="html">
 | 
					      <trans-unit id="719892092227206532" datatype="html">
 | 
				
			||||||
        <source>Delete document</source>
 | 
					        <source>Delete document</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">692</context>
 | 
					          <context context-type="linenumber">694</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="7295637485862454066" datatype="html">
 | 
					      <trans-unit id="7295637485862454066" datatype="html">
 | 
				
			||||||
        <source>Error deleting document</source>
 | 
					        <source>Error deleting document</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">711</context>
 | 
					          <context context-type="linenumber">713</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="7362691899087997122" datatype="html">
 | 
					      <trans-unit id="7362691899087997122" datatype="html">
 | 
				
			||||||
        <source>Redo OCR confirm</source>
 | 
					        <source>Redo OCR confirm</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">731</context>
 | 
					          <context context-type="linenumber">733</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
 | 
				
			||||||
@@ -4977,28 +4977,28 @@
 | 
				
			|||||||
        <source>This operation will permanently redo OCR for this document.</source>
 | 
					        <source>This operation will permanently redo OCR for this document.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">732</context>
 | 
					          <context context-type="linenumber">734</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="5729001209753056399" datatype="html">
 | 
					      <trans-unit id="5729001209753056399" datatype="html">
 | 
				
			||||||
        <source>Redo OCR operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
 | 
					        <source>Redo OCR operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">743</context>
 | 
					          <context context-type="linenumber">745</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="4409560272830824468" datatype="html">
 | 
					      <trans-unit id="4409560272830824468" datatype="html">
 | 
				
			||||||
        <source>Error executing operation</source>
 | 
					        <source>Error executing operation</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">754</context>
 | 
					          <context context-type="linenumber">756</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="4458954481601077369" datatype="html">
 | 
					      <trans-unit id="4458954481601077369" datatype="html">
 | 
				
			||||||
        <source>Page Fit</source>
 | 
					        <source>Page Fit</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">823</context>
 | 
					          <context context-type="linenumber">825</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="6857598786757174736" datatype="html">
 | 
					      <trans-unit id="6857598786757174736" datatype="html">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -158,6 +158,14 @@
 | 
				
			|||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <h4 class="mt-4" i18n>Document editing</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="row mb-3">
 | 
				
			||||||
 | 
					          <div class="offset-md-3 col">
 | 
				
			||||||
 | 
					            <pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <h4 class="mt-4" i18n>Bulk editing</h4>
 | 
					        <h4 class="mt-4" i18n>Bulk editing</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="row mb-3">
 | 
					        <div class="row mb-3">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -289,7 +289,7 @@ describe('SettingsComponent', () => {
 | 
				
			|||||||
    expect(toastErrorSpy).toHaveBeenCalled()
 | 
					    expect(toastErrorSpy).toHaveBeenCalled()
 | 
				
			||||||
    expect(storeSpy).toHaveBeenCalled()
 | 
					    expect(storeSpy).toHaveBeenCalled()
 | 
				
			||||||
    expect(appearanceSettingsSpy).not.toHaveBeenCalled()
 | 
					    expect(appearanceSettingsSpy).not.toHaveBeenCalled()
 | 
				
			||||||
    expect(setSpy).toHaveBeenCalledTimes(24)
 | 
					    expect(setSpy).toHaveBeenCalledTimes(25)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // succeed
 | 
					    // succeed
 | 
				
			||||||
    storeSpy.mockReturnValueOnce(of(true))
 | 
					    storeSpy.mockReturnValueOnce(of(true))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,6 +88,7 @@ export class SettingsComponent
 | 
				
			|||||||
    defaultPermsViewGroups: new FormControl(null),
 | 
					    defaultPermsViewGroups: new FormControl(null),
 | 
				
			||||||
    defaultPermsEditUsers: new FormControl(null),
 | 
					    defaultPermsEditUsers: new FormControl(null),
 | 
				
			||||||
    defaultPermsEditGroups: new FormControl(null),
 | 
					    defaultPermsEditGroups: new FormControl(null),
 | 
				
			||||||
 | 
					    documentEditingRemoveInboxTags: new FormControl(null),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    notificationsConsumerNewDocument: new FormControl(null),
 | 
					    notificationsConsumerNewDocument: new FormControl(null),
 | 
				
			||||||
    notificationsConsumerSuccess: new FormControl(null),
 | 
					    notificationsConsumerSuccess: new FormControl(null),
 | 
				
			||||||
@@ -271,6 +272,9 @@ export class SettingsComponent
 | 
				
			|||||||
      defaultPermsEditGroups: this.settings.get(
 | 
					      defaultPermsEditGroups: this.settings.get(
 | 
				
			||||||
        SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS
 | 
					        SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 | 
					      documentEditingRemoveInboxTags: this.settings.get(
 | 
				
			||||||
 | 
					        SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
      savedViews: {},
 | 
					      savedViews: {},
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -484,6 +488,10 @@ export class SettingsComponent
 | 
				
			|||||||
      SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS,
 | 
					      SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS,
 | 
				
			||||||
      this.settingsForm.value.defaultPermsEditGroups
 | 
					      this.settingsForm.value.defaultPermsEditGroups
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS,
 | 
				
			||||||
 | 
					      this.settingsForm.value.documentEditingRemoveInboxTags
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    this.settings.setLanguage(this.settingsForm.value.displayLanguage)
 | 
					    this.settings.setLanguage(this.settingsForm.value.displayLanguage)
 | 
				
			||||||
    this.settings
 | 
					    this.settings
 | 
				
			||||||
      .storeSettings()
 | 
					      .storeSettings()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -630,7 +630,9 @@ export class DocumentDetailComponent
 | 
				
			|||||||
      .update(this.document)
 | 
					      .update(this.document)
 | 
				
			||||||
      .pipe(first())
 | 
					      .pipe(first())
 | 
				
			||||||
      .subscribe({
 | 
					      .subscribe({
 | 
				
			||||||
        next: () => {
 | 
					        next: (docValues) => {
 | 
				
			||||||
 | 
					          // in case data changed while saving eg removing inbox_tags
 | 
				
			||||||
 | 
					          this.documentForm.patchValue(docValues)
 | 
				
			||||||
          this.store.next(this.documentForm.value)
 | 
					          this.store.next(this.documentForm.value)
 | 
				
			||||||
          this.toastService.showInfo($localize`Document saved successfully.`)
 | 
					          this.toastService.showInfo($localize`Document saved successfully.`)
 | 
				
			||||||
          close && this.close()
 | 
					          close && this.close()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,4 +63,7 @@ export interface Document extends ObjectWithPermissions {
 | 
				
			|||||||
  __search_hit__?: SearchHit
 | 
					  __search_hit__?: SearchHit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  custom_fields?: CustomFieldInstance[]
 | 
					  custom_fields?: CustomFieldInstance[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // write-only field
 | 
				
			||||||
 | 
					  remove_inbox_tags?: boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,8 @@ export const SETTINGS_KEYS = {
 | 
				
			|||||||
  DEFAULT_PERMS_VIEW_GROUPS: 'general-settings:permissions:default-view-groups',
 | 
					  DEFAULT_PERMS_VIEW_GROUPS: 'general-settings:permissions:default-view-groups',
 | 
				
			||||||
  DEFAULT_PERMS_EDIT_USERS: 'general-settings:permissions:default-edit-users',
 | 
					  DEFAULT_PERMS_EDIT_USERS: 'general-settings:permissions:default-edit-users',
 | 
				
			||||||
  DEFAULT_PERMS_EDIT_GROUPS: 'general-settings:permissions:default-edit-groups',
 | 
					  DEFAULT_PERMS_EDIT_GROUPS: 'general-settings:permissions:default-edit-groups',
 | 
				
			||||||
 | 
					  DOCUMENT_EDITING_REMOVE_INBOX_TAGS:
 | 
				
			||||||
 | 
					    'general-settings:document-editing:remove-inbox-tags',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SETTINGS: UiSetting[] = [
 | 
					export const SETTINGS: UiSetting[] = [
 | 
				
			||||||
@@ -206,4 +208,9 @@ export const SETTINGS: UiSetting[] = [
 | 
				
			|||||||
    type: 'string',
 | 
					    type: 'string',
 | 
				
			||||||
    default: '',
 | 
					    default: '',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    key: SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS,
 | 
				
			||||||
 | 
					    type: 'boolean',
 | 
				
			||||||
 | 
					    default: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,13 @@ import { TestBed } from '@angular/core/testing'
 | 
				
			|||||||
import { environment } from 'src/environments/environment'
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
import { DocumentService } from './document.service'
 | 
					import { DocumentService } from './document.service'
 | 
				
			||||||
import { FILTER_TITLE } from 'src/app/data/filter-rule-type'
 | 
					import { FILTER_TITLE } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
 | 
					import { SettingsService } from '../settings.service'
 | 
				
			||||||
 | 
					import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let httpTestingController: HttpTestingController
 | 
					let httpTestingController: HttpTestingController
 | 
				
			||||||
let service: DocumentService
 | 
					let service: DocumentService
 | 
				
			||||||
let subscription: Subscription
 | 
					let subscription: Subscription
 | 
				
			||||||
 | 
					let settingsService: SettingsService
 | 
				
			||||||
const endpoint = 'documents'
 | 
					const endpoint = 'documents'
 | 
				
			||||||
const documents = [
 | 
					const documents = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -34,6 +37,17 @@ const documents = [
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					beforeEach(() => {
 | 
				
			||||||
 | 
					  TestBed.configureTestingModule({
 | 
				
			||||||
 | 
					    providers: [DocumentService],
 | 
				
			||||||
 | 
					    imports: [HttpClientTestingModule],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  httpTestingController = TestBed.inject(HttpTestingController)
 | 
				
			||||||
 | 
					  service = TestBed.inject(DocumentService)
 | 
				
			||||||
 | 
					  settingsService = TestBed.inject(SettingsService)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe(`DocumentService`, () => {
 | 
					describe(`DocumentService`, () => {
 | 
				
			||||||
  // common tests e.g. commonAbstractPaperlessServiceTests differ slightly
 | 
					  // common tests e.g. commonAbstractPaperlessServiceTests differ slightly
 | 
				
			||||||
  it('should call appropriate api endpoint for list all', () => {
 | 
					  it('should call appropriate api endpoint for list all', () => {
 | 
				
			||||||
@@ -237,16 +251,21 @@ describe(`DocumentService`, () => {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    expect(req.request.method).toEqual('GET')
 | 
					    expect(req.request.method).toEqual('GET')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
beforeEach(() => {
 | 
					  it('should pass remove_inbox_tags setting to update', () => {
 | 
				
			||||||
  TestBed.configureTestingModule({
 | 
					    subscription = service.update(documents[0]).subscribe()
 | 
				
			||||||
    providers: [DocumentService],
 | 
					    let req = httpTestingController.expectOne(
 | 
				
			||||||
    imports: [HttpClientTestingModule],
 | 
					      `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    expect(req.request.body.remove_inbox_tags).toEqual(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true)
 | 
				
			||||||
 | 
					    subscription = service.update(documents[0]).subscribe()
 | 
				
			||||||
 | 
					    req = httpTestingController.expectOne(
 | 
				
			||||||
 | 
					      `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    expect(req.request.body.remove_inbox_tags).toEqual(true)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					 | 
				
			||||||
  httpTestingController = TestBed.inject(HttpTestingController)
 | 
					 | 
				
			||||||
  service = TestBed.inject(DocumentService)
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
afterEach(() => {
 | 
					afterEach(() => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
 | 
				
			|||||||
import { Document } from 'src/app/data/document'
 | 
					import { Document } from 'src/app/data/document'
 | 
				
			||||||
import { DocumentMetadata } from 'src/app/data/document-metadata'
 | 
					import { DocumentMetadata } from 'src/app/data/document-metadata'
 | 
				
			||||||
import { AbstractPaperlessService } from './abstract-paperless-service'
 | 
					import { AbstractPaperlessService } from './abstract-paperless-service'
 | 
				
			||||||
import { HttpClient, HttpParams } from '@angular/common/http'
 | 
					import { HttpClient } from '@angular/common/http'
 | 
				
			||||||
import { Observable } from 'rxjs'
 | 
					import { Observable } from 'rxjs'
 | 
				
			||||||
import { Results } from 'src/app/data/results'
 | 
					import { Results } from 'src/app/data/results'
 | 
				
			||||||
import { FilterRule } from 'src/app/data/filter-rule'
 | 
					import { FilterRule } from 'src/app/data/filter-rule'
 | 
				
			||||||
@@ -18,6 +18,8 @@ import {
 | 
				
			|||||||
  PermissionType,
 | 
					  PermissionType,
 | 
				
			||||||
  PermissionsService,
 | 
					  PermissionsService,
 | 
				
			||||||
} from '../permissions.service'
 | 
					} from '../permissions.service'
 | 
				
			||||||
 | 
					import { SettingsService } from '../settings.service'
 | 
				
			||||||
 | 
					import { SETTINGS, SETTINGS_KEYS } from 'src/app/data/ui-settings'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DOCUMENT_SORT_FIELDS = [
 | 
					export const DOCUMENT_SORT_FIELDS = [
 | 
				
			||||||
  { field: 'archive_serial_number', name: $localize`ASN` },
 | 
					  { field: 'archive_serial_number', name: $localize`ASN` },
 | 
				
			||||||
@@ -63,7 +65,8 @@ export class DocumentService extends AbstractPaperlessService<Document> {
 | 
				
			|||||||
    private documentTypeService: DocumentTypeService,
 | 
					    private documentTypeService: DocumentTypeService,
 | 
				
			||||||
    private tagService: TagService,
 | 
					    private tagService: TagService,
 | 
				
			||||||
    private storagePathService: StoragePathService,
 | 
					    private storagePathService: StoragePathService,
 | 
				
			||||||
    private permissionsService: PermissionsService
 | 
					    private permissionsService: PermissionsService,
 | 
				
			||||||
 | 
					    private settingsService: SettingsService
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super(http, 'documents')
 | 
					    super(http, 'documents')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -180,6 +183,9 @@ export class DocumentService extends AbstractPaperlessService<Document> {
 | 
				
			|||||||
  update(o: Document): Observable<Document> {
 | 
					  update(o: Document): Observable<Document> {
 | 
				
			||||||
    // we want to only set created_date
 | 
					    // we want to only set created_date
 | 
				
			||||||
    o.created = undefined
 | 
					    o.created = undefined
 | 
				
			||||||
 | 
					    o.remove_inbox_tags = this.settingsService.get(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    return super.update(o)
 | 
					    return super.update(o)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -638,6 +638,11 @@ class DocumentSerializer(
 | 
				
			|||||||
        allow_null=True,
 | 
					        allow_null=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    remove_inbox_tags = serializers.BooleanField(
 | 
				
			||||||
 | 
					        default=False,
 | 
				
			||||||
 | 
					        write_only=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_original_file_name(self, obj):
 | 
					    def get_original_file_name(self, obj):
 | 
				
			||||||
        return obj.original_filename
 | 
					        return obj.original_filename
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -681,12 +686,48 @@ class DocumentSerializer(
 | 
				
			|||||||
                            custom_field_instance.field,
 | 
					                            custom_field_instance.field,
 | 
				
			||||||
                            doc_id,
 | 
					                            doc_id,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            "remove_inbox_tags" in validated_data
 | 
				
			||||||
 | 
					            and validated_data["remove_inbox_tags"]
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            tag_ids_being_added = (
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    tag.id
 | 
				
			||||||
 | 
					                    for tag in validated_data["tags"]
 | 
				
			||||||
 | 
					                    if tag not in instance.tags.all()
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					                if "tags" in validated_data
 | 
				
			||||||
 | 
					                else []
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            inbox_tags_not_being_added = Tag.objects.filter(is_inbox_tag=True).exclude(
 | 
				
			||||||
 | 
					                id__in=tag_ids_being_added,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            if "tags" in validated_data:
 | 
				
			||||||
 | 
					                validated_data["tags"] = [
 | 
				
			||||||
 | 
					                    tag
 | 
				
			||||||
 | 
					                    for tag in validated_data["tags"]
 | 
				
			||||||
 | 
					                    if tag not in inbox_tags_not_being_added
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                validated_data["tags"] = [
 | 
				
			||||||
 | 
					                    tag
 | 
				
			||||||
 | 
					                    for tag in instance.tags.all()
 | 
				
			||||||
 | 
					                    if tag not in inbox_tags_not_being_added
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
        super().update(instance, validated_data)
 | 
					        super().update(instance, validated_data)
 | 
				
			||||||
        return instance
 | 
					        return instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
        self.truncate_content = kwargs.pop("truncate_content", False)
 | 
					        self.truncate_content = kwargs.pop("truncate_content", False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # return full permissions if we're doing a PATCH or PUT
 | 
				
			||||||
 | 
					        context = kwargs.get("context")
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            context.get("request").method == "PATCH"
 | 
				
			||||||
 | 
					            or context.get("request").method == "PUT"
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            kwargs["full_perms"] = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
@@ -714,6 +755,7 @@ class DocumentSerializer(
 | 
				
			|||||||
            "set_permissions",
 | 
					            "set_permissions",
 | 
				
			||||||
            "notes",
 | 
					            "notes",
 | 
				
			||||||
            "custom_fields",
 | 
					            "custom_fields",
 | 
				
			||||||
 | 
					            "remove_inbox_tags",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2080,6 +2080,72 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
 | 
				
			|||||||
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
					        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
				
			||||||
        self.assertEqual(resp.content, b"1")
 | 
					        self.assertEqual(resp.content, b"1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_remove_inbox_tags(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        GIVEN:
 | 
				
			||||||
 | 
					            - Existing document with or without inbox tags
 | 
				
			||||||
 | 
					        WHEN:
 | 
				
			||||||
 | 
					            - API request to update document, with or without `remove_inbox_tags` flag
 | 
				
			||||||
 | 
					        THEN:
 | 
				
			||||||
 | 
					            - Inbox tags are removed as long as they are not being added
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        tag1 = Tag.objects.create(name="tag1", color="#abcdef")
 | 
				
			||||||
 | 
					        inbox_tag1 = Tag.objects.create(
 | 
				
			||||||
 | 
					            name="inbox1",
 | 
				
			||||||
 | 
					            color="#abcdef",
 | 
				
			||||||
 | 
					            is_inbox_tag=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        inbox_tag2 = Tag.objects.create(
 | 
				
			||||||
 | 
					            name="inbox2",
 | 
				
			||||||
 | 
					            color="#abcdef",
 | 
				
			||||||
 | 
					            is_inbox_tag=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        doc1 = Document.objects.create(
 | 
				
			||||||
 | 
					            title="test",
 | 
				
			||||||
 | 
					            mime_type="application/pdf",
 | 
				
			||||||
 | 
					            content="this is a document 1",
 | 
				
			||||||
 | 
					            checksum="1",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        doc1.tags.add(tag1)
 | 
				
			||||||
 | 
					        doc1.tags.add(inbox_tag1)
 | 
				
			||||||
 | 
					        doc1.tags.add(inbox_tag2)
 | 
				
			||||||
 | 
					        doc1.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove inbox tags defaults to false
 | 
				
			||||||
 | 
					        resp = self.client.patch(
 | 
				
			||||||
 | 
					            f"/api/documents/{doc1.pk}/",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "title": "New title",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        doc1.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					        self.assertEqual(doc1.tags.count(), 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove inbox tags set to true
 | 
				
			||||||
 | 
					        resp = self.client.patch(
 | 
				
			||||||
 | 
					            f"/api/documents/{doc1.pk}/",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "remove_inbox_tags": True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        doc1.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					        self.assertEqual(doc1.tags.count(), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove inbox tags set to true but adding a new inbox tag
 | 
				
			||||||
 | 
					        resp = self.client.patch(
 | 
				
			||||||
 | 
					            f"/api/documents/{doc1.pk}/",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "remove_inbox_tags": True,
 | 
				
			||||||
 | 
					                "tags": [inbox_tag1.pk, tag1.pk],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        doc1.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(resp.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					        self.assertEqual(doc1.tags.count(), 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestDocumentApiV2(DirectoriesMixin, APITestCase):
 | 
					class TestDocumentApiV2(DirectoriesMixin, APITestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user