]> git.ipfire.org Git - thirdparty/paperless-ngx.git/blob
7cbb6b6f27a0d81f6d6574739640caade6f1b67c
[thirdparty/paperless-ngx.git] /
1 <form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
2 <div class="modal-header">
3 <h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
4 @if (object?.id) {
5 <span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
6 }
7 <button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
8 </button>
9 </div>
10 <div class="modal-body">
11 <div class="row">
12 <div class="col-md-6">
13 <pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
14 </div>
15 <div class="col-4">
16 <pngx-input-number i18n-title title="Sort order" formControlName="order" [showAdd]="false" [error]="error?.order"></pngx-input-number>
17 </div>
18 <div class="col">
19 <pngx-input-switch i18n-title title="Enabled" formControlName="enabled" [error]="error?.enabled"></pngx-input-switch>
20 </div>
21 </div>
22 <div ngbAccordion>
23 <div ngbAccordionItem>
24 <h2 ngbAccordionHeader>
25 <button ngbAccordionButton i18n>Triggers</button>
26 </h2>
27 <div ngbAccordionCollapse>
28 <div ngbAccordionBody>
29 <ng-template>
30 <div class="d-flex">
31 <p class="p-2" i18n>Trigger Workflow On:</p>
32 <button type="button" class="btn btn-sm btn-outline-primary ms-auto mb-3" (click)="addTrigger()">
33 <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Trigger</ng-container>
34 </button>
35 </div>
36 <div ngbAccordion [closeOthers]="true">
37 @for (trigger of object?.triggers; track trigger; let i = $index){
38 <div ngbAccordionItem>
39 <div ngbAccordionHeader>
40 <button ngbAccordionButton>{{i + 1}}. {{getTriggerTypeOptionName(triggerFields.controls[i].value.type)}}
41 @if(trigger.id) {
42 <span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{trigger.id}}</span>
43 }
44 <pngx-confirm-button
45 label="Delete"
46 i18n-label
47 (confirm)="removeTrigger(i)"
48 buttonClasses="btn-link text-danger ms-2"
49 iconName="trash">
50 </pngx-confirm-button>
51 </button>
52 </div>
53 <div ngbAccordionCollapse>
54 <div ngbAccordionBody>
55 <ng-template [ngTemplateOutlet]="triggerForm" [ngTemplateOutletContext]="{ formGroup: triggerFields.controls[i], trigger: trigger }"></ng-template>
56 </div>
57 </div>
58 </div>
59 }
60 </div>
61 </ng-template>
62 </div>
63 </div>
64 </div>
65 <div ngbAccordionItem>
66 <h2 ngbAccordionHeader>
67 <button class="btn-lg" ngbAccordionButton i18n>Actions</button>
68 </h2>
69 <div ngbAccordionCollapse>
70 <div ngbAccordionBody>
71 <ng-template>
72 <div class="d-flex">
73 <p class="p-2" i18n>Apply Actions:</p>
74 <button type="button" class="btn btn-sm btn-outline-primary ms-auto mb-3" (click)="addAction()">
75 <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Action</ng-container>
76 </button>
77 </div>
78 <div ngbAccordion [closeOthers]="true" cdkDropList (cdkDropListDropped)="onActionDrop($event)">
79 @for (action of object?.actions; track action; let i = $index){
80 <div ngbAccordionItem cdkDrag [formGroup]="actionFields.controls[i]">
81 <div ngbAccordionHeader>
82 <button ngbAccordionButton>{{i + 1}}. {{getActionTypeOptionName(actionFields.controls[i].value.type)}}
83 @if(action.id) {
84 <span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{action.id}}</span>
85 }
86 <pngx-confirm-button
87 label="Delete"
88 i18n-label
89 (confirm)="removeAction(i)"
90 buttonClasses="btn-link text-danger ms-2"
91 iconName="trash">
92 </pngx-confirm-button>
93 </button>
94 </div>
95 <div ngbAccordionCollapse>
96 <div ngbAccordionBody>
97 <ng-template [ngTemplateOutlet]="actionForm" [ngTemplateOutletContext]="{ formGroup: actionFields.controls[i], action: action }"></ng-template>
98 </div>
99 </div>
100 </div>
101 }
102 </div>
103 </ng-template>
104 </div>
105 </div>
106 </div>
107 </div>
108 </div>
109 <div class="modal-footer">
110 @if (error?.non_field_errors) {
111 <span class="text-danger"><ng-container i18n>Error</ng-container>: {{error.non_field_errors}}</span>
112 }
113 <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
114 <button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
115 </div>
116 </form>
117
118 <ng-template #triggerForm let-formGroup="formGroup" let-trigger="trigger">
119 <div [formGroup]="formGroup">
120 <input type="hidden" formControlName="id" />
121 <pngx-input-select i18n-title title="Trigger type" [horizontal]="true" [items]="triggerTypeOptions" formControlName="type"></pngx-input-select>
122 @if (formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
123 <p class="small" i18n>Set scheduled trigger offset and which date field to use.</p>
124 <div class="row">
125 <div class="col-4">
126 <pngx-input-number i18n-title title="Offset days" formControlName="schedule_offset_days" [showAdd]="false" [error]="error?.schedule_offset_days"></pngx-input-number>
127 </div>
128 <div class="col-4">
129 <pngx-input-select i18n-title title="Relative to" formControlName="schedule_date_field" [items]="scheduleDateFieldOptions" [error]="error?.schedule_date_field"></pngx-input-select>
130 </div>
131 @if (formGroup.get('schedule_date_field').value === 'custom_field') {
132 <div class="col-4">
133 <pngx-input-select i18n-title title="Custom field" formControlName="schedule_date_custom_field" [items]="dateCustomFields" i18n-hint hint="Custom field to use for date." [error]="error?.schedule_date_custom_field"></pngx-input-select>
134 </div>
135 }
136 </div>
137 <div class="row">
138 <div class="col-4">
139 <pngx-input-check i18n-title title="Recurring" formControlName="schedule_is_recurring" i18n-hint hint="Trigger is recurring." [error]="error?.schedule_is_recurring"></pngx-input-check>
140 </div>
141 <div class="col-4">
142 @if (formGroup.get('schedule_is_recurring').value === true) {
143 <pngx-input-number i18n-title title="Recurring interval days" formControlName="schedule_recurring_interval_days" i18n-hint hint="Repeat the trigger every n days." [showAdd]="false" [error]="error?.schedule_recurring_interval_days"></pngx-input-number>
144 }
145 </div>
146 </div>
147 }
148 <p class="small" i18n>Trigger for documents that match <em>all</em> filters specified below.</p>
149 <div class="row">
150 <div class="col">
151 <pngx-input-text i18n-title title="Filter filename" formControlName="filter_filename" i18n-hint hint="Apply to documents that match this filename. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive." [error]="error?.filter_filename"></pngx-input-text>
152 @if (formGroup.get('type').value === WorkflowTriggerType.Consumption) {
153 <pngx-input-select i18n-title title="Filter sources" [items]="sourceOptions" [multiple]="true" formControlName="sources" [error]="error?.sources"></pngx-input-select>
154 <pngx-input-text i18n-title title="Filter path" formControlName="filter_path" i18n-hint hint="Apply to documents that match this path. Wildcards specified as * are allowed. Case-normalized.</a>" [error]="error?.filter_path"></pngx-input-text>
155 <pngx-input-select i18n-title title="Filter mail rule" [items]="mailRules" [allowNull]="true" formControlName="filter_mailrule" i18n-hint hint="Apply to documents consumed via this mail rule." [error]="error?.filter_mailrule"></pngx-input-select>
156 }
157 @if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
158 <pngx-input-select i18n-title title="Content matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
159 @if (patternRequired) {
160 <pngx-input-text i18n-title title="Content matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
161 }
162 @if (patternRequired) {
163 <pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
164 }
165 }
166 </div>
167 @if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
168 <div class="col-md-6">
169 <pngx-input-tags [allowCreate]="false" i18n-title title="Has any of tags" formControlName="filter_has_tags"></pngx-input-tags>
170 <pngx-input-select i18n-title title="Has correspondent" [items]="correspondents" [allowNull]="true" formControlName="filter_has_correspondent"></pngx-input-select>
171 <pngx-input-select i18n-title title="Has document type" [items]="documentTypes" [allowNull]="true" formControlName="filter_has_document_type"></pngx-input-select>
172 </div>
173 }
174 </div>
175 </div>
176 </ng-template>
177
178 <ng-template #actionForm let-formGroup="formGroup" let action="action">
179 <div [formGroup]="formGroup">
180 <input type="hidden" formControlName="id" />
181 <pngx-input-select i18n-title title="Action type" [horizontal]="true" [items]="actionTypeOptions" formControlName="type"></pngx-input-select>
182 @switch(formGroup.get('type').value) {
183 @case ( WorkflowActionType.Assignment) {
184 <div class="row">
185 <div class="col">
186 <pngx-input-text i18n-title title="Assign title" formControlName="assign_title" i18n-hint hint="Can include some placeholders, see <a target='_blank' href='https://docs.paperless-ngx.com/usage/#workflows'>documentation</a>." [error]="error?.actions?.[i]?.assign_title"></pngx-input-text>
187 <pngx-input-tags [allowCreate]="false" i18n-title title="Assign tags" formControlName="assign_tags"></pngx-input-tags>
188 <pngx-input-select i18n-title title="Assign document type" [items]="documentTypes" [allowNull]="true" formControlName="assign_document_type"></pngx-input-select>
189 <pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
190 <pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
191 <pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
192 </div>
193 <div class="col">
194 <pngx-input-select i18n-title title="Assign owner" [items]="users" bindLabel="username" formControlName="assign_owner" [allowNull]="true"></pngx-input-select>
195 <div>
196 <label class="form-label" i18n>Assign view permissions</label>
197 <div class="mb-2">
198 <div class="row mb-1">
199 <div class="col-lg-3">
200 <label class="form-label d-block my-2 text-nowrap" i18n>Users:</label>
201 </div>
202 <div class="col-lg-9">
203 <pngx-permissions-user type="view" formControlName="assign_view_users"></pngx-permissions-user>
204 </div>
205 </div>
206 <div class="row">
207 <div class="col-lg-3">
208 <label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
209 </div>
210 <div class="col-lg-9">
211 <pngx-permissions-group type="view" formControlName="assign_view_groups"></pngx-permissions-group>
212 </div>
213 </div>
214 </div>
215 <label class="form-label" i18n>Assign edit permissions</label>
216 <div>
217 <div class="row mb-1">
218 <div class="col-lg-3">
219 <label class="form-label d-block my-2 text-nowrap" i18n>Users:</label>
220 </div>
221 <div class="col-lg-9">
222 <pngx-permissions-user type="change" formControlName="assign_change_users"></pngx-permissions-user>
223 </div>
224 </div>
225 <div class="row">
226 <div class="col-lg-3">
227 <label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
228 </div>
229 <div class="col-lg-9">
230 <pngx-permissions-group type="change" formControlName="assign_change_groups"></pngx-permissions-group>
231 </div>
232 </div>
233 <small class="form-text text-muted text-end d-block" i18n>Edit permissions also grant viewing permissions</small>
234 </div>
235 </div>
236 </div>
237 </div>
238 }
239 @case (WorkflowActionType.Removal) {
240 <div class="row">
241 <div class="col">
242 <h6 class="form-label" i18n>Remove tags</h6>
243 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_tags"></pngx-input-switch>
244 <div class="mt-n3">
245 <pngx-input-tags [allowCreate]="false" title="" formControlName="remove_tags"></pngx-input-tags>
246 </div>
247
248 <h6 class="form-label" i18n>Remove correspondents</h6>
249 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_correspondents"></pngx-input-switch>
250 <div class="mt-n3">
251 <pngx-input-select i18n-title title="" multiple="true" [items]="correspondents" formControlName="remove_correspondents"></pngx-input-select>
252 </div>
253
254 <h6 class="form-label" i18n>Remove document types</h6>
255 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_document_types"></pngx-input-switch>
256 <div class="mt-n3">
257 <pngx-input-select i18n-title title="" multiple="true" [items]="documentTypes" formControlName="remove_document_types"></pngx-input-select>
258 </div>
259
260 <h6 class="form-label" i18n>Remove storage paths</h6>
261 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_storage_paths"></pngx-input-switch>
262 <div class="mt-n3">
263 <pngx-input-select i18n-title title="" multiple="true" [items]="storagePaths" formControlName="remove_storage_paths"></pngx-input-select>
264 </div>
265
266 <h6 class="form-label" i18n>Remove custom fields</h6>
267 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_custom_fields"></pngx-input-switch>
268 <div class="mt-n3">
269 <pngx-input-select i18n-title title="" multiple="true" [items]="customFields" formControlName="remove_custom_fields"></pngx-input-select>
270 </div>
271 </div>
272 <div class="col">
273 <h6 class="form-label" i18n>Remove owners</h6>
274 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_owners"></pngx-input-switch>
275 <div class="mt-n3">
276 <pngx-input-select i18n-title title="" multiple="true" [items]="users" bindLabel="username" formControlName="remove_owners"></pngx-input-select>
277 </div>
278
279 <h6 class="form-label" i18n>Remove permissions</h6>
280 <pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_permissions"></pngx-input-switch>
281 <div>
282 <label class="form-label" i18n>View permissions</label>
283 <div class="mb-2">
284 <div class="row mb-1">
285 <div class="col-lg-3">
286 <label class="form-label d-block my-2 text-nowrap" i18n>Users:</label>
287 </div>
288 <div class="col-lg-9">
289 <pngx-permissions-user type="view" formControlName="remove_view_users"></pngx-permissions-user>
290 </div>
291 </div>
292 <div class="row">
293 <div class="col-lg-3">
294 <label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
295 </div>
296 <div class="col-lg-9">
297 <pngx-permissions-group type="view" formControlName="remove_view_groups"></pngx-permissions-group>
298 </div>
299 </div>
300 </div>
301 <label class="form-label" i18n>Edit permissions</label>
302 <div>
303 <div class="row mb-1">
304 <div class="col-lg-3">
305 <label class="form-label d-block my-2 text-nowrap" i18n>Users:</label>
306 </div>
307 <div class="col-lg-9">
308 <pngx-permissions-user type="change" formControlName="remove_change_users"></pngx-permissions-user>
309 </div>
310 </div>
311 <div class="row">
312 <div class="col-lg-3">
313 <label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
314 </div>
315 <div class="col-lg-9">
316 <pngx-permissions-group type="change" formControlName="remove_change_groups"></pngx-permissions-group>
317 </div>
318 </div>
319 <small class="form-text text-muted text-end d-block" i18n>Edit permissions also grant viewing permissions</small>
320 </div>
321 </div>
322 </div>
323 </div>
324 }
325 @case (WorkflowActionType.Email) {
326 <div class="row" [formGroup]="formGroup.get('email')">
327 <input type="hidden" formControlName="id" />
328 <div class="col">
329 <pngx-input-text i18n-title title="Email subject" formControlName="subject" [error]="error?.actions?.[i]?.email?.subject"></pngx-input-text>
330 <pngx-input-textarea i18n-title title="Email body" formControlName="body" [error]="error?.actions?.[i]?.email?.body"></pngx-input-textarea>
331 <pngx-input-text i18n-title title="Email recipients" formControlName="to" [error]="error?.actions?.[i]?.email?.to"></pngx-input-text>
332 <pngx-input-switch i18n-title title="Attach document" formControlName="include_document"></pngx-input-switch>
333 </div>
334 </div>
335 }
336 @case (WorkflowActionType.Webhook) {
337 <div class="row" [formGroup]="formGroup.get('webhook')">
338 <input type="hidden" formControlName="id" />
339 <div class="col">
340 <pngx-input-text i18n-title title="Webhook url" formControlName="url" [error]="error?.actions?.[i]?.url"></pngx-input-text>
341 <pngx-input-switch i18n-title title="Use parameters for webhook body" formControlName="use_params"></pngx-input-switch>
342 @if (formGroup.get('webhook').value['use_params']) {
343 <pngx-input-entries i18n-title title="Webhook params" formControlName="params" [error]="error?.actions?.[i]?.params"></pngx-input-entries>
344 } @else {
345 <pngx-input-textarea i18n-title title="Webhook body" formControlName="body" [error]="error?.actions?.[i]?.body"></pngx-input-textarea>
346 }
347 <pngx-input-entries i18n-title title="Webhook headers" formControlName="headers" [error]="error?.actions?.[i]?.headers"></pngx-input-entries>
348 <pngx-input-switch i18n-title title="Include document" formControlName="include_document"></pngx-input-switch>
349 </div>
350 </div>
351 }
352 }
353 </div>
354 </ng-template>