Feature: bulk edit custom field values (#8428)

This commit is contained in:
shamoon 2024-12-09 09:35:49 -08:00 committed by GitHub
parent 8574d28c6f
commit e4f69dc945
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 709 additions and 105 deletions

View File

@ -473,6 +473,11 @@ The following methods are supported:
- Requires `parameters`: - Requires `parameters`:
- `"pages": [..]` The list should be a list of integers e.g. `"[2,3,4]"` - `"pages": [..]` The list should be a list of integers e.g. `"[2,3,4]"`
- The delete_pages operation only accepts a single document. - The delete_pages operation only accepts a single document.
- `modify_custom_fields`
- Requires `parameters`:
- `"add_custom_fields": { CUSTOM_FIELD_ID: VALUE }`: JSON object consisting of custom field id:value pairs to add to the document, can also be a list of custom field IDs
to add with empty values.
- `"remove_custom_fields": [CUSTOM_FIELD_ID]`: custom field ids to remove from the document.
### Objects ### Objects

View File

@ -594,6 +594,10 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">341</context> <context context-type="linenumber">341</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
<context context-type="linenumber">79</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context> <context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
@ -1161,7 +1165,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">101</context> <context context-type="linenumber">104</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
@ -1450,6 +1454,10 @@
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context> <context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">20</context>
@ -1756,7 +1764,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">107</context> <context context-type="linenumber">110</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context> <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
@ -2049,7 +2057,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">161</context> <context context-type="linenumber">164</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context> <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
@ -2520,15 +2528,15 @@
</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>
<context context-type="linenumber">758</context> <context context-type="linenumber">759</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>
<context context-type="linenumber">791</context> <context context-type="linenumber">792</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>
<context context-type="linenumber">810</context> <context context-type="linenumber">811</context>
</context-group> </context-group>
<context-group purpose="location"> <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/custom-fields/custom-fields.component.ts</context>
@ -2927,7 +2935,7 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">143</context> <context context-type="linenumber">147</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6329940072345709724" datatype="html"> <trans-unit id="6329940072345709724" datatype="html">
@ -2953,7 +2961,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">133</context> <context context-type="linenumber">136</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
@ -2961,7 +2969,7 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">149</context> <context context-type="linenumber">153</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="searchResults.noResults" datatype="html"> <trans-unit id="searchResults.noResults" datatype="html">
@ -3114,27 +3122,27 @@
</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>
<context context-type="linenumber">401</context> <context context-type="linenumber">402</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>
<context context-type="linenumber">441</context> <context context-type="linenumber">442</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>
<context context-type="linenumber">479</context> <context context-type="linenumber">480</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>
<context context-type="linenumber">517</context> <context context-type="linenumber">518</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>
<context context-type="linenumber">579</context> <context context-type="linenumber">580</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>
<context context-type="linenumber">712</context> <context context-type="linenumber">713</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1407560924967345762" datatype="html"> <trans-unit id="1407560924967345762" datatype="html">
@ -4947,7 +4955,7 @@
<source>Click again to exclude items.</source> <source>Click again to exclude items.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">77</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7593728289020204896" datatype="html"> <trans-unit id="7593728289020204896" datatype="html">
@ -4962,7 +4970,7 @@
<source>Open <x id="PH" equiv-text="this.title"/> filter</source> <source>Open <x id="PH" equiv-text="this.title"/> filter</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context> <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
<context context-type="linenumber">488</context> <context context-type="linenumber">494</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7005745151564974365" datatype="html"> <trans-unit id="7005745151564974365" datatype="html">
@ -6096,7 +6104,7 @@
</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>
<context context-type="linenumber">346</context> <context context-type="linenumber">347</context>
</context-group> </context-group>
<note priority="1" from="description">this string is used to separate processing, failed and added on the file upload widget</note> <note priority="1" from="description">this string is used to separate processing, failed and added on the file upload widget</note>
</trans-unit> </trans-unit>
@ -6171,7 +6179,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">111</context> <context context-type="linenumber">114</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1418444397960583910" datatype="html"> <trans-unit id="1418444397960583910" datatype="html">
@ -6200,7 +6208,7 @@
</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.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">114</context> <context context-type="linenumber">117</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4399672576012609374" datatype="html"> <trans-unit id="4399672576012609374" datatype="html">
@ -6626,7 +6634,7 @@
</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>
<context context-type="linenumber">714</context> <context context-type="linenumber">715</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2048798344356757326" datatype="html"> <trans-unit id="2048798344356757326" datatype="html">
@ -6637,7 +6645,7 @@
</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>
<context context-type="linenumber">716</context> <context context-type="linenumber">717</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="619486176823357521" datatype="html"> <trans-unit id="619486176823357521" datatype="html">
@ -6648,7 +6656,7 @@
</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>
<context context-type="linenumber">754</context> <context context-type="linenumber">755</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2951161989614003846" datatype="html"> <trans-unit id="2951161989614003846" datatype="html">
@ -6722,7 +6730,7 @@
</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>
<context context-type="linenumber">788</context> <context context-type="linenumber">789</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="857641176955257111" datatype="html"> <trans-unit id="857641176955257111" datatype="html">
@ -6872,64 +6880,75 @@
<context context-type="linenumber">83</context> <context context-type="linenumber">83</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5139192806922838657" datatype="html">
<source>Set values</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">93</context>
</context-group>
</trans-unit>
<trans-unit id="3206542606001340679" datatype="html"> <trans-unit id="3206542606001340679" datatype="html">
<source>Merge</source> <source>Merge</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">120</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1015374532025907183" datatype="html"> <trans-unit id="1015374532025907183" datatype="html">
<source>Include:</source> <source>Include:</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">139</context> <context context-type="linenumber">142</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1537670659786159738" datatype="html"> <trans-unit id="1537670659786159738" datatype="html">
<source>Archived files</source> <source>Archived files</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">143</context> <context context-type="linenumber">146</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2520291319362448498" datatype="html"> <trans-unit id="2520291319362448498" datatype="html">
<source>Original files</source> <source>Original files</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">150</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8009862506882713059" datatype="html"> <trans-unit id="8009862506882713059" datatype="html">
<source>Use formatted filename</source> <source>Use formatted filename</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">152</context> <context context-type="linenumber">155</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1215215387232313677" datatype="html"> <trans-unit id="1215215387232313677" datatype="html">
<source>Error executing bulk operation</source> <source>Error executing bulk operation</source>
<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>
<context context-type="linenumber">250</context> <context context-type="linenumber">251</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">859</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7894972847287473517" datatype="html"> <trans-unit id="7894972847287473517" datatype="html">
<source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot;</source> <source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot;</source>
<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>
<context context-type="linenumber">338</context> <context context-type="linenumber">339</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>
<context context-type="linenumber">344</context> <context context-type="linenumber">345</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8639884465898458690" datatype="html"> <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> <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-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>
<context context-type="linenumber">340</context> <context context-type="linenumber">341</context>
</context-group> </context-group>
<note priority="1" from="description">This is for messages like &apos;modify &quot;tag1&quot; and &quot;tag2&quot;&apos;</note> <note priority="1" from="description">This is for messages like &apos;modify &quot;tag1&quot; and &quot;tag2&quot;&apos;</note>
</trans-unit> </trans-unit>
@ -6937,7 +6956,7 @@
<source><x id="PH" equiv-text="list"/> and &quot;<x id="PH_1" equiv-text="items[items.length - 1].name"/>&quot;</source> <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-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>
<context context-type="linenumber">348,350</context> <context context-type="linenumber">349,351</context>
</context-group> </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> <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> </trans-unit>
@ -6945,14 +6964,14 @@
<source>Confirm tags assignment</source> <source>Confirm tags assignment</source>
<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>
<context context-type="linenumber">365</context> <context context-type="linenumber">366</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6619516195038467207" datatype="html"> <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> <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-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>
<context context-type="linenumber">371</context> <context context-type="linenumber">372</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1894412783609570695" datatype="html"> <trans-unit id="1894412783609570695" datatype="html">
@ -6961,14 +6980,14 @@
)"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">376,378</context> <context context-type="linenumber">377,379</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7181166515756808573" datatype="html"> <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> <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-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>
<context context-type="linenumber">384</context> <context context-type="linenumber">385</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3819792277998068944" datatype="html"> <trans-unit id="3819792277998068944" datatype="html">
@ -6977,7 +6996,7 @@
)"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">389,391</context> <context context-type="linenumber">390,392</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2739066218579571288" datatype="html"> <trans-unit id="2739066218579571288" datatype="html">
@ -6988,84 +7007,84 @@
)"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">393,397</context> <context context-type="linenumber">394,398</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2996713129519325161" datatype="html"> <trans-unit id="2996713129519325161" datatype="html">
<source>Confirm correspondent assignment</source> <source>Confirm correspondent assignment</source>
<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>
<context context-type="linenumber">434</context> <context context-type="linenumber">435</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6900893559485781849" datatype="html"> <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> <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-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>
<context context-type="linenumber">436</context> <context context-type="linenumber">437</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1257522660364398440" datatype="html"> <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> <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-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>
<context context-type="linenumber">438</context> <context context-type="linenumber">439</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5393409374423140648" datatype="html"> <trans-unit id="5393409374423140648" datatype="html">
<source>Confirm document type assignment</source> <source>Confirm document type assignment</source>
<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>
<context context-type="linenumber">472</context> <context context-type="linenumber">473</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="332180123895325027" datatype="html"> <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> <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-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>
<context context-type="linenumber">474</context> <context context-type="linenumber">475</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2236642492594872779" datatype="html"> <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> <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-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>
<context context-type="linenumber">476</context> <context context-type="linenumber">477</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6386555513013840736" datatype="html"> <trans-unit id="6386555513013840736" datatype="html">
<source>Confirm storage path assignment</source> <source>Confirm storage path assignment</source>
<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>
<context context-type="linenumber">510</context> <context context-type="linenumber">511</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8750527458618415924" datatype="html"> <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> <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-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>
<context context-type="linenumber">512</context> <context context-type="linenumber">513</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="60728365335056946" datatype="html"> <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> <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-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>
<context context-type="linenumber">514</context> <context context-type="linenumber">515</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4187352575310415704" datatype="html"> <trans-unit id="4187352575310415704" datatype="html">
<source>Confirm custom field assignment</source> <source>Confirm custom field assignment</source>
<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>
<context context-type="linenumber">543</context> <context context-type="linenumber">544</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7966494636326273856" datatype="html"> <trans-unit id="7966494636326273856" datatype="html">
<source>This operation will assign the custom field &quot;<x id="PH" equiv-text="customField.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will assign the custom field &quot;<x id="PH" equiv-text="customField.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">549</context> <context context-type="linenumber">550</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5789455969634598553" datatype="html"> <trans-unit id="5789455969634598553" datatype="html">
@ -7074,14 +7093,14 @@
)"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">554,556</context> <context context-type="linenumber">555,557</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5648572354333199245" datatype="html"> <trans-unit id="5648572354333199245" datatype="html">
<source>This operation will remove the custom field &quot;<x id="PH" equiv-text="customField.name"/>&quot; from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will remove the custom field &quot;<x id="PH" equiv-text="customField.name"/>&quot; from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">562</context> <context context-type="linenumber">563</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6666899594015948817" datatype="html"> <trans-unit id="6666899594015948817" datatype="html">
@ -7090,7 +7109,7 @@
)"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">567,569</context> <context context-type="linenumber">568,570</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8050047262594964176" datatype="html"> <trans-unit id="8050047262594964176" datatype="html">
@ -7101,56 +7120,85 @@
)"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source> )"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">571,575</context> <context context-type="linenumber">572,576</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8615059324209654051" datatype="html"> <trans-unit id="8615059324209654051" datatype="html">
<source>Move <x id="PH" equiv-text="this.list.selected.size"/> selected document(s) to the trash?</source> <source>Move <x id="PH" equiv-text="this.list.selected.size"/> selected document(s) to the trash?</source>
<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>
<context context-type="linenumber">713</context> <context context-type="linenumber">714</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8585195717323764335" datatype="html"> <trans-unit id="8585195717323764335" datatype="html">
<source>This operation will permanently recreate the archive files for <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will permanently recreate the archive files for <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
<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>
<context context-type="linenumber">755</context> <context context-type="linenumber">756</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7366623494074776040" datatype="html"> <trans-unit id="7366623494074776040" datatype="html">
<source>The archive files will be re-generated with the current settings.</source> <source>The archive files will be re-generated with the current settings.</source>
<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>
<context context-type="linenumber">756</context> <context context-type="linenumber">757</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6390006284731990222" datatype="html"> <trans-unit id="6390006284731990222" datatype="html">
<source>This operation will permanently rotate the original version of <x id="PH" equiv-text="this.list.selected.size"/> document(s).</source> <source>This operation will permanently rotate the original version of <x id="PH" equiv-text="this.list.selected.size"/> document(s).</source>
<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>
<context context-type="linenumber">789</context> <context context-type="linenumber">790</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7910756456450124185" datatype="html"> <trans-unit id="7910756456450124185" datatype="html">
<source>Merge confirm</source> <source>Merge confirm</source>
<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>
<context context-type="linenumber">808</context> <context context-type="linenumber">809</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7643543647233874431" datatype="html"> <trans-unit id="7643543647233874431" datatype="html">
<source>This operation will merge <x id="PH" equiv-text="this.list.selected.size"/> selected documents into a new document.</source> <source>This operation will merge <x id="PH" equiv-text="this.list.selected.size"/> selected documents into a new document.</source>
<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>
<context context-type="linenumber">809</context> <context context-type="linenumber">810</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7869008840945899895" datatype="html"> <trans-unit id="7869008840945899895" datatype="html">
<source>Merged document will be queued for consumption.</source> <source>Merged document will be queued for consumption.</source>
<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>
<context context-type="linenumber">825</context> <context context-type="linenumber">826</context>
</context-group>
</trans-unit>
<trans-unit id="8362457261872200374" datatype="html">
<source>Bulk operation executed successfully</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">849</context>
</context-group>
</trans-unit>
<trans-unit id="6307402210351946694" datatype="html">
<source>{VAR_PLURAL, plural, =1 {Set custom fields for 1 document} other {Set custom fields for <x id="INTERPOLATION"/> documents}}</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
<context context-type="linenumber">3,7</context>
</context-group>
</trans-unit>
<trans-unit id="8100177157764133131" datatype="html">
<source>Select custom fields</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="8244572554104037643" datatype="html">
<source>{VAR_PLURAL, plural, =1 {This operation will also remove 1 custom field from the selected documents.} other {This operation will also
remove <x id="INTERPOLATION"/> custom fields from the selected documents.}}</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
<context context-type="linenumber">69,74</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2784168796433474565" datatype="html"> <trans-unit id="2784168796433474565" datatype="html">
@ -7179,11 +7227,11 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">76,77</context> <context context-type="linenumber">80,81</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">91,92</context> <context context-type="linenumber">95,96</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2030261243264601523" datatype="html"> <trans-unit id="2030261243264601523" datatype="html">
@ -7194,11 +7242,11 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">77,78</context> <context context-type="linenumber">81,82</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">92,93</context> <context context-type="linenumber">96,97</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4235671847487610290" datatype="html"> <trans-unit id="4235671847487610290" datatype="html">
@ -7209,11 +7257,11 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">78,79</context> <context context-type="linenumber">82,83</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">93,94</context> <context context-type="linenumber">97,98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="197162226430950645" datatype="html"> <trans-unit id="197162226430950645" datatype="html">
@ -7224,7 +7272,7 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">110</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5739581984228459958" datatype="html"> <trans-unit id="5739581984228459958" datatype="html">
@ -7235,7 +7283,7 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">125</context> <context context-type="linenumber">129</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context> <context context-type="sourcefile">src/app/data/document.ts</context>
@ -7271,14 +7319,14 @@
<source>Toggle document type filter</source> <source>Toggle document type filter</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">59</context> <context context-type="linenumber">63</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8950368321707344185" datatype="html"> <trans-unit id="8950368321707344185" datatype="html">
<source>Toggle storage path filter</source> <source>Toggle storage path filter</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">66</context> <context context-type="linenumber">70</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5145213156408463657" datatype="html"> <trans-unit id="5145213156408463657" datatype="html">

View File

@ -133,6 +133,7 @@ import { DeletePagesConfirmDialogComponent } from './components/common/confirm-d
import { TrashComponent } from './components/admin/trash/trash.component' import { TrashComponent } from './components/admin/trash/trash.component'
import { EntriesComponent } from './components/common/input/entries/entries.component' import { EntriesComponent } from './components/common/input/entries/entries.component'
import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component' import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component'
import { CustomFieldsBulkEditDialogComponent } from './components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component'
import { import {
airplane, airplane,
archive, archive,
@ -528,6 +529,7 @@ function initializeApp(settings: SettingsService) {
TrashComponent, TrashComponent,
EntriesComponent, EntriesComponent,
SavedViewsComponent, SavedViewsComponent,
CustomFieldsBulkEditDialogComponent,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
imports: [ imports: [

View File

@ -60,12 +60,18 @@
</button> </button>
} }
@if ((selectionModel.items | filter: filterText:'name').length > 0) { @if ((selectionModel.items | filter: filterText:'name').length > 0) {
<button class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled"> <button class="list-group-item list-group-item-action bg-light d-flex align-items-center" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled">
<small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small> <small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
<i-bs width="1.5em" height="1em" name="arrow-right"></i-bs> <i-bs width="1.5em" height="1em" name="arrow-right"></i-bs>
</button> </button>
} }
} }
@if (extraButtonTitle) {
<button class="list-group-item list-group-item-action bg-light d-flex align-items-center" (click)="extraButtonClicked($event)" [disabled]="disabled">
<small class="ms-2 fw-bold">{{extraButtonTitle}}</small>
<i-bs width="1.5em" height="1em" name="arrow-right"></i-bs>
</button>
}
@if (!editing && manyToOne) { @if (!editing && manyToOne) {
<div class="list-group-item list-group-item-note pt-1 pb-2"> <div class="list-group-item list-group-item-note pt-1 pb-2">
<small i18n>Click again to exclude items.</small> <small i18n>Click again to exclude items.</small>

View File

@ -616,4 +616,24 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
document.dispatchEvent(new KeyboardEvent('keydown', { key: 't' })) document.dispatchEvent(new KeyboardEvent('keydown', { key: 't' }))
expect(openSpy).toHaveBeenCalled() expect(openSpy).toHaveBeenCalled()
}) })
it('should support an extra button and not apply changes when clicked', () => {
component.items = items
component.icon = 'tag-fill'
component.extraButtonTitle = 'Extra'
component.selectionModel = selectionModel
component.applyOnClose = true
let extraButtonClicked,
applied = false
component.extraButton.subscribe(() => (extraButtonClicked = true))
component.apply.subscribe(() => (applied = true))
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
expect(fixture.debugElement.nativeElement.textContent).toContain('Extra')
component.extraButtonClicked()
expect(extraButtonClicked).toBeTruthy()
expect(applied).toBeFalsy()
})
}) })

View File

@ -437,21 +437,6 @@ export class FilterableDropdownComponent
@Input() @Input()
createRef: (name) => void createRef: (name) => void
creating: boolean = false
@Output()
apply = new EventEmitter<ChangedItems>()
@Output()
opened = new EventEmitter()
get modifierToggleEnabled(): boolean {
return this.manyToOne
? this.selectionModel.selectionSize() > 1 &&
this.selectionModel.getExcludedItems().length == 0
: !this.selectionModel.isNoneSelected()
}
@Input() @Input()
set documentCounts(counts: SelectionDataItem[]) { set documentCounts(counts: SelectionDataItem[]) {
if (counts) { if (counts) {
@ -462,6 +447,27 @@ export class FilterableDropdownComponent
@Input() @Input()
shortcutKey: string shortcutKey: string
@Input()
extraButtonTitle: string
creating: boolean = false
@Output()
apply = new EventEmitter<ChangedItems>()
@Output()
opened = new EventEmitter()
@Output()
extraButton = new EventEmitter<ChangedItems>()
get modifierToggleEnabled(): boolean {
return this.manyToOne
? this.selectionModel.selectionSize() > 1 &&
this.selectionModel.getExcludedItems().length == 0
: !this.selectionModel.isNoneSelected()
}
get name(): string { get name(): string {
return this.title ? this.title.replace(/\s/g, '_').toLowerCase() : null return this.title ? this.title.replace(/\s/g, '_').toLowerCase() : null
} }
@ -641,4 +647,13 @@ export class FilterableDropdownComponent
this.selectionModel.get(item.id) !== ToggleableItemState.Selected this.selectionModel.get(item.id) !== ToggleableItemState.Selected
) )
} }
extraButtonClicked() {
// don't apply changes when clicking the extra button
const applyOnClose = this.applyOnClose
this.applyOnClose = false
this.dropdown.close()
this.extraButton.emit(this.selectionModel.diff())
this.applyOnClose = applyOnClose
}
} }

View File

@ -18,7 +18,7 @@
<div class="ms-n2 me-1"> <div class="ms-n2 me-1">
<i-bs name="grip-vertical"></i-bs> <i-bs name="grip-vertical"></i-bs>
</div> </div>
<h6 class="card-title mb-0" i18n></h6> <h6 class="card-title mb-0"></h6>
</div> </div>
</div> </div>
</div> </div>

View File

@ -90,6 +90,9 @@
(opened)="openCustomFieldsDropdown()" (opened)="openCustomFieldsDropdown()"
[(selectionModel)]="customFieldsSelectionModel" [(selectionModel)]="customFieldsSelectionModel"
[documentCounts]="customFieldDocumentCounts" [documentCounts]="customFieldDocumentCounts"
extraButtonTitle="Set values"
i18n-extraButtonTitle
(extraButton)="setCustomFieldValues($event)"
(apply)="setCustomFields($event)"> (apply)="setCustomFields($event)">
</pngx-filterable-dropdown> </pngx-filterable-dropdown>
} }

View File

@ -1416,4 +1416,55 @@ describe('BulkEditorComponent', () => {
) )
expect(component.customFields).toEqual(customFields.results) expect(component.customFields).toEqual(customFields.results)
}) })
it('should open the bulk edit custom field values dialog with correct parameters', () => {
let modal: NgbModalRef
modalService.activeInstances.subscribe((m) => (modal = m[0]))
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
jest
.spyOn(documentListViewService, 'documents', 'get')
.mockReturnValue([{ id: 3 }, { id: 4 }])
jest.spyOn(documentService, 'getFew').mockReturnValue(
of({
all: [3, 4],
count: 2,
results: [{ id: 3 }, { id: 4 }],
})
)
jest
.spyOn(documentListViewService, 'selected', 'get')
.mockReturnValue(new Set([3, 4]))
fixture.detectChanges()
const toastServiceShowInfoSpy = jest.spyOn(toastService, 'showInfo')
const toastServiceShowErrorSpy = jest.spyOn(toastService, 'showError')
const listReloadSpy = jest.spyOn(documentListViewService, 'reload')
component.customFields = [
{ id: 1, name: 'Custom Field 1', data_type: CustomFieldDataType.String },
{ id: 2, name: 'Custom Field 2', data_type: CustomFieldDataType.String },
]
component.setCustomFieldValues({
itemsToAdd: [{ id: 1 }, { id: 2 }],
itemsToRemove: [1],
} as any)
expect(modal.componentInstance.customFields).toEqual(component.customFields)
expect(modal.componentInstance.fieldsToAddIds).toEqual([1, 2])
expect(modal.componentInstance.documents).toEqual([3, 4])
modal.componentInstance.failed.emit()
expect(toastServiceShowErrorSpy).toHaveBeenCalled()
expect(listReloadSpy).not.toHaveBeenCalled()
modal.componentInstance.succeeded.emit()
expect(toastServiceShowInfoSpy).toHaveBeenCalled()
expect(listReloadSpy).toHaveBeenCalled()
httpTestingController.match(
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true`
) // list reload
httpTestingController.match(
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
) // listAllFilteredIds
})
}) })

View File

@ -44,6 +44,7 @@ import { MergeConfirmDialogComponent } from '../../common/confirm-dialog/merge-c
import { CustomField } from 'src/app/data/custom-field' import { CustomField } from 'src/app/data/custom-field'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component' import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component'
@Component({ @Component({
selector: 'pngx-bulk-editor', selector: 'pngx-bulk-editor',
@ -826,4 +827,38 @@ export class BulkEditorComponent
) )
}) })
} }
public setCustomFieldValues(changedCustomFields: ChangedItems) {
const modal = this.modalService.open(CustomFieldsBulkEditDialogComponent, {
backdrop: 'static',
size: 'lg',
})
const dialog =
modal.componentInstance as CustomFieldsBulkEditDialogComponent
dialog.customFields = this.customFields
dialog.fieldsToAddIds = changedCustomFields.itemsToAdd.map(
(item) => item.id
)
dialog.fieldsToRemoveIds = changedCustomFields.itemsToRemove.map(
(item) => item.id
)
dialog.documents = Array.from(this.list.selected)
dialog.succeeded.subscribe((result) => {
this.toastService.showInfo(
$localize`Bulk operation executed successfully`
)
this.list.reload()
this.list.reduceSelectionToFilter()
this.list.selected.forEach((id) => {
this.openDocumentService.refreshDocument(id)
})
})
dialog.failed.subscribe((error) => {
this.toastService.showError(
$localize`Error executing bulk operation`,
error
)
})
}
} }

View File

@ -0,0 +1,81 @@
<form [formGroup]="form" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title" i8n>{
documents.length,
plural,
=1 {Set custom fields for 1 document} other {Set custom fields for {{documents.length}} documents}
}</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<pngx-input-select i18n-title title="" multiple="true" [items]="customFields" [(ngModel)]="fieldsToAddIds"
placeholder="Select custom fields" i18n-placeholder [ngModelOptions]="{standalone: true}">
</pngx-input-select>
<div class="d-flex flex-column gap-2">
@for (field of fieldsToAdd; track field.id) {
<div class="d-flex gap-2">
@switch (field.data_type) {
@case (CustomFieldDataType.String) {
<pngx-input-text formControlName="{{field.id}}" class="w-100" [title]="field.name" [horizontal]="true">
</pngx-input-text>
}
@case (CustomFieldDataType.Date) {
<pngx-input-date formControlName="{{field.id}}" class="w-100" [title]="field.name" [horizontal]="true">
</pngx-input-date>
}
@case (CustomFieldDataType.Integer) {
<pngx-input-number formControlName="{{field.id}}" class="w-100" [title]="field.name" [showAdd]="false"
[horizontal]="true">
</pngx-input-number>
}
@case (CustomFieldDataType.Float) {
<pngx-input-number formControlName="{{field.id}}" class="w-100" [title]="field.name" [showAdd]="false"
[step]=".1" [horizontal]="true">
</pngx-input-number>
}
@case (CustomFieldDataType.Monetary) {
<pngx-input-monetary formControlName="{{field.id}}" class="w-100" [title]="field.name"
[defaultCurrency]="field.extra_data?.default_currency" [horizontal]="true">
</pngx-input-monetary>
}
@case (CustomFieldDataType.Boolean) {
<pngx-input-check formControlName="{{field.id}}" class="w-100" [title]="field.name" [horizontal]="true">
</pngx-input-check>
}
@case (CustomFieldDataType.Url) {
<pngx-input-url formControlName="{{field.id}}" class="w-100" [title]="field.name" [horizontal]="true">
</pngx-input-url>
}
@case (CustomFieldDataType.DocumentLink) {
<pngx-input-document-link formControlName="{{field.id}}" class="w-100" [title]="field.name" [horizontal]="true">
</pngx-input-document-link>
}
@case (CustomFieldDataType.Select) {
<pngx-input-select formControlName="{{field.id}}" class="w-100" [title]="field.name"
[items]="field.extra_data.select_options" bindLabel="label" [allowNull]="true" [horizontal]="true">
</pngx-input-select>
}
}
<button type="button" class="btn btn-outline-danger mb-3" (click)="removeField(field.id)">
<i-bs name="x"></i-bs>
</button>
</div>
}
</div>
</div>
<div class="modal-footer">
@if (fieldsToRemoveIds.length) {
<p class="mb-0 small"><em i18n>{
fieldsToRemoveIds.length,
plural,
=1 {This operation will also remove 1 custom field from the selected documents.} other {This operation will also
remove {{fieldsToRemoveIds.length}} custom fields from the selected documents.}
}</em></p>
}
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n
[disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n
[disabled]="networkActive || fieldsToRemoveIds.length + fieldsToAddIds.length === 0">Save</button>
</div>
</form>

View File

@ -0,0 +1,89 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { of, throwError } from 'rxjs'
import { DocumentService } from 'src/app/services/rest/document.service'
import { SelectComponent } from 'src/app/components/common/input/select/select.component'
import { CustomFieldDataType } from 'src/app/data/custom-field'
import { NgSelectModule } from '@ng-select/ng-select'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { provideHttpClient } from '@angular/common/http'
describe('CustomFieldsBulkEditDialogComponent', () => {
let component: CustomFieldsBulkEditDialogComponent
let fixture: ComponentFixture<CustomFieldsBulkEditDialogComponent>
let documentService: DocumentService
let activeModal: NgbActiveModal
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [CustomFieldsBulkEditDialogComponent, SelectComponent],
imports: [FormsModule, ReactiveFormsModule, NgbModule, NgSelectModule],
providers: [
NgbActiveModal,
provideHttpClient(),
provideHttpClientTesting(),
],
}).compileComponents()
fixture = TestBed.createComponent(CustomFieldsBulkEditDialogComponent)
component = fixture.componentInstance
documentService = TestBed.inject(DocumentService)
activeModal = TestBed.inject(NgbActiveModal)
fixture.detectChanges()
})
it('should initialize form controls based on selected field ids', () => {
component.customFields = [
{ id: 1, name: 'Field 1', data_type: CustomFieldDataType.String },
{ id: 2, name: 'Field 2', data_type: CustomFieldDataType.Integer },
]
component.fieldsToAddIds = [1, 2]
expect(component.form.contains('1')).toBeTruthy()
expect(component.form.contains('2')).toBeTruthy()
})
it('should emit succeeded event and close modal on successful save', () => {
const editSpy = jest
.spyOn(documentService, 'bulkEdit')
.mockReturnValue(of('Success'))
const successSpy = jest.spyOn(component.succeeded, 'emit')
component.documents = [1, 2]
component.fieldsToAddIds = [1]
component.form.controls['1'].setValue('Value 1')
component.save()
expect(editSpy).toHaveBeenCalled()
expect(successSpy).toHaveBeenCalled()
})
it('should emit failed event on save error', () => {
const editSpy = jest
.spyOn(documentService, 'bulkEdit')
.mockReturnValue(throwError(new Error('Error')))
const failSpy = jest.spyOn(component.failed, 'emit')
component.documents = [1, 2]
component.fieldsToAddIds = [1]
component.form.controls['1'].setValue('Value 1')
component.save()
expect(editSpy).toHaveBeenCalled()
expect(failSpy).toHaveBeenCalled()
})
it('should close modal on cancel', () => {
const activeModalSpy = jest.spyOn(activeModal, 'close')
component.cancel()
expect(activeModalSpy).toHaveBeenCalled()
})
it('should remove field from selected fields', () => {
component.fieldsToAddIds = [1, 2]
component.removeField(1)
expect(component.fieldsToAddIds).toEqual([2])
})
})

View File

@ -0,0 +1,90 @@
import { Component, EventEmitter, Output } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import { DocumentService } from 'src/app/services/rest/document.service'
@Component({
selector: 'pngx-custom-fields-bulk-edit-dialog',
templateUrl: './custom-fields-bulk-edit-dialog.component.html',
styleUrl: './custom-fields-bulk-edit-dialog.component.scss',
})
export class CustomFieldsBulkEditDialogComponent {
CustomFieldDataType = CustomFieldDataType
@Output()
succeeded = new EventEmitter()
@Output()
failed = new EventEmitter()
public networkActive = false
public customFields: CustomField[] = []
private _fieldsToAdd: CustomField[] = [] // static object for change detection
public get fieldsToAdd() {
return this._fieldsToAdd
}
private _fieldsToAddIds: number[] = []
public get fieldsToAddIds() {
return this._fieldsToAddIds
}
public set fieldsToAddIds(ids: number[]) {
this._fieldsToAddIds = ids
this._fieldsToAdd = this.customFields.filter((field) =>
this._fieldsToAddIds.includes(field.id)
)
this.initForm()
}
public fieldsToRemoveIds: number[] = []
public form: FormGroup = new FormGroup({})
public documents: number[] = []
constructor(
private activeModal: NgbActiveModal,
private documentService: DocumentService
) {}
initForm() {
Object.keys(this.form.controls).forEach((key) => {
if (!this._fieldsToAddIds.includes(parseInt(key))) {
this.form.removeControl(key)
}
})
this._fieldsToAddIds.forEach((field_id) => {
this.form.addControl(field_id.toString(), new FormControl(null))
})
}
public save() {
this.documentService
.bulkEdit(this.documents, 'modify_custom_fields', {
add_custom_fields: this.form.value,
remove_custom_fields: this.fieldsToRemoveIds,
})
.pipe(first())
.subscribe({
next: () => {
this.activeModal.close()
this.succeeded.emit()
},
error: (error) => {
this.failed.emit(error)
},
})
}
public cancel() {
this.activeModal.close()
}
public removeField(fieldId: number) {
this.fieldsToAddIds = this._fieldsToAddIds.filter((id) => id !== fieldId)
}
}

View File

@ -17,6 +17,7 @@ from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentMetadataOverrides
from documents.data_models import DocumentSource from documents.data_models import DocumentSource
from documents.models import Correspondent from documents.models import Correspondent
from documents.models import CustomField
from documents.models import CustomFieldInstance from documents.models import CustomFieldInstance
from documents.models import Document from documents.models import Document
from documents.models import DocumentType from documents.models import DocumentType
@ -147,17 +148,34 @@ def modify_tags(
def modify_custom_fields( def modify_custom_fields(
doc_ids: list[int], doc_ids: list[int],
add_custom_fields, add_custom_fields: list[int] | dict,
remove_custom_fields, remove_custom_fields: list[int],
) -> Literal["OK"]: ) -> Literal["OK"]:
qs = Document.objects.filter(id__in=doc_ids).only("pk") qs = Document.objects.filter(id__in=doc_ids).only("pk")
affected_docs = list(qs.values_list("pk", flat=True)) affected_docs = list(qs.values_list("pk", flat=True))
# Ensure add_custom_fields is a list of tuples, supports old API
add_custom_fields = (
add_custom_fields.items()
if isinstance(add_custom_fields, dict)
else [(field, None) for field in add_custom_fields]
)
for field in add_custom_fields: custom_fields = CustomField.objects.filter(
id__in=[int(field) for field, _ in add_custom_fields],
).distinct()
for field_id, value in add_custom_fields:
for doc_id in affected_docs: for doc_id in affected_docs:
defaults = {}
custom_field = custom_fields.get(id=field_id)
if custom_field:
value_field = CustomFieldInstance.TYPE_TO_DATA_STORE_NAME_MAP[
custom_field.data_type
]
defaults[value_field] = value
CustomFieldInstance.objects.update_or_create( CustomFieldInstance.objects.update_or_create(
document_id=doc_id, document_id=doc_id,
field_id=field, field_id=field_id,
defaults=defaults,
) )
CustomFieldInstance.objects.filter( CustomFieldInstance.objects.filter(
document_id__in=affected_docs, document_id__in=affected_docs,

View File

@ -638,7 +638,10 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
uri_validator(data["value"]) uri_validator(data["value"])
elif field.data_type == CustomField.FieldDataType.INT: elif field.data_type == CustomField.FieldDataType.INT:
integer_validator(data["value"]) integer_validator(data["value"])
elif field.data_type == CustomField.FieldDataType.MONETARY: elif (
field.data_type == CustomField.FieldDataType.MONETARY
and data["value"] != ""
):
try: try:
# First try to validate as a number from legacy format # First try to validate as a number from legacy format
DecimalValidator(max_digits=12, decimal_places=2)( DecimalValidator(max_digits=12, decimal_places=2)(
@ -1140,13 +1143,28 @@ class BulkEditSerializer(
f"Some tags in {name} don't exist or were specified twice.", f"Some tags in {name} don't exist or were specified twice.",
) )
def _validate_custom_field_id_list(self, custom_fields, name="custom_fields"): def _validate_custom_field_id_list_or_dict(
if not isinstance(custom_fields, list): self,
raise serializers.ValidationError(f"{name} must be a list") custom_fields,
if not all(isinstance(i, int) for i in custom_fields): name="custom_fields",
raise serializers.ValidationError(f"{name} must be a list of integers") ):
count = CustomField.objects.filter(id__in=custom_fields).count() ids = custom_fields
if not count == len(custom_fields): if isinstance(custom_fields, dict):
try:
ids = [int(i[0]) for i in custom_fields.items()]
except Exception as e:
logger.exception(f"Error validating custom fields: {e}")
raise serializers.ValidationError(
f"{name} must be a list of integers or a dict of id:value pairs, see the log for details",
)
elif not isinstance(custom_fields, list) or not all(
isinstance(i, int) for i in ids
):
raise serializers.ValidationError(
f"{name} must be a list of integers or a dict of id:value pairs",
)
count = CustomField.objects.filter(id__in=ids).count()
if not count == len(ids):
raise serializers.ValidationError( raise serializers.ValidationError(
f"Some custom fields in {name} don't exist or were specified twice.", f"Some custom fields in {name} don't exist or were specified twice.",
) )
@ -1245,7 +1263,7 @@ class BulkEditSerializer(
def _validate_parameters_modify_custom_fields(self, parameters): def _validate_parameters_modify_custom_fields(self, parameters):
if "add_custom_fields" in parameters: if "add_custom_fields" in parameters:
self._validate_custom_field_id_list( self._validate_custom_field_id_list_or_dict(
parameters["add_custom_fields"], parameters["add_custom_fields"],
"add_custom_fields", "add_custom_fields",
) )
@ -1253,7 +1271,7 @@ class BulkEditSerializer(
raise serializers.ValidationError("add_custom_fields not specified") raise serializers.ValidationError("add_custom_fields not specified")
if "remove_custom_fields" in parameters: if "remove_custom_fields" in parameters:
self._validate_custom_field_id_list( self._validate_custom_field_id_list_or_dict(
parameters["remove_custom_fields"], parameters["remove_custom_fields"],
"remove_custom_fields", "remove_custom_fields",
) )

View File

@ -244,7 +244,9 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
"documents": [self.doc1.id, self.doc3.id], "documents": [self.doc1.id, self.doc3.id],
"method": "modify_custom_fields", "method": "modify_custom_fields",
"parameters": { "parameters": {
"add_custom_fields": [self.cf1.id], "add_custom_fields": [
self.cf1.id,
], # old format accepts list of IDs
"remove_custom_fields": [self.cf2.id], "remove_custom_fields": [self.cf2.id],
}, },
}, },
@ -258,6 +260,30 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.assertEqual(kwargs["add_custom_fields"], [self.cf1.id]) self.assertEqual(kwargs["add_custom_fields"], [self.cf1.id])
self.assertEqual(kwargs["remove_custom_fields"], [self.cf2.id]) self.assertEqual(kwargs["remove_custom_fields"], [self.cf2.id])
@mock.patch("documents.serialisers.bulk_edit.modify_custom_fields")
def test_api_modify_custom_fields_with_values(self, m):
self.setup_mock(m, "modify_custom_fields")
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc1.id, self.doc3.id],
"method": "modify_custom_fields",
"parameters": {
"add_custom_fields": {self.cf1.id: "foo"},
"remove_custom_fields": [self.cf2.id],
},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
m.assert_called_once()
args, kwargs = m.call_args
self.assertListEqual(args[0], [self.doc1.id, self.doc3.id])
self.assertEqual(kwargs["add_custom_fields"], {str(self.cf1.id): "foo"})
self.assertEqual(kwargs["remove_custom_fields"], [self.cf2.id])
@mock.patch("documents.serialisers.bulk_edit.modify_custom_fields") @mock.patch("documents.serialisers.bulk_edit.modify_custom_fields")
def test_api_modify_custom_fields_invalid_params(self, m): def test_api_modify_custom_fields_invalid_params(self, m):
""" """
@ -322,7 +348,23 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
m.assert_not_called() m.assert_not_called()
# Not a list of integers # Invalid dict
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc1.id, self.doc3.id],
"method": "modify_custom_fields",
"parameters": {
"add_custom_fields": {"foo": 99},
"remove_custom_fields": [self.cf2.id],
},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
m.assert_not_called()
# Missing remove_custom_fields # Missing remove_custom_fields
response = self.client.post( response = self.client.post(

View File

@ -189,6 +189,15 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
self.assertCountEqual(kwargs["document_ids"], [self.doc2.id, self.doc3.id]) self.assertCountEqual(kwargs["document_ids"], [self.doc2.id, self.doc3.id])
def test_modify_custom_fields(self): def test_modify_custom_fields(self):
"""
GIVEN:
- 2 documents with custom fields
- 3 custom fields
WHEN:
- Custom fields are modified using old format (list of ids)
THEN:
- Custom fields are modified for the documents
"""
cf = CustomField.objects.create( cf = CustomField.objects.create(
name="cf1", name="cf1",
data_type=CustomField.FieldDataType.STRING, data_type=CustomField.FieldDataType.STRING,
@ -235,6 +244,78 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
args, kwargs = self.async_task.call_args args, kwargs = self.async_task.call_args
self.assertCountEqual(kwargs["document_ids"], [self.doc1.id, self.doc2.id]) self.assertCountEqual(kwargs["document_ids"], [self.doc1.id, self.doc2.id])
def test_modify_custom_fields_with_values(self):
"""
GIVEN:
- 2 documents with custom fields
- 3 custom fields
WHEN:
- Custom fields are modified using new format (dict)
THEN:
- Custom fields are modified for the documents
"""
cf = CustomField.objects.create(
name="cf",
data_type=CustomField.FieldDataType.STRING,
)
cf1 = CustomField.objects.create(
name="cf1",
data_type=CustomField.FieldDataType.STRING,
)
cf2 = CustomField.objects.create(
name="cf2",
data_type=CustomField.FieldDataType.MONETARY,
)
cf3 = CustomField.objects.create(
name="cf3",
data_type=CustomField.FieldDataType.STRING,
)
CustomFieldInstance.objects.create(
document=self.doc2,
field=cf,
)
CustomFieldInstance.objects.create(
document=self.doc2,
field=cf1,
)
CustomFieldInstance.objects.create(
document=self.doc2,
field=cf3,
)
bulk_edit.modify_custom_fields(
[self.doc1.id, self.doc2.id],
add_custom_fields={cf2.id: None, cf3.id: "value"},
remove_custom_fields=[cf.id],
)
self.doc1.refresh_from_db()
self.doc2.refresh_from_db()
self.assertEqual(
self.doc1.custom_fields.count(),
2,
)
self.assertEqual(
self.doc1.custom_fields.get(field=cf2).value,
None,
)
self.assertEqual(
self.doc1.custom_fields.get(field=cf3).value,
"value",
)
self.assertEqual(
self.doc2.custom_fields.count(),
3,
)
self.assertEqual(
self.doc2.custom_fields.get(field=cf3).value,
"value",
)
self.async_task.assert_called_once()
args, kwargs = self.async_task.call_args
self.assertCountEqual(kwargs["document_ids"], [self.doc1.id, self.doc2.id])
def test_delete(self): def test_delete(self):
self.assertEqual(Document.objects.count(), 5) self.assertEqual(Document.objects.count(), 5)
bulk_edit.delete([self.doc1.id, self.doc2.id]) bulk_edit.delete([self.doc1.id, self.doc2.id])