]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Enhancement: bulk delete objects (#5688)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Thu, 8 Feb 2024 18:13:15 +0000 (10:13 -0800)
committerGitHub <noreply@github.com>
Thu, 8 Feb 2024 18:13:15 +0000 (10:13 -0800)
15 files changed:
docs/api.md
src-ui/messages.xlf
src-ui/src/app/components/manage/management-list/management-list.component.html
src-ui/src/app/components/manage/management-list/management-list.component.spec.ts
src-ui/src/app/components/manage/management-list/management-list.component.ts
src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts
src-ui/src/app/services/rest/abstract-name-filter-service.ts
src-ui/src/environments/environment.prod.ts
src-ui/src/environments/environment.ts
src/documents/serialisers.py
src/documents/tests/test_api_objects.py
src/documents/tests/test_api_permissions.py
src/documents/views.py
src/paperless/settings.py
src/paperless/urls.py

index bd5154adaccf84e1d9508d1ab9b7f56c201ce3a2..fa32bf2aa0c1e2fdf5032a3580a635119ea39f5a 100644 (file)
@@ -375,14 +375,15 @@ The following methods are supported:
 
 ### Objects
 
-Bulk editing for objects (tags, document types etc.) currently supports only updating permissions, using
-the endpoint: `/api/bulk_edit_object_perms/` which requires a json payload of the format:
+Bulk editing for objects (tags, document types etc.) currently supports set permissions or delete
+operations, using the endpoint: `/api/bulk_edit_objects/`, which requires a json payload of the format:
 
 ```json
 {
   "objects": [LIST_OF_OBJECT_IDS],
-  "object_type": "tags", "correspondents", "document_types" or "storage_paths"
-  "owner": OWNER_ID // optional
+  "object_type": "tags", "correspondents", "document_types" or "storage_paths",
+  "operation": "set_permissions" or "delete",
+  "owner": OWNER_ID, // optional
   "permissions": { "view": { "users": [] ... }, "change": { ... } }, // (see 'set_permissions' format above)
   "merge": true / false // defaults to false, see above
 }
index f2b356d9af0d42ef620f52ccf33d3794f11f0a58..a959d9fb2257253bcaa3f7e380a85aaab157ff2a 100644 (file)
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">51</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">51</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">51</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">51</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4930506384627295710" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">78</context>
+          <context context-type="linenumber">82</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">20</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">20</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">20</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">20</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">37</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">37</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">37</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">37</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">84</context>
+          <context context-type="linenumber">88</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">136</context>
+          <context context-type="linenumber">140</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">9</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">9</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">9</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">9</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">93</context>
+          <context context-type="linenumber">84</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">93</context>
+          <context context-type="linenumber">84</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">93</context>
+          <context context-type="linenumber">84</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">93</context>
+          <context context-type="linenumber">84</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">96</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">96</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">96</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">96</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">205</context>
+          <context context-type="linenumber">208</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">83</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">83</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">83</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">83</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">489</context>
+          <context context-type="linenumber">580</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">528</context>
+          <context context-type="linenumber">619</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
           <context context-type="linenumber">173</context>
         </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">320</context>
+        </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
           <context context-type="linenumber">97</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">530</context>
+          <context context-type="linenumber">621</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
           <context context-type="linenumber">175</context>
         </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">322</context>
+        </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
           <context context-type="linenumber">99</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">87</context>
+          <context context-type="linenumber">90</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">87</context>
+          <context context-type="linenumber">90</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">87</context>
+          <context context-type="linenumber">90</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">87</context>
+          <context context-type="linenumber">90</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6988090220128974198" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">351</context>
+          <context context-type="linenumber">356</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">391</context>
+          <context context-type="linenumber">396</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">429</context>
+          <context context-type="linenumber">434</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">467</context>
+          <context context-type="linenumber">472</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2159130950882492111" datatype="html">
           <context context-type="linenumber">27</context>
         </context-group>
       </trans-unit>
+      <trans-unit id="5674286808255988565" datatype="html">
+        <source>Create</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
+          <context context-type="linenumber">50</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
+          <context context-type="linenumber">64</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">12</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">12</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">12</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
+          <context context-type="linenumber">12</context>
+        </context-group>
+      </trans-unit>
       <trans-unit id="4391289919356861627" datatype="html">
         <source>Apply</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
-          <context context-type="linenumber">49</context>
+          <context context-type="linenumber">56</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7780041345210191160" datatype="html">
         <source>Click again to exclude items.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
-          <context context-type="linenumber">55</context>
+          <context context-type="linenumber">63</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7593728289020204896" datatype="html">
           <context context-type="linenumber">51</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="5674286808255988565" datatype="html">
-        <source>Create</source>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
-          <context context-type="linenumber">64</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">9</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">9</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">9</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">9</context>
-        </context-group>
-      </trans-unit>
       <trans-unit id="4776429682428363094" datatype="html">
         <source>1 day</source>
         <context-group purpose="location">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">36</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">102</context>
+          <context context-type="linenumber">106</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">296</context>
+          <context context-type="linenumber">301</context>
         </context-group>
         <note priority="1" from="description">this string is used to separate processing, failed and added on the file upload widget</note>
       </trans-unit>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">87</context>
+          <context context-type="linenumber">91</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1418444397960583910" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">50</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">61</context>
+          <context context-type="linenumber">64</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">201</context>
+          <context context-type="linenumber">204</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">318</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5382975254277698192" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">526</context>
+          <context context-type="linenumber">617</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9197453786953646058" datatype="html">
         <source>Filter correspondents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">37</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
         <source>Filter document types</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">49</context>
+          <context context-type="linenumber">51</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
         <source>Filter storage paths</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">62</context>
+          <context context-type="linenumber">65</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
         <source>Include:</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">108</context>
+          <context context-type="linenumber">112</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1208547554603365604" datatype="html">
         <source> Archived files </source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">112,114</context>
+          <context context-type="linenumber">116,118</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6791570188945688785" datatype="html">
         <source> Original files </source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">118,120</context>
+          <context context-type="linenumber">122,124</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3608345051493493574" datatype="html">
         <source> Use formatted filename </source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
-          <context context-type="linenumber">125,127</context>
+          <context context-type="linenumber">129,131</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1215215387232313677" datatype="html">
         <source>Error executing bulk operation</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">213</context>
+          <context context-type="linenumber">218</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7894972847287473517" datatype="html">
         <source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot;</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">288</context>
+          <context context-type="linenumber">293</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">294</context>
+          <context context-type="linenumber">299</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8639884465898458690" datatype="html">
         <source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot; and &quot;<x id="PH_1" equiv-text="items[1].name"/>&quot;</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">290</context>
+          <context context-type="linenumber">295</context>
         </context-group>
         <note priority="1" from="description">This is for messages like &apos;modify &quot;tag1&quot; and &quot;tag2&quot;&apos;</note>
       </trans-unit>
         <source><x id="PH" equiv-text="list"/> and &quot;<x id="PH_1" equiv-text="items[items.length - 1].name"/>&quot;</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">298,300</context>
+          <context context-type="linenumber">303,305</context>
         </context-group>
         <note priority="1" from="description">this is for messages like &apos;modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;&apos;</note>
       </trans-unit>
         <source>Confirm tags assignment</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">315</context>
+          <context context-type="linenumber">320</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6619516195038467207" datatype="html">
         <source>This operation will add the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">321</context>
+          <context context-type="linenumber">326</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1894412783609570695" datatype="html">
         )"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">326,328</context>
+          <context context-type="linenumber">331,333</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7181166515756808573" datatype="html">
         <source>This operation will remove the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">334</context>
+          <context context-type="linenumber">339</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3819792277998068944" datatype="html">
         )"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">339,341</context>
+          <context context-type="linenumber">344,346</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2739066218579571288" datatype="html">
         )"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">343,347</context>
+          <context context-type="linenumber">348,352</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2996713129519325161" datatype="html">
         <source>Confirm correspondent assignment</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">384</context>
+          <context context-type="linenumber">389</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6900893559485781849" datatype="html">
         <source>This operation will assign the correspondent &quot;<x id="PH" equiv-text="correspondent.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">386</context>
+          <context context-type="linenumber">391</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1257522660364398440" datatype="html">
         <source>This operation will remove the correspondent from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">388</context>
+          <context context-type="linenumber">393</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5393409374423140648" datatype="html">
         <source>Confirm document type assignment</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">422</context>
+          <context context-type="linenumber">427</context>
         </context-group>
       </trans-unit>
       <trans-unit id="332180123895325027" datatype="html">
         <source>This operation will assign the document type &quot;<x id="PH" equiv-text="documentType.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">424</context>
+          <context context-type="linenumber">429</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2236642492594872779" datatype="html">
         <source>This operation will remove the document type from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">426</context>
+          <context context-type="linenumber">431</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6386555513013840736" datatype="html">
         <source>Confirm storage path assignment</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">460</context>
+          <context context-type="linenumber">465</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8750527458618415924" datatype="html">
         <source>This operation will assign the storage path &quot;<x id="PH" equiv-text="storagePath.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">462</context>
+          <context context-type="linenumber">467</context>
         </context-group>
       </trans-unit>
       <trans-unit id="60728365335056946" datatype="html">
         <source>This operation will remove the storage path from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">464</context>
+          <context context-type="linenumber">469</context>
         </context-group>
       </trans-unit>
       <trans-unit id="749430623564850405" datatype="html">
         <source>Delete confirm</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">487</context>
+          <context context-type="linenumber">578</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4303174930844518780" datatype="html">
         <source>This operation will permanently delete <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">488</context>
+          <context context-type="linenumber">579</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6734339521247847366" datatype="html">
         <source>Delete document(s)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">491</context>
+          <context context-type="linenumber">582</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8968869182645922415" datatype="html">
         <source>This operation will permanently redo OCR for <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
-          <context context-type="linenumber">527</context>
+          <context context-type="linenumber">618</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8076495233090006322" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">301</context>
+          <context context-type="linenumber">305</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4010735610815226758" datatype="html">
         <source>Filter by:</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">16</context>
+          <context context-type="linenumber">19</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">16</context>
+          <context context-type="linenumber">19</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">16</context>
+          <context context-type="linenumber">19</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">16</context>
+          <context context-type="linenumber">19</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1383365546483928780" datatype="html">
         <source>Matching</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">38</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">38</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">38</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">38</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1488347670280290838" datatype="html">
         <source>Document count</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">39</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">39</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">39</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">39</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7376880254267897616" datatype="html">
         <source>Filter Documents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">79</context>
+          <context context-type="linenumber">82</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">79</context>
+          <context context-type="linenumber">82</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">79</context>
+          <context context-type="linenumber">82</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">79</context>
+          <context context-type="linenumber">82</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8095412801504464756" datatype="html">
         <source>{VAR_PLURAL, plural, =1 {One <x id="INTERPOLATION"/>} other {<x id="INTERPOLATION_1"/> total <x id="INTERPOLATION_2"/>}}</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">107</context>
+          <context context-type="linenumber">110</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">107</context>
+          <context context-type="linenumber">110</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">107</context>
+          <context context-type="linenumber">110</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
-          <context context-type="linenumber">107</context>
+          <context context-type="linenumber">110</context>
         </context-group>
       </trans-unit>
       <trans-unit id="810888510148304696" datatype="html">
         <source>Automatic</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">113</context>
+          <context context-type="linenumber">116</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/data/matching-model.ts</context>
         <source>None</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">115</context>
+          <context context-type="linenumber">118</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/data/matching-model.ts</context>
         <source>Successfully created <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">158</context>
+          <context context-type="linenumber">161</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3928835053823658072" datatype="html">
         <source>Error occurred while creating <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">163</context>
+          <context context-type="linenumber">166</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2541368547549828690" datatype="html">
         <source>Successfully updated <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">178</context>
+          <context context-type="linenumber">181</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6442673774206210733" datatype="html">
         <source>Error occurred while saving <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">183</context>
+          <context context-type="linenumber">186</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8371896857609524947" datatype="html">
         <source>Associated documents will not be deleted.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">203</context>
+          <context context-type="linenumber">206</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6639207128255974941" datatype="html">
         <source>Error while deleting element</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">219</context>
+          <context context-type="linenumber">222</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4863024195229581844" datatype="html">
         <source>Permissions updated successfully</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">294</context>
+          <context context-type="linenumber">298</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="1464476612812630086" datatype="html">
+        <source>This operation will permanently delete all objects.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">319</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="5897787932098828336" datatype="html">
+        <source>Objects deleted successfully</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">333</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="8273353839648035634" datatype="html">
+        <source>Error deleting objects</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
+          <context context-type="linenumber">339</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5101757640976222639" datatype="html">
index 9d6cf87c5cc59b444fbe4266d80ba93c8eb4a8ae..58101c388aae649dd93af4a11e9e5836d9c70e3d 100644 (file)
@@ -2,9 +2,12 @@
   <button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedObjects.size === 0">
     <i-bs  name="x"></i-bs>&nbsp;<ng-container i18n>Clear selection</ng-container>
     </button>
-    <button type="button" class="btn btn-sm btn-outline-primary me-5" (click)="setPermissions()" [disabled]="!userOwnsAll || selectedObjects.size === 0">
+    <button type="button" class="btn btn-sm btn-outline-primary me-2" (click)="setPermissions()" [disabled]="!userOwnsAll || selectedObjects.size === 0">
       <i-bs name="person-fill-lock"></i-bs>&nbsp;<ng-container i18n>Permissions</ng-container>
     </button>
+    <button type="button" class="btn btn-sm btn-outline-danger me-5" (click)="delete()" [disabled]="!userOwnsAll || selectedObjects.size === 0">
+      <i-bs name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
+    </button>
     <button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" *pngxIfPermissions="{ action: PermissionAction.Add, type: permissionType }">
       <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Create</ng-container>
     </button>
index 6196a3c8aab9b2da27e56b3d782f22a04ba7af1c..710d3018a9a25643974a547faf4ba104d881557f 100644 (file)
@@ -37,6 +37,7 @@ import { MATCH_NONE } from 'src/app/data/matching-model'
 import { MATCH_LITERAL } from 'src/app/data/matching-model'
 import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
 import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
+import { BulkEditObjectOperation } from 'src/app/services/rest/abstract-name-filter-service'
 
 const tags: Tag[] = [
   {
@@ -149,7 +150,7 @@ describe('ManagementListComponent', () => {
     const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
     const reloadSpy = jest.spyOn(component, 'reloadData')
 
-    const createButton = fixture.debugElement.queryAll(By.css('button'))[2]
+    const createButton = fixture.debugElement.queryAll(By.css('button'))[3]
     createButton.triggerEventHandler('click')
 
     expect(modal).not.toBeUndefined()
@@ -173,7 +174,7 @@ describe('ManagementListComponent', () => {
     const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
     const reloadSpy = jest.spyOn(component, 'reloadData')
 
-    const editButton = fixture.debugElement.queryAll(By.css('button'))[6]
+    const editButton = fixture.debugElement.queryAll(By.css('button'))[7]
     editButton.triggerEventHandler('click')
 
     expect(modal).not.toBeUndefined()
@@ -198,7 +199,7 @@ describe('ManagementListComponent', () => {
     const deleteSpy = jest.spyOn(tagService, 'delete')
     const reloadSpy = jest.spyOn(component, 'reloadData')
 
-    const deleteButton = fixture.debugElement.queryAll(By.css('button'))[7]
+    const deleteButton = fixture.debugElement.queryAll(By.css('button'))[8]
     deleteButton.triggerEventHandler('click')
 
     expect(modal).not.toBeUndefined()
@@ -218,7 +219,7 @@ describe('ManagementListComponent', () => {
 
   it('should support quick filter for objects', () => {
     const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
-    const filterButton = fixture.debugElement.queryAll(By.css('button'))[5]
+    const filterButton = fixture.debugElement.queryAll(By.css('button'))[6]
     filterButton.triggerEventHandler('click')
     expect(qfSpy).toHaveBeenCalledWith([
       { rule_type: FILTER_HAS_TAGS_ALL, value: tags[0].id.toString() },
@@ -246,7 +247,7 @@ describe('ManagementListComponent', () => {
   })
 
   it('should support bulk edit permissions', () => {
-    const bulkEditPermsSpy = jest.spyOn(tagService, 'bulk_update_permissions')
+    const bulkEditPermsSpy = jest.spyOn(tagService, 'bulk_edit_objects')
     component.toggleSelected(tags[0])
     component.toggleSelected(tags[1])
     component.toggleSelected(tags[2])
@@ -280,4 +281,35 @@ describe('ManagementListComponent', () => {
     expect(bulkEditPermsSpy).toHaveBeenCalled()
     expect(successToastSpy).toHaveBeenCalled()
   })
+
+  it('should support bulk delete objects', () => {
+    const bulkEditSpy = jest.spyOn(tagService, 'bulk_edit_objects')
+    component.toggleSelected(tags[0])
+    component.toggleSelected(tags[1])
+    const selected = new Set([tags[0].id, tags[1].id])
+    expect(component.selectedObjects).toEqual(selected)
+    let modal: NgbModalRef
+    modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
+    fixture.detectChanges()
+    component.delete()
+    expect(modal).not.toBeUndefined()
+
+    // fail first
+    bulkEditSpy.mockReturnValueOnce(
+      throwError(() => new Error('error setting permissions'))
+    )
+    const errorToastSpy = jest.spyOn(toastService, 'showError')
+    modal.componentInstance.confirmClicked.emit(null)
+    expect(bulkEditSpy).toHaveBeenCalledWith(
+      Array.from(selected),
+      BulkEditObjectOperation.Delete
+    )
+    expect(errorToastSpy).toHaveBeenCalled()
+
+    const successToastSpy = jest.spyOn(toastService, 'showInfo')
+    bulkEditSpy.mockReturnValueOnce(of('OK'))
+    modal.componentInstance.confirmClicked.emit(null)
+    expect(bulkEditSpy).toHaveBeenCalled()
+    expect(successToastSpy).toHaveBeenCalled()
+  })
 })
index a89b5e4f63eac67f1627ce74eedf40dab816ff0a..0b0365f0680a30c12bc79a27e930b9ccc8e734cb 100644 (file)
@@ -28,7 +28,10 @@ import {
   PermissionsService,
   PermissionType,
 } from 'src/app/services/permissions.service'
-import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service'
+import {
+  AbstractNameFilterService,
+  BulkEditObjectOperation,
+} from 'src/app/services/rest/abstract-name-filter-service'
 import { ToastService } from 'src/app/services/toast.service'
 import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
@@ -282,8 +285,9 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
       ({ permissions, merge }) => {
         modal.componentInstance.buttonsEnabled = false
         this.service
-          .bulk_update_permissions(
+          .bulk_edit_objects(
             Array.from(this.selectedObjects),
+            BulkEditObjectOperation.SetPermissions,
             permissions,
             merge
           )
@@ -306,4 +310,37 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
       }
     )
   }
+
+  delete() {
+    let modal = this.modalService.open(ConfirmDialogComponent, {
+      backdrop: 'static',
+    })
+    modal.componentInstance.title = $localize`Confirm delete`
+    modal.componentInstance.messageBold = $localize`This operation will permanently delete all objects.`
+    modal.componentInstance.message = $localize`This operation cannot be undone.`
+    modal.componentInstance.btnClass = 'btn-danger'
+    modal.componentInstance.btnCaption = $localize`Proceed`
+    modal.componentInstance.confirmClicked.subscribe(() => {
+      modal.componentInstance.buttonsEnabled = false
+      this.service
+        .bulk_edit_objects(
+          Array.from(this.selectedObjects),
+          BulkEditObjectOperation.Delete
+        )
+        .subscribe({
+          next: () => {
+            modal.close()
+            this.toastService.showInfo($localize`Objects deleted successfully`)
+            this.reloadData()
+          },
+          error: (error) => {
+            modal.componentInstance.buttonsEnabled = true
+            this.toastService.showError(
+              $localize`Error deleting objects`,
+              error
+            )
+          },
+        })
+    })
+  }
 }
index e092707010d13cb3b04629ca47825681fe03a3a4..f61efc6401b6abdb29dab572599a02d4a8af4dea 100644 (file)
@@ -2,7 +2,10 @@ import { HttpTestingController } from '@angular/common/http/testing'
 import { Subscription } from 'rxjs'
 import { TestBed } from '@angular/core/testing'
 import { environment } from 'src/environments/environment'
-import { AbstractNameFilterService } from './abstract-name-filter-service'
+import {
+  AbstractNameFilterService,
+  BulkEditObjectOperation,
+} from './abstract-name-filter-service'
 import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec'
 
 let httpTestingController: HttpTestingController
@@ -53,8 +56,9 @@ export const commonAbstractNameFilterPaperlessServiceTests = (
         },
       }
       subscription = service
-        .bulk_update_permissions(
+        .bulk_edit_objects(
           [1, 2],
+          BulkEditObjectOperation.SetPermissions,
           {
             owner,
             set_permissions: permissions,
@@ -63,9 +67,33 @@ export const commonAbstractNameFilterPaperlessServiceTests = (
         )
         .subscribe()
       const req = httpTestingController.expectOne(
-        `${environment.apiBaseUrl}bulk_edit_object_perms/`
+        `${environment.apiBaseUrl}bulk_edit_objects/`
       )
       expect(req.request.method).toEqual('POST')
+      expect(req.request.body).toEqual({
+        objects: [1, 2],
+        object_type: endpoint,
+        operation: BulkEditObjectOperation.SetPermissions,
+        permissions,
+        owner,
+        merge: true,
+      })
+      req.flush([])
+    })
+
+    test('should call appropriate api endpoint for bulk delete objects', () => {
+      subscription = service
+        .bulk_edit_objects([1, 2], BulkEditObjectOperation.Delete)
+        .subscribe()
+      const req = httpTestingController.expectOne(
+        `${environment.apiBaseUrl}bulk_edit_objects/`
+      )
+      expect(req.request.method).toEqual('POST')
+      expect(req.request.body).toEqual({
+        objects: [1, 2],
+        object_type: endpoint,
+        operation: BulkEditObjectOperation.Delete,
+      })
       req.flush([])
     })
   })
index b38994086b3185eec023550d083fd518ad15ee7b..1018f0fa2998d3a5d493e42aabf2078937a8dac3 100644 (file)
@@ -3,6 +3,11 @@ import { AbstractPaperlessService } from './abstract-paperless-service'
 import { PermissionsObject } from 'src/app/data/object-with-permissions'
 import { Observable } from 'rxjs'
 
+export enum BulkEditObjectOperation {
+  SetPermissions = 'set_permissions',
+  Delete = 'delete',
+}
+
 export abstract class AbstractNameFilterService<
   T extends ObjectWithId,
 > extends AbstractPaperlessService<T> {
@@ -24,17 +29,22 @@ export abstract class AbstractNameFilterService<
     return this.list(page, pageSize, sortField, sortReverse, params)
   }
 
-  bulk_update_permissions(
+  bulk_edit_objects(
     objects: Array<number>,
-    permissions: { owner: number; set_permissions: PermissionsObject },
-    merge: boolean
+    operation: BulkEditObjectOperation,
+    permissions: { owner: number; set_permissions: PermissionsObject } = null,
+    merge: boolean = null
   ): Observable<string> {
-    return this.http.post<string>(`${this.baseUrl}bulk_edit_object_perms/`, {
+    const params = {
       objects,
       object_type: this.resourceName,
-      owner: permissions.owner,
-      permissions: permissions.set_permissions,
-      merge,
-    })
+      operation,
+    }
+    if (operation === BulkEditObjectOperation.SetPermissions) {
+      params['owner'] = permissions?.owner
+      params['permissions'] = permissions?.set_permissions
+      params['merge'] = merge
+    }
+    return this.http.post<string>(`${this.baseUrl}bulk_edit_objects/`, params)
   }
 }
index 6135915d933bb2ebc27666f501d510cef58599d9..7de16a988761b5cf3a86197e38f01335790a037f 100644 (file)
@@ -3,7 +3,7 @@ const base_url = new URL(document.baseURI)
 export const environment = {
   production: true,
   apiBaseUrl: document.baseURI + 'api/',
-  apiVersion: '4',
+  apiVersion: '5',
   appTitle: 'Paperless-ngx',
   version: '2.4.3-dev',
   webSocketHost: window.location.host,
index fccb8927c238d951c58deae439db4dbc0c067190..18715e90f8fa5b594f6df374e9c377cc6605da99 100644 (file)
@@ -5,7 +5,7 @@
 export const environment = {
   production: false,
   apiBaseUrl: 'http://localhost:8000/api/',
-  apiVersion: '4',
+  apiVersion: '5',
   appTitle: 'Paperless-ngx',
   version: 'DEVELOPMENT',
   webSocketHost: 'localhost:8000',
index e712d4b59d17e2c7bebbd9f80b7333e573c00a3c..5fa1046403d952061d0565106097b4b93c1fe216 100644 (file)
@@ -1281,7 +1281,7 @@ class ShareLinkSerializer(OwnedObjectSerializer):
         return super().create(validated_data)
 
 
-class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissionsMixin):
+class BulkEditObjectsSerializer(serializers.Serializer, SetPermissionsMixin):
     objects = serializers.ListField(
         required=True,
         allow_empty=False,
@@ -1301,6 +1301,16 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions
         write_only=True,
     )
 
+    operation = serializers.ChoiceField(
+        choices=[
+            "set_permissions",
+            "delete",
+        ],
+        label="Operation",
+        required=True,
+        write_only=True,
+    )
+
     owner = serializers.PrimaryKeyRelatedField(
         queryset=User.objects.all(),
         required=False,
@@ -1353,11 +1363,14 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions
     def validate(self, attrs):
         object_type = attrs["object_type"]
         objects = attrs["objects"]
-        permissions = attrs.get("permissions")
+        operation = attrs.get("operation")
 
         self._validate_objects(objects, object_type)
-        if permissions is not None:
-            self._validate_permissions(permissions)
+
+        if operation == "set_permissions":
+            permissions = attrs.get("permissions")
+            if permissions is not None:
+                self._validate_permissions(permissions)
 
         return attrs
 
index e894cae90bf648a3fcd9e5fb6dd9387dc3a38507..3b38f2b5fb3dd70b2c3b050046eb8518ed7dea1e 100644 (file)
@@ -222,3 +222,118 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
         args, _ = bulk_update_mock.call_args
 
         self.assertCountEqual([document.pk], args[0])
+
+
+class TestBulkEditObjects(APITestCase):
+    # See test_api_permissions.py for bulk tests on permissions
+    def setUp(self):
+        super().setUp()
+
+        self.temp_admin = User.objects.create_superuser(username="temp_admin")
+        self.client.force_authenticate(user=self.temp_admin)
+
+        self.t1 = Tag.objects.create(name="t1")
+        self.t2 = Tag.objects.create(name="t2")
+        self.c1 = Correspondent.objects.create(name="c1")
+        self.dt1 = DocumentType.objects.create(name="dt1")
+        self.sp1 = StoragePath.objects.create(name="sp1")
+        self.user1 = User.objects.create(username="user1")
+        self.user2 = User.objects.create(username="user2")
+        self.user3 = User.objects.create(username="user3")
+
+    def test_bulk_objects_delete(self):
+        """
+        GIVEN:
+            - Existing objects
+        WHEN:
+            - bulk_edit_objects API endpoint is called with delete operation
+        THEN:
+            - Objects are deleted
+        """
+        response = self.client.post(
+            "/api/bulk_edit_objects/",
+            json.dumps(
+                {
+                    "objects": [self.t1.id, self.t2.id],
+                    "object_type": "tags",
+                    "operation": "delete",
+                },
+            ),
+            content_type="application/json",
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Tag.objects.count(), 0)
+
+        response = self.client.post(
+            "/api/bulk_edit_objects/",
+            json.dumps(
+                {
+                    "objects": [self.c1.id],
+                    "object_type": "correspondents",
+                    "operation": "delete",
+                },
+            ),
+            content_type="application/json",
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Correspondent.objects.count(), 0)
+
+        response = self.client.post(
+            "/api/bulk_edit_objects/",
+            json.dumps(
+                {
+                    "objects": [self.dt1.id],
+                    "object_type": "document_types",
+                    "operation": "delete",
+                },
+            ),
+            content_type="application/json",
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(DocumentType.objects.count(), 0)
+
+        response = self.client.post(
+            "/api/bulk_edit_objects/",
+            json.dumps(
+                {
+                    "objects": [self.sp1.id],
+                    "object_type": "storage_paths",
+                    "operation": "delete",
+                },
+            ),
+            content_type="application/json",
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(StoragePath.objects.count(), 0)
+
+    def test_bulk_edit_object_permissions_insufficient_perms(self):
+        """
+        GIVEN:
+            - Objects owned by user other than logged in user
+        WHEN:
+            - bulk_edit_objects API endpoint is called with delete operation
+        THEN:
+            - User is not able to delete objects
+        """
+        self.t1.owner = User.objects.get(username="temp_admin")
+        self.t1.save()
+        self.client.force_authenticate(user=self.user1)
+
+        response = self.client.post(
+            "/api/bulk_edit_objects/",
+            json.dumps(
+                {
+                    "objects": [self.t1.id, self.t2.id],
+                    "object_type": "tags",
+                    "operation": "delete",
+                },
+            ),
+            content_type="application/json",
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        self.assertEqual(response.content, b"Insufficient permissions")
index 851608f37a161bc5535bdd7a613d5877fca1d574..92e47a1eda86c1152d60b5412393be12ff68d1e2 100644 (file)
@@ -717,7 +717,7 @@ class TestBulkEditObjectPermissions(APITestCase):
         GIVEN:
             - Existing objects
         WHEN:
-            - bulk_edit_object_perms API endpoint is called
+            - bulk_edit_objects API endpoint is called with set_permissions operation
         THEN:
             - Permissions and / or owner are changed
         """
@@ -733,11 +733,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         }
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id],
                     "object_type": "tags",
+                    "operation": "set_permissions",
                     "permissions": permissions,
                 },
             ),
@@ -748,11 +749,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.assertIn(self.user1, get_users_with_perms(self.t1))
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.c1.id],
                     "object_type": "correspondents",
+                    "operation": "set_permissions",
                     "permissions": permissions,
                 },
             ),
@@ -763,11 +765,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.assertIn(self.user1, get_users_with_perms(self.c1))
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.dt1.id],
                     "object_type": "document_types",
+                    "operation": "set_permissions",
                     "permissions": permissions,
                 },
             ),
@@ -778,11 +781,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.assertIn(self.user1, get_users_with_perms(self.dt1))
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.sp1.id],
                     "object_type": "storage_paths",
+                    "operation": "set_permissions",
                     "permissions": permissions,
                 },
             ),
@@ -793,11 +797,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.assertIn(self.user1, get_users_with_perms(self.sp1))
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id],
                     "object_type": "tags",
+                    "operation": "set_permissions",
                     "owner": self.user3.id,
                 },
             ),
@@ -808,11 +813,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.assertEqual(Tag.objects.get(pk=self.t2.id).owner, self.user3)
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.sp1.id],
                     "object_type": "storage_paths",
+                    "operation": "set_permissions",
                     "owner": self.user3.id,
                 },
             ),
@@ -827,7 +833,7 @@ class TestBulkEditObjectPermissions(APITestCase):
         GIVEN:
             - Existing objects
         WHEN:
-            - bulk_edit_object_perms API endpoint is called with merge=True or merge=False (default)
+            - bulk_edit_objects API endpoint is called with set_permissions operation with merge=True or merge=False (default)
         THEN:
             - Permissions and / or owner are replaced or merged, depending on the merge flag
         """
@@ -848,13 +854,14 @@ class TestBulkEditObjectPermissions(APITestCase):
 
         # merge=True
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id],
                     "object_type": "tags",
                     "owner": self.user1.id,
                     "permissions": permissions,
+                    "operation": "set_permissions",
                     "merge": True,
                 },
             ),
@@ -877,12 +884,13 @@ class TestBulkEditObjectPermissions(APITestCase):
 
         # merge=False (default)
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id],
                     "object_type": "tags",
                     "permissions": permissions,
+                    "operation": "set_permissions",
                     "merge": False,
                 },
             ),
@@ -900,7 +908,7 @@ class TestBulkEditObjectPermissions(APITestCase):
         GIVEN:
             - Objects owned by user other than logged in user
         WHEN:
-            - bulk_edit_object_perms API endpoint is called
+            - bulk_edit_objects API endpoint is called with set_permissions operation
         THEN:
             - User is not able to change permissions
         """
@@ -909,11 +917,12 @@ class TestBulkEditObjectPermissions(APITestCase):
         self.client.force_authenticate(user=self.user1)
 
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id],
                     "object_type": "tags",
+                    "operation": "set_permissions",
                     "owner": self.user1.id,
                 },
             ),
@@ -928,17 +937,18 @@ class TestBulkEditObjectPermissions(APITestCase):
         GIVEN:
             - Existing objects
         WHEN:
-            - bulk_edit_object_perms API endpoint is called with invalid params
+            - bulk_edit_objects API endpoint is called with set_permissions operation with invalid params
         THEN:
             - Validation fails
         """
         # not a list
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": self.t1.id,
                     "object_type": "tags",
+                    "operation": "set_permissions",
                     "owner": self.user1.id,
                 },
             ),
@@ -949,7 +959,7 @@ class TestBulkEditObjectPermissions(APITestCase):
 
         # not a list of ints
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": ["one"],
@@ -964,11 +974,12 @@ class TestBulkEditObjectPermissions(APITestCase):
 
         # duplicates
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [self.t1.id, self.t2.id, self.t1.id],
                     "object_type": "tags",
+                    "operation": "set_permissions",
                     "owner": self.user1.id,
                 },
             ),
@@ -979,11 +990,12 @@ class TestBulkEditObjectPermissions(APITestCase):
 
         # not a valid object type
         response = self.client.post(
-            "/api/bulk_edit_object_perms/",
+            "/api/bulk_edit_objects/",
             json.dumps(
                 {
                     "objects": [1],
                     "object_type": "madeup",
+                    "operation": "set_permissions",
                     "owner": self.user1.id,
                 },
             ),
index 7a037c27d551870e230b2285547b96bf6cd656de..3be7f4ec6ed979037576c8d3c02d069fbd461a5f 100644 (file)
@@ -115,7 +115,7 @@ from documents.permissions import has_perms_owner_aware
 from documents.permissions import set_permissions_for_object
 from documents.serialisers import AcknowledgeTasksViewSerializer
 from documents.serialisers import BulkDownloadSerializer
-from documents.serialisers import BulkEditObjectPermissionsSerializer
+from documents.serialisers import BulkEditObjectsSerializer
 from documents.serialisers import BulkEditSerializer
 from documents.serialisers import CorrespondentSerializer
 from documents.serialisers import CustomFieldSerializer
@@ -1401,9 +1401,9 @@ def serve_file(doc: Document, use_archive: bool, disposition: str):
     return response
 
 
-class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin):
+class BulkEditObjectsView(GenericAPIView, PassUserMixin):
     permission_classes = (IsAuthenticated,)
-    serializer_class = BulkEditObjectPermissionsSerializer
+    serializer_class = BulkEditObjectsSerializer
     parser_classes = (parsers.JSONParser,)
 
     def post(self, request, *args, **kwargs):
@@ -1414,42 +1414,52 @@ class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin):
         object_type = serializer.validated_data.get("object_type")
         object_ids = serializer.validated_data.get("objects")
         object_class = serializer.get_object_class(object_type)
-        permissions = serializer.validated_data.get("permissions")
-        owner = serializer.validated_data.get("owner")
-        merge = serializer.validated_data.get("merge")
+        operation = serializer.validated_data.get("operation")
+
+        objs = object_class.objects.filter(pk__in=object_ids)
 
         if not user.is_superuser:
-            objs = object_class.objects.filter(pk__in=object_ids)
             has_perms = all((obj.owner == user or obj.owner is None) for obj in objs)
 
             if not has_perms:
                 return HttpResponseForbidden("Insufficient permissions")
 
-        try:
-            qs = object_class.objects.filter(id__in=object_ids)
+        if operation == "set_permissions":
+            permissions = serializer.validated_data.get("permissions")
+            owner = serializer.validated_data.get("owner")
+            merge = serializer.validated_data.get("merge")
 
-            # if merge is true, we dont want to remove the owner
-            if "owner" in serializer.validated_data and (
-                not merge or (merge and owner is not None)
-            ):
-                # if merge is true, we dont want to overwrite the owner
-                qs_owner_update = qs.filter(owner__isnull=True) if merge else qs
-                qs_owner_update.update(owner=owner)
-
-            if "permissions" in serializer.validated_data:
-                for obj in qs:
-                    set_permissions_for_object(
-                        permissions=permissions,
-                        object=obj,
-                        merge=merge,
-                    )
+            try:
+                qs = object_class.objects.filter(id__in=object_ids)
 
-            return Response({"result": "OK"})
-        except Exception as e:
-            logger.warning(f"An error occurred performing bulk permissions edit: {e!s}")
-            return HttpResponseBadRequest(
-                "Error performing bulk permissions edit, check logs for more detail.",
-            )
+                # if merge is true, we dont want to remove the owner
+                if "owner" in serializer.validated_data and (
+                    not merge or (merge and owner is not None)
+                ):
+                    # if merge is true, we dont want to overwrite the owner
+                    qs_owner_update = qs.filter(owner__isnull=True) if merge else qs
+                    qs_owner_update.update(owner=owner)
+
+                if "permissions" in serializer.validated_data:
+                    for obj in qs:
+                        set_permissions_for_object(
+                            permissions=permissions,
+                            object=obj,
+                            merge=merge,
+                        )
+
+            except Exception as e:
+                logger.warning(
+                    f"An error occurred performing bulk permissions edit: {e!s}",
+                )
+                return HttpResponseBadRequest(
+                    "Error performing bulk permissions edit, check logs for more detail.",
+                )
+
+        elif operation == "delete":
+            objs.delete()
+
+        return Response({"result": "OK"})
 
 
 class WorkflowTriggerViewSet(ModelViewSet):
index 94400c8dde9bd83cbdfc26f9928481dc5ef08e20..a9792db9f8a7881f94b898193a86ebbc86d61c38 100644 (file)
@@ -322,7 +322,7 @@ REST_FRAMEWORK = {
     "DEFAULT_VERSION": "1",
     # Make sure these are ordered and that the most recent version appears
     # last
-    "ALLOWED_VERSIONS": ["1", "2", "3", "4"],
+    "ALLOWED_VERSIONS": ["1", "2", "3", "4", "5"],
 }
 
 if DEBUG:
index 74f6fc1086eb0e0e97cb3bff8e6e89646767eadd..0419b8e66ad4dae054a303fe4190b4a43fbfbd83 100644 (file)
@@ -16,7 +16,7 @@ from rest_framework.routers import DefaultRouter
 
 from documents.views import AcknowledgeTasksView
 from documents.views import BulkDownloadView
-from documents.views import BulkEditObjectPermissionsView
+from documents.views import BulkEditObjectsView
 from documents.views import BulkEditView
 from documents.views import CorrespondentViewSet
 from documents.views import CustomFieldViewSet
@@ -129,9 +129,9 @@ urlpatterns = [
                 ),
                 path("token/", views.obtain_auth_token),
                 re_path(
-                    "^bulk_edit_object_perms/",
-                    BulkEditObjectPermissionsView.as_view(),
-                    name="bulk_edit_object_permissions",
+                    "^bulk_edit_objects/",
+                    BulkEditObjectsView.as_view(),
+                    name="bulk_edit_objects",
                 ),
                 path("profile/generate_auth_token/", GenerateAuthTokenView.as_view()),
                 path(