BrightSide Workbench Full Report + Source Code
DocumentationVM.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2022 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 package org.turro.documentation.model;
20 
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.List;
25 import org.turro.string.Strings;
26 import org.turro.action.MailSenders;
27 import org.turro.action.PushSenders;
28 import org.turro.assistant.AssistantListbox;
29 import org.turro.attach.db.AttachPU;
30 import org.turro.attach.entity.EntityDocumentation;
31 import org.turro.attach.entity.EntityDocumentationStatus;
32 import org.turro.auth.Authentication;
33 import org.turro.documentation.DocumentDefinition;
34 import org.turro.elephant.context.ElephantContext;
35 import org.turro.elephant.util.Messages;
36 import org.turro.entities.Entities;
37 import org.turro.entities.EntityRole;
38 import org.turro.entities.IElephantEntity;
39 import org.turro.file.Document;
40 import org.turro.file.FileWrapper;
41 import org.turro.file.Folder;
42 import org.turro.i18n.I_;
43 import org.turro.jpa.Dao;
44 import org.turro.log.SystemLogger;
45 import org.turro.plugin.contacts.IContact;
46 import org.turro.sql.SqlClause;
47 import org.turro.upload.Medias;
48 import org.turro.zkoss.dialog.DialogField;
49 import org.turro.zkoss.dialog.Dialogs;
50 import org.zkoss.bind.BindUtils;
51 import org.zkoss.bind.annotation.BindingParam;
52 import org.zkoss.bind.annotation.Command;
53 import org.zkoss.bind.annotation.ExecutionArgParam;
54 import org.zkoss.bind.annotation.Init;
55 import org.zkoss.bind.annotation.NotifyChange;
56 import org.zkoss.util.media.Media;
57 import org.zkoss.zk.ui.Executions;
58 import org.zkoss.zul.Datebox;
59 import org.zkoss.zul.Label;
60 import org.zkoss.zul.Space;
61 import org.zkoss.zul.Textbox;
62 
67 public class DocumentationVM {
68 
69  private IElephantEntity iee;
70  private List<String> contactIds;
71  private IContact contact;
72  private boolean canCreate;
73  private List<EntityDocumentation> model;
74 
75  @Init
76  public void init(@ExecutionArgParam("entityPath") String entityPath) {
77  if(Strings.isBlank(entityPath)) {
78  entityPath = Executions.getCurrent().getParameter("entityPath");
79  }
80  if(!Strings.isBlank(entityPath)) {
81  iee = Entities.getController(entityPath);
82  } else {
83  iee = Entities.emptyController();
84  }
85  }
86 
88  return iee;
89  }
90 
91  public boolean isCanCreate() {
92  return canCreate;
93  }
94 
95  @Command("add")
96  public void add(@BindingParam("document") EntityDocumentation document) {
97  DocumentDefinition dd = document.getDefinition();
98  if(dd.isAllowMultiple()) {
99  Dialogs.title(I_.get("Add contact"))
100  .width("400px")
101  .height("150px")
102  .addField(DialogField.field("Request to").onEditor(() -> {
103  AssistantListbox al = new AssistantListbox();
104  al.setWebUsers(true);
105  al.setEntityPath(iee.getPath());
106  al.setMold("select");
107  return al;
108  }))
109  .onOk((dialogs) -> {
110  IContact require = dialogs.<AssistantListbox>getEditor("Request to").getObjectValue();
111  if(require != null && require.isValid()) {
112  document.getAssistantIds().add(require.getId());
113  getDao().saveObject(document);
114  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
117  .addContact(require)
118  .send(I_.get("Documentation"), I_.get("Pending upload documents"), document.getEntity().getEntityUrl());
119  MailSenders.getPool().setRoot("/documentation")
121  .addContact(require)
122  .silentSendTemplate(document, "required", I_.get("Pending upload documents"));
123  }
124  })
125  .emptyCancel()
126  .show();
127  }
128  }
129 
130  @NotifyChange("model")
131  @Command("upload")
132  public void upload(@BindingParam("document") EntityDocumentation document, @BindingParam("medias") Media medias[]) {
133  DocumentDefinition dd = document.getDefinition();
134  Folder folder = document.getFolder();
135  long count = folder.count();
136  int done = 0;
137  for(Media media : medias) {
138  if(++count > dd.getMax() && !exists(folder, media)) break;
139  if(!dd.isUploadControl() || !guessOverrides(folder, media)) {
140  Medias.toFolder(media, folder);
141  done++;
142  } else {
143  Messages.error().format(I_.get("Download current %s version first!"), media.getName()).show();
144  }
145  }
146  if(count > dd.getMin()) {
147  document.setStatus(EntityDocumentationStatus.PENDING_VALIDATION);
148  }
149  document.reviewStatus();
150  getDao().saveObject(document);
151  if(done > 0) {
154  .addContact(document.getRequester())
155  .send(I_.get("Documentation"), I_.get("Pending to validate"), document.getEntity().getEntityUrl());
156  MailSenders.getPool().setRoot("/documentation")
158  .addContact(document.getRequester())
159  .silentSendTemplate(document, "uploaded", I_.get("Pending to validate"));
160  }
161  }
162 
163  @Command("download")
164  public void download(@BindingParam("document") Document document) {
165  new FileWrapper(document.document()).download();
166  }
167 
168  @Command("deleteDoc")
169  public void deleteDoc(@BindingParam("document") Document document) {
170  Messages.confirmDeletion().add(document.name()).show(() -> {
171  document.delete();
172  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
173  });
174  }
175 
176  @NotifyChange("model")
177  @Command("validate")
178  public void validate(@BindingParam("document") EntityDocumentation document) {
179  document.setStatus(EntityDocumentationStatus.VALIDATED);
180  getDao().saveObject(document);
183  .addContact(document.getContact())
184  .addContacts(document.getAssistants())
185  .send(I_.get("Documentation"), I_.get("Validated"), document.getEntity().getEntityUrl());
186  MailSenders.getPool().setRoot("/documentation")
188  .addContact(document.getContact())
189  .addContacts(document.getAssistants())
190  .silentSendTemplate(document, "validated", I_.get("Validated"));
191  }
192 
193  @Command("deny")
194  public void deny(@BindingParam("document") EntityDocumentation document) {
195  Dialogs.title(I_.get("Deny"))
196  .width("400px")
197  .height("250px")
198  .addField(DialogField.field("Reason").scale(2))
199  .onOk((dialogs) -> {
200  String comment = dialogs.<Textbox>getEditor("Reason").getValue();
201  document.setStatus(EntityDocumentationStatus.DENIED);
202  document.setComment(comment);
203  getDao().saveObject(document);
204  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
207  .addContact(document.getContact())
208  .addContacts(document.getAssistants())
209  .send(I_.get("Documentation"), I_.get("Denied"), document.getEntity().getEntityUrl());
210  MailSenders.getPool().setRoot("/documentation")
212  .addContact(document.getContact())
213  .addContacts(document.getAssistants())
214  .silentSendTemplate(document, "denied", I_.get("Denied") + ": " + document.getComment());
215  })
216  .emptyCancel()
217  .show();
218  }
219 
220  @Command("delete")
221  public void delete(@BindingParam("document") EntityDocumentation document) {
222  Messages.confirmDeletion().add(document.getDocumentation()).show(() -> {
223  document.getFolder().remove();
224  getDao().deleteObject(document);
225  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
226  });
227  }
228 
229  @Command("info")
230  public void info(@BindingParam("document") DocumentDefinition document) {
231  Dialogs.title(I_.get("Information"))
232  .width("400px")
233  .height("300px")
234  .addComponent(new Label(document.getDescription()))
235  .addComponent(new Space())
236  .addComponent(new DownloadDocuments(I_.get("References"), document.getDocumentsFor(contact)))
237  .emptyClose()
238  .show();
239  }
240 
241  @Command("create")
242  public void create() {
243  Dialogs.title(I_.get("Request new documentation"))
244  .width("400px")
245  .height("500px")
246  .addField(DialogField.field("Request to").onEditor(() -> {
247  AssistantListbox al = new AssistantListbox();
248  al.setWebUsers(true);
249  al.setEntityPath(iee.getPath());
250  al.setMold("select");
251  return al;
252  }))
253  .addField(DialogField.field("Deadline").onEditor(() -> {
254  Datebox dl = new Datebox();
255  dl.setFormat("short");
256  return dl;
257  }))
258  .addField(DialogField.field("Documentation").onEditor(() -> {
259  DocumentationListbox dl = new DocumentationListbox(iee.getRoot());
260  dl.setSelectFirst(false);
261  dl.setCheckmark(true);
262  dl.setMultiple(true);
263  return dl;
264  }))
265  .onOk((dialogs) -> {
266  Collection<DocumentDefinition> dds = dialogs.<DocumentationListbox>getEditor("Documentation").getObjectValues();
267  if(!dds.isEmpty()) {
268  IContact require = dialogs.<AssistantListbox>getEditor("Request to").getObjectValue();
269  if(require != null && require.isValid()) {
270  List<EntityDocumentation> eds = new ArrayList<>();
271  dds.forEach(dd -> {
274  ed.setEntityPath(iee.getPath());
275  ed.setContactId(require.getId());
276  ed.setDocumentation(dd.getName());
278  ed.setDeadline(dialogs.<Datebox>getEditor("Deadline").getValue());
279  eds.add(ed);
280  });
281  getDao().saveCollection(eds);
282  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
283  EntityDocumentation document = eds.iterator().next();
286  .addContact(require)
287  .send(I_.get("Documentation"), I_.get("Pending upload documents"), document.getEntity().getEntityUrl());
288  MailSenders.getPool().setRoot("/documentation")
290  .addContact(require)
291  .silentSendTemplate(document, "required", I_.get("Pending upload documents"));
292  }
293  }
294  })
295  .emptyCancel()
296  .show();
297  }
298 
299  private String selectedType;
300 
301  public String getSelectedType() {
302  return selectedType;
303  }
304 
305  public void setSelectedType(String selectedType) {
306  this.selectedType = selectedType;
307  BindUtils.postNotifyChange(null, null, DocumentationVM.this, "model");
308  }
309 
310  public List<String> getDocumentationTypes() {
311  List<String> types = new ArrayList<>();
312  types.add("");
313  types.addAll(model.stream().map(ed -> ed.getDefinition().getName()).distinct().toList());
314  return types;
315  }
316 
317  public List<EntityDocumentation> getModel() {
318  prepareValues();
319  String orderBy = canCreate ? "ed.status, ed.entityPath, ed.documentation" :
320  "ed.documentation";
321  model = SqlClause.select("ed")
322  .from("EntityDocumentation ed")
323  .where().any()
324  .startIf(!canCreate)
325  .and().group()
326  .and().in("ed.contactId", contactIds)
327  .or().equal("ed.requesterId", contact.getId())
328  .or().members("ed.assistantIds", contactIds)
329  .endGroup()
330  .endIf()
331  .startIf(!iee.isEmpty())
332  .and().equal("ed.entityPath", iee.getPath())
333  .endIf()
334  .startIf(iee.isEmpty())
335  .and().not().equal("ed.status", EntityDocumentationStatus.VALIDATED)
336  .endIf()
337  .orderBy(orderBy)
338  .dao(getDao())
339  .resultList(EntityDocumentation.class);
340  List<EntityDocumentation> invalids = new ArrayList<>();
341  model.removeIf(ed -> !ed.isValid(invalids));
342  if(!invalids.isEmpty()) getDao().deleteCollection(invalids);
343  model.forEach(ed -> ed.reviewStatus());
344  getDao().saveCollection(model);
345  if(!Strings.isBlank(selectedType)) {
346  model.removeIf(ed -> !selectedType.equals(ed.getDefinition().getName()));
347  }
348  return model;
349  }
350 
351  private void prepareValues() {
352  if(contact == null) {
353  canCreate = false;
354  contactIds = null;
355  contact = Authentication.getIContact();
356  if(!iee.isEmpty()) {
357  canCreate = contact.isAdmin() || iee.hasRelatedRole(EntityRole.DOCUMENTATION, contact);
358  }
359  if(!contact.isAdmin()) {
360  contactIds = new ArrayList<>();
361  contactIds.add(contact.getId());
362  contact.getBusinessList().forEach(b -> contactIds.add(b.getId()));
363  }
364  }
365  }
366 
367  private boolean exists(Folder folder, Media media) {
368  String correctedName = Document.correctName(media.getName());
369  Document document = Document.from(folder.path().resolve(correctedName));
370  return document.exists();
371  }
372 
373  private boolean guessOverrides(Folder folder, Media media) {
374  String correctedName = Document.correctName(media.getName());
375  Document document = Document.from(folder.path().resolve(correctedName));
376  if(document.exists()) {
377  String entityPath = "/file" + ElephantContext.getRelativePath(document.path().toString());
378  Date lastDownload = SystemLogger.getInstance().getLastDateOf(entityPath, "downloaded");
379  return lastDownload == null || document.modified().after(lastDownload);
380  }
381  return false;
382  }
383 
384  /* Dao */
385 
386  private Dao _dao;
387 
388  private Dao getDao() {
389  if(_dao == null) {
390  _dao = new AttachPU();
391  }
392  return _dao;
393  }
394 
395 }
static IMailSender getPool()
static IPushSender getPool()
void setStatus(EntityDocumentationStatus status)
void deleteDoc(@BindingParam("document") Document document)
void upload(@BindingParam("document") EntityDocumentation document, @BindingParam("medias") Media medias[])
void deny(@BindingParam("document") EntityDocumentation document)
void delete(@BindingParam("document") EntityDocumentation document)
void validate(@BindingParam("document") EntityDocumentation document)
void info(@BindingParam("document") DocumentDefinition document)
void init(@ExecutionArgParam("entityPath") String entityPath)
void download(@BindingParam("document") Document document)
void add(@BindingParam("document") EntityDocumentation document)
Messages format(String word, Object... args)
Definition: Messages.java:70
static Messages confirmDeletion()
Definition: Messages.java:87
Messages add(String word)
Definition: Messages.java:50
static IElephantEntity getController(String path)
Definition: Entities.java:78
static IElephantEntity emptyController()
Definition: Entities.java:74
static String get(String msg)
Definition: I_.java:41
void deleteObject(Object obj)
Definition: Dao.java:162
void saveCollection(Collection objs)
Definition: Dao.java:144
void deleteCollection(Collection objs)
Definition: Dao.java:175
static void toFolder(Media media, Folder folder)
Definition: Medias.java:50
static DialogField field(String label)
Dialogs width(String width)
Definition: Dialogs.java:70
Dialogs height(String height)
Definition: Dialogs.java:75
Dialogs onOk(Consumer< Dialogs > onOk)
Definition: Dialogs.java:85
static Dialogs title(String title)
Definition: Dialogs.java:161
Dialogs addComponent(HtmlBasedComponent component)
Definition: Dialogs.java:115
Dialogs addField(DialogField field)
Definition: Dialogs.java:110
IMailSender setRoot(String root)
boolean hasRelatedRole(EntityRole role, IContact contact)
List< IContact > getBusinessList()