refreshing RegistrationWorkingsetPresenter on popup editor close
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / vaadin / view / registration / RegistrationWorkingsetPresenter.java
1 /**
2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.vaadin.view.registration;
10
11 import java.util.ArrayList;
12 import java.util.EnumSet;
13 import java.util.List;
14
15 import org.springframework.beans.factory.annotation.Autowired;
16 import org.springframework.context.event.EventListener;
17 import org.springframework.security.core.Authentication;
18 import org.springframework.transaction.TransactionStatus;
19
20 import com.vaadin.spring.annotation.SpringComponent;
21 import com.vaadin.spring.annotation.ViewScope;
22 import com.vaadin.ui.Label;
23 import com.vaadin.ui.UI;
24 import com.vaadin.ui.VerticalLayout;
25 import com.vaadin.ui.Window;
26
27 import eu.etaxonomy.cdm.api.service.INameService;
28 import eu.etaxonomy.cdm.api.service.IRegistrationService;
29 import eu.etaxonomy.cdm.api.service.idminter.IdentifierMinter.Identifier;
30 import eu.etaxonomy.cdm.api.service.idminter.RegistrationIdentifierMinter;
31 import eu.etaxonomy.cdm.model.common.User;
32 import eu.etaxonomy.cdm.model.name.Rank;
33 import eu.etaxonomy.cdm.model.name.Registration;
34 import eu.etaxonomy.cdm.model.name.RegistrationStatus;
35 import eu.etaxonomy.cdm.model.name.TaxonName;
36 import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
37 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
38 import eu.etaxonomy.cdm.model.reference.Reference;
39 import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
40 import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;
41 import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
42 import eu.etaxonomy.cdm.service.CdmStore;
43 import eu.etaxonomy.cdm.service.IRegistrationWorkingSetService;
44 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
45 import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction;
46 import eu.etaxonomy.cdm.vaadin.event.RegistrationEditorAction;
47 import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEvent;
48 import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction;
49 import eu.etaxonomy.cdm.vaadin.event.TypeDesignationWorkingsetEditorAction;
50 import eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction;
51 import eu.etaxonomy.cdm.vaadin.model.EntityReference;
52 import eu.etaxonomy.cdm.vaadin.model.registration.RegistrationWorkingSet;
53 import eu.etaxonomy.cdm.vaadin.security.UserHelper;
54 import eu.etaxonomy.cdm.vaadin.ui.RegistrationUIDefaults;
55 import eu.etaxonomy.cdm.vaadin.util.CdmTitleCacheCaptionGenerator;
56 import eu.etaxonomy.cdm.vaadin.util.converter.TypeDesignationSetManager.TypeDesignationWorkingSetType;
57 import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationWorkingsetPopupEditor;
58 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditor;
59 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditorMode;
60 import eu.etaxonomy.cdm.vaadin.view.name.TypeDesignationWorkingsetEditorIdSet;
61 import eu.etaxonomy.cdm.vaadin.view.reference.ReferencePopupEditor;
62 import eu.etaxonomy.vaadin.mvp.AbstractPresenter;
63 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
64 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
65
66 /**
67 * @author a.kohlbecker
68 * @since Mar 3, 2017
69 *
70 */
71 @SpringComponent
72 @ViewScope
73 public class RegistrationWorkingsetPresenter extends AbstractPresenter<RegistrationWorkingsetView> {
74
75 private static final long serialVersionUID = 1L;
76
77 @Autowired
78 private IRegistrationWorkingSetService workingSetService;
79
80 @Autowired
81 private RegistrationIdentifierMinter minter;
82
83 /**
84 * @return the workingSetService
85 */
86 public IRegistrationWorkingSetService getWorkingSetService() {
87 return workingSetService;
88 }
89
90 private RegistrationWorkingSet workingset;
91
92 private TaxonName newTaxonNameForRegistration = null;
93
94 private RegistrationDTO newRegistrationDTOWithExistingName;
95
96
97 /**
98 *
99 */
100 public RegistrationWorkingsetPresenter() {
101 }
102
103
104 /**
105 * Always create a new Store
106 *
107 * @return
108 */
109 protected CdmStore<Registration, IRegistrationService> getRegistrationStore(){
110 return new CdmStore<Registration, IRegistrationService>(getRepo(), getRepo().getRegistrationService());
111 }
112
113 /**
114 * Always create a new Store
115 *
116 * @return
117 */
118 protected CdmStore<TaxonName, INameService> getTaxonNameStore(){
119 return new CdmStore<TaxonName, INameService>(getRepo(), getRepo().getNameService());
120 }
121
122
123 /**
124 * @param taxonNameId
125 * @return
126 */
127 protected Registration createNewRegistrationForName(Integer taxonNameId) {
128 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129 // move into RegistrationWorkflowStateMachine
130 TransactionStatus txStatus = getRepo().startTransaction();
131 long identifier = System.currentTimeMillis();
132
133 Identifier<String> identifiers = minter.mint();
134 if(identifiers.getIdentifier() == null){
135 throw new RuntimeException("RegistrationIdentifierMinter configuration incomplete.");
136 }
137 Registration reg = Registration.NewInstance(
138 identifiers.getIdentifier(),
139 identifiers.getLocalId(),
140 taxonNameId != null ? getRepo().getNameService().find(taxonNameId) : null,
141 null);
142 Authentication authentication = currentSecurityContext().getAuthentication();
143 reg.setSubmitter((User)authentication.getPrincipal());
144 EntityChangeEvent event = getRegistrationStore().saveBean(reg);
145 UserHelper.fromSession().createAuthorityForCurrentUser(Registration.class, event.getEntityId(), Operation.UPDATE, RegistrationStatus.PREPARATION.name());
146 getRepo().commitTransaction(txStatus);
147 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148 return getRepo().getRegistrationService().find(event.getEntityId());
149 }
150
151
152 /**
153 * @param doReload TODO
154 *
155 */
156 protected void refreshView(boolean doReload) {
157 if(doReload){
158 loadWorkingSet(workingset.getCitationId());
159 }
160 getView().setWorkingset(workingset);
161 }
162
163
164 /**
165 * {@inheritDoc}
166 */
167 @Override
168 public void handleViewEntered() {
169
170 super.handleViewEntered();
171
172 loadWorkingSet(getView().getCitationID());
173 getView().setWorkingset(workingset);
174
175 CdmFilterablePagingProvider<TaxonName, TaxonName> pagingProvider = new CdmFilterablePagingProvider<TaxonName, TaxonName>(
176 getRepo().getNameService());
177 CdmTitleCacheCaptionGenerator<TaxonName> titleCacheGenrator = new CdmTitleCacheCaptionGenerator<TaxonName>();
178 getView().getAddExistingNameCombobox().setCaptionGenerator(titleCacheGenrator);
179 getView().getAddExistingNameCombobox().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
180 }
181
182
183 /**
184 * @param referenceID
185 */
186 protected void loadWorkingSet(Integer referenceID) {
187 try {
188 workingset = getWorkingSetService().loadWorkingSetByReferenceID(referenceID);
189 } catch (RegistrationValidationException error) {
190 logger.error(error);
191 Window errorDialog = new Window("Validation Error");
192 errorDialog.setModal(true);
193 VerticalLayout subContent = new VerticalLayout();
194 subContent.setMargin(true);
195 errorDialog.setContent(subContent);
196 subContent.addComponent(new Label(error.getMessage()));
197 UI.getCurrent().addWindow(errorDialog);
198 }
199 if(workingset == null || workingset.getCitationId() == null){
200 Reference citation = getRepo().getReferenceService().find(referenceID);
201 workingset = new RegistrationWorkingSet(citation);
202 }
203 }
204
205 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).ADD")
206 public void onReferenceEditorActionAdd(ReferenceEditorAction event) {
207
208 if(!checkFromOwnView(event)){
209 return;
210 }
211
212 ReferencePopupEditor popup = getNavigationManager().showInPopup(ReferencePopupEditor.class);
213 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
214 popup.loadInEditor(null);
215 }
216
217 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
218 public void onReferenceEditorActionEdit(ReferenceEditorAction event) {
219
220 if(!checkFromOwnView(event)){
221 return;
222 }
223 ReferencePopupEditor popup = getNavigationManager().showInPopup(ReferencePopupEditor.class);
224 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
225 popup.withDeleteButton(true);
226 popup.loadInEditor(event.getEntityId());
227 }
228
229 @EventListener
230 public void onDoneWithReferencePopupEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
231 if(event.getPopup() instanceof ReferencePopupEditor){
232 if(event.getReason().equals(Reason.SAVE)){
233 refreshView(true);
234 }
235 }
236 }
237
238 @EventListener
239 public void onDoneWithSpecimenTypeDesignationWorkingsetPopupEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
240 if(event.getPopup() instanceof SpecimenTypeDesignationWorkingsetPopupEditor){
241 if(event.getReason().equals(Reason.SAVE)){
242 refreshView(true);
243 }
244 }
245 }
246
247 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
248 public void onRegistrationEditorAction(RegistrationEditorAction event) {
249
250 if(!checkFromOwnView(event)){
251 return;
252 }
253
254 RegistrationPopupEditor popup = getNavigationManager().showInPopup(RegistrationPopupEditor.class);
255 popup.loadInEditor(event.getEntityId());
256 }
257
258 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
259 public void onTaxonNameEditorActionEdit(TaxonNameEditorAction event) {
260
261 if(!checkFromOwnView(event)){
262 return;
263 }
264
265 TaxonNamePopupEditor popup = getNavigationManager().showInPopup(TaxonNamePopupEditor.class);
266 popup.withDeleteButton(true);
267 configureTaxonNameEditor(popup);
268 popup.loadInEditor(event.getEntityId());
269 if(event.getSourceComponent() != null){
270 popup.setReadOnly(event.getSourceComponent().isReadOnly());
271 }
272
273 }
274
275
276 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).ADD")
277 public void onTaxonNameEditorActionAdd(TaxonNameEditorAction event) {
278
279 if(!checkFromOwnView(event)){
280 return;
281 }
282
283 newTaxonNameForRegistration = TaxonNameFactory.NewNameInstance(RegistrationUIDefaults.NOMENCLATURAL_CODE, Rank.SPECIES());
284 newTaxonNameForRegistration.setNomenclaturalReference(getRepo().getReferenceService().find(workingset.getCitationId()));
285 EntityChangeEvent nameSaveEvent = getTaxonNameStore().saveBean(newTaxonNameForRegistration);
286 newTaxonNameForRegistration = getRepo().getNameService().find(nameSaveEvent.getEntityId());
287 TaxonNamePopupEditor popup = getNavigationManager().showInPopup(TaxonNamePopupEditor.class);
288 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE,CRUD.DELETE));
289 popup.withDeleteButton(true);
290 configureTaxonNameEditor(popup);
291 popup.loadInEditor(newTaxonNameForRegistration.getId());
292 }
293
294 /**
295 * TODO consider putting this into a Configurer Bean per UIScope.
296 * In the configurator bean this methods popup papamerter should be of the type
297 * AbstractPopupEditor
298 *
299 * @param popup
300 */
301 protected void configureTaxonNameEditor(TaxonNamePopupEditor popup) {
302 popup.enableMode(TaxonNamePopupEditorMode.suppressReplacementAuthorshipData);
303 popup.enableMode(TaxonNamePopupEditorMode.nomenclaturalReferenceSectionEditingOnly);
304 }
305
306 /**
307 * Creates a new <code>Registration</code> for a new name that has just been edited
308 * using the <code>TaxonNamePopupEditor</code>. The new name was previously created
309 * in this presenter as <code>newTaxonNameForRegistration</code> (see {@link #onTaxonNameEditorActionAdd(TaxonNameEditorAction)})
310 * and is either filled with data (<code>Reason.SAVE</code>) or it is still empty
311 * (<code>Reason.CANCEL</code>).
312 *
313 *
314 * @param event
315 * @throws RegistrationValidationException
316 */
317 @EventListener
318 public void onDoneWithTaxonnameEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
319 if(event.getPopup() instanceof TaxonNamePopupEditor){
320 TransactionStatus txStatus = getRepo().startTransaction();
321 if(event.getReason().equals(Reason.SAVE)){
322 if(newTaxonNameForRegistration != null){
323 int taxonNameId = newTaxonNameForRegistration.getId();
324 getRepo().getSession().refresh(newTaxonNameForRegistration);
325 Registration reg = createNewRegistrationForName(taxonNameId);
326 // reload workingset into current session
327 loadWorkingSet(workingset.getCitationId());
328 workingset.add(reg);
329 }
330 refreshView(true);
331 } else if(event.getReason().equals(Reason.CANCEL)){
332 if(newTaxonNameForRegistration != null){
333 // clean up
334 getTaxonNameStore().deleteBean(newTaxonNameForRegistration);
335 }
336 }
337 getRepo().commitTransaction(txStatus);
338 newTaxonNameForRegistration = null;
339 }
340 }
341
342
343 /**
344 * Creates a new Registration for an exiting (previously published name).
345 *
346 * @param event
347 * @throws RegistrationValidationException
348 */
349 @EventListener(condition = "#event.action == T(eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction.Action).start")
350 public void onRegistrationWorkflowEventActionStart(RegistrationWorkingsetAction event) throws RegistrationValidationException {
351
352 getView().getAddExistingNameCombobox().commit();
353 TaxonName typifiedName = getView().getAddExistingNameCombobox().getValue();
354 if(typifiedName != null){
355 Registration newRegistrationWithExistingName = createNewRegistrationForName(null);
356 Reference citation = getRepo().getReferenceService().find(workingset.getCitationId());
357 newRegistrationDTOWithExistingName = new RegistrationDTO(newRegistrationWithExistingName, typifiedName, citation);
358 workingset.add(newRegistrationDTOWithExistingName);
359 // tell the view to update the workingset
360 getView().setWorkingset(workingset);
361 getView().getAddExistingNameRegistrationButton().setEnabled(false);
362 getView().getAddExistingNameRegistrationButton().setDescription("You first need to add a type designation to the previously created registration.");
363 } else {
364 logger.error("Seletced name is NULL");
365 }
366
367 }
368
369 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
370 public void onTypeDesignationsEditorActionEdit(TypeDesignationWorkingsetEditorAction event) {
371
372 if(!checkFromOwnView(event)){
373 return;
374 }
375
376 if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET ){
377 SpecimenTypeDesignationWorkingsetPopupEditor popup = getNavigationManager().showInPopup(SpecimenTypeDesignationWorkingsetPopupEditor.class);
378 popup.withDeleteButton(true);
379 popup.loadInEditor(new TypeDesignationWorkingsetEditorIdSet(event.getRegistrationId(), event.getEntityId()));
380 if(event.getSourceComponent() != null){
381 popup.setReadOnly(event.getSourceComponent().isReadOnly());
382 }
383 } else {
384 // TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET
385 // FIXME implement NameTypeDesignationWorkingsetPopupEditor
386 }
387 }
388
389 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).ADD && #event.sourceComponent == null")
390 public void onAddNewTypeDesignationWorkingset(TypeDesignationWorkingsetEditorAction event) {
391
392 if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET){
393 SpecimenTypeDesignationWorkingsetPopupEditor popup = getNavigationManager().showInPopup(SpecimenTypeDesignationWorkingsetPopupEditor.class);
394 TypeDesignationWorkingsetEditorIdSet identifierSet;
395 Integer typifiedNameId;
396 if(newRegistrationDTOWithExistingName != null){
397 typifiedNameId = newRegistrationDTOWithExistingName.getTypifiedNameRef().getId();
398 } else {
399 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationId()).get();
400 EntityReference typifiedNameRef = registrationDTO.getTypifiedNameRef();
401 if(typifiedNameRef != null){
402 // case for registrations without name, in which case the typifiedName is only defined via the typedesignations
403 typifiedNameId = typifiedNameRef.getId();
404 } else {
405 // case of registrations with a name in the nomenclatural act.
406 typifiedNameId = registrationDTO.getNameRef().getId();
407 }
408 }
409 identifierSet = new TypeDesignationWorkingsetEditorIdSet(
410 event.getRegistrationId(),
411 getView().getCitationID(),
412 typifiedNameId
413 );
414 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
415 popup.loadInEditor(identifierSet);
416 popup.withDeleteButton(true);
417 } else {
418 // TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET
419 // FIXME implement NameTypeDesignationWorkingsetPopupEditor
420 }
421 }
422
423 /**
424 * Performs final actions after a TypeDesignationEditor which has been
425 * opened to add a TypeDesignation to a Registration object which was
426 * created for an previously published name. Prior adding a typedesignation,
427 * the according Registration object is dangling, that has no association to
428 * any entity denoting an nomenclatural act which has a reference to a
429 * publication. This means that the registration object is not in the
430 * working set.
431 *
432 *
433 * @param event
434 * @throws RegistrationValidationException
435 */
436 @EventListener
437 public void onDoneWithTypeDesignationEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
438 if(event.getPopup() instanceof SpecimenTypeDesignationWorkingsetPopupEditor){
439 if(event.getReason().equals(Reason.SAVE)){
440 refreshView(true);
441 } else if(event.getReason().equals(Reason.CANCEL)){
442 // clean up
443 if(newRegistrationDTOWithExistingName != null){
444 getRegistrationStore().deleteBean(newRegistrationDTOWithExistingName.registration());
445 }
446 }
447 // set newRegistrationDTOWithExistingName NULL in any case
448 newRegistrationDTOWithExistingName = null;
449 }
450 }
451
452
453 @EventListener(classes=ShowDetailsEvent.class, condition = "#event.type == T(eu.etaxonomy.cdm.vaadin.model.registration.RegistrationWorkingSet)")
454 public void onShowRegistrationWorkingSetMessages(ShowDetailsEvent<?,?> event) { // WARNING don't use more specific generic type arguments
455 List<String> messages = new ArrayList<>();
456 for(RegistrationDTO dto : workingset.getRegistrationDTOs()){
457 dto.getMessages().forEach(m -> messages.add(dto.getSummary() + ": " + m));
458 }
459 if(event.getProperty().equals("messages")){
460 getView().openDetailsPopup("Messages", messages);
461 }
462 }
463
464 @EventListener(classes=ShowDetailsEvent.class, condition = "#event.type == T(eu.etaxonomy.cdm.vaadin.view.registration.RegistrationDTO)")
465 public void onShowRegistrationMessages(ShowDetailsEvent<?,?> event) { // WARNING don't use more specific generic type arguments
466 RegistrationDTO regDto = workingSetService.loadDtoById((Integer)event.getIdentifier());
467 if(event.getProperty().equals("messages")){
468 if(getView() != null){
469 getView().openDetailsPopup("Messages", regDto.getMessages());
470 }
471 }
472 }
473
474 @EventListener
475 public void onEntityChangeEvent(EntityChangeEvent event){
476 if(workingset == null){
477 return;
478 }
479 if(Reference.class.isAssignableFrom(event.getEntityType())){
480 if(workingset.getCitationId().equals(event.getEntityId())){
481 refreshView(true);
482 }
483 } else
484 if(Registration.class.isAssignableFrom(event.getEntityType())){
485 if(workingset.getRegistrations().stream().anyMatch(reg -> reg.getId() == event.getEntityId())){
486 refreshView(true);
487 }
488 } else
489 if(TaxonName.class.isAssignableFrom(event.getEntityType())){
490 if(workingset.getRegistrationDTOs().stream().anyMatch(reg ->
491 reg.getTypifiedNameRef() != null
492 && reg.getTypifiedNameRef().getId() == event.getEntityId())){
493 refreshView(true);
494 }
495 } else
496 if(TypeDesignationBase.class.isAssignableFrom(event.getEntityType())){
497 if(workingset.getRegistrationDTOs().stream().anyMatch(
498 reg -> reg.getTypeDesignations().stream().anyMatch(
499 td -> td.getId() == event.getEntityId()
500 )
501 )
502 ){
503 refreshView(true);
504 }
505 }
506
507 }
508
509 }