73123df83181dd3edf991a46b42ca909ec10635d
[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(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
230 public void onRegistrationEditorAction(RegistrationEditorAction event) {
231
232 if(!checkFromOwnView(event)){
233 return;
234 }
235
236 RegistrationPopupEditor popup = getNavigationManager().showInPopup(RegistrationPopupEditor.class);
237 popup.loadInEditor(event.getEntityId());
238 }
239
240 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
241 public void onTaxonNameEditorActionEdit(TaxonNameEditorAction event) {
242
243 if(!checkFromOwnView(event)){
244 return;
245 }
246
247 TaxonNamePopupEditor popup = getNavigationManager().showInPopup(TaxonNamePopupEditor.class);
248 popup.withDeleteButton(true);
249 configureTaxonNameEditor(popup);
250 popup.loadInEditor(event.getEntityId());
251 if(event.getSourceComponent() != null){
252 popup.setReadOnly(event.getSourceComponent().isReadOnly());
253 }
254
255 }
256
257
258 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).ADD")
259 public void onTaxonNameEditorActionAdd(TaxonNameEditorAction event) {
260
261 if(!checkFromOwnView(event)){
262 return;
263 }
264
265 newTaxonNameForRegistration = TaxonNameFactory.NewNameInstance(RegistrationUIDefaults.NOMENCLATURAL_CODE, Rank.SPECIES());
266 newTaxonNameForRegistration.setNomenclaturalReference(getRepo().getReferenceService().find(workingset.getCitationId()));
267 EntityChangeEvent nameSaveEvent = getTaxonNameStore().saveBean(newTaxonNameForRegistration);
268 newTaxonNameForRegistration = getRepo().getNameService().find(nameSaveEvent.getEntityId());
269 TaxonNamePopupEditor popup = getNavigationManager().showInPopup(TaxonNamePopupEditor.class);
270 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE,CRUD.DELETE));
271 popup.withDeleteButton(true);
272 configureTaxonNameEditor(popup);
273 popup.loadInEditor(newTaxonNameForRegistration.getId());
274 }
275
276 /**
277 * TODO consider putting this into a Configurer Bean per UIScope.
278 * In the configurator bean this methods popup papamerter should be of the type
279 * AbstractPopupEditor
280 *
281 * @param popup
282 */
283 protected void configureTaxonNameEditor(TaxonNamePopupEditor popup) {
284 popup.enableMode(TaxonNamePopupEditorMode.suppressReplacementAuthorshipData);
285 popup.enableMode(TaxonNamePopupEditorMode.nomenclaturalReferenceSectionEditingOnly);
286 }
287
288 /**
289 * Creates a new <code>Registration</code> for a new name that has just been edited
290 * using the <code>TaxonNamePopupEditor</code>. The new name was previously created
291 * in this presenter as <code>newTaxonNameForRegistration</code> (see {@link #onTaxonNameEditorActionAdd(TaxonNameEditorAction)})
292 * and is either filled with data (<code>Reason.SAVE</code>) or it is still empty
293 * (<code>Reason.CANCEL</code>).
294 *
295 *
296 * @param event
297 * @throws RegistrationValidationException
298 */
299 @EventListener
300 public void onDoneWithTaxonnameEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
301 if(event.getPopup() instanceof TaxonNamePopupEditor){
302 TransactionStatus txStatus = getRepo().startTransaction();
303 if(event.getReason().equals(Reason.SAVE)){
304 if(newTaxonNameForRegistration != null){
305 int taxonNameId = newTaxonNameForRegistration.getId();
306 getRepo().getSession().refresh(newTaxonNameForRegistration);
307 Registration reg = createNewRegistrationForName(taxonNameId);
308 // reload workingset into current session
309 loadWorkingSet(workingset.getCitationId());
310 workingset.add(reg);
311 }
312 refreshView(true);
313 } else if(event.getReason().equals(Reason.CANCEL)){
314 if(newTaxonNameForRegistration != null){
315 // clean up
316 getTaxonNameStore().deleteBean(newTaxonNameForRegistration);
317 }
318 }
319 getRepo().commitTransaction(txStatus);
320 newTaxonNameForRegistration = null;
321 }
322 }
323
324
325 /**
326 * Creates a new Registration for an exiting (previously published name).
327 *
328 * @param event
329 * @throws RegistrationValidationException
330 */
331 @EventListener(condition = "#event.action == T(eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction.Action).start")
332 public void onRegistrationWorkflowEventActionStart(RegistrationWorkingsetAction event) throws RegistrationValidationException {
333
334 getView().getAddExistingNameCombobox().commit();
335 TaxonName typifiedName = getView().getAddExistingNameCombobox().getValue();
336 if(typifiedName != null){
337 Registration newRegistrationWithExistingName = createNewRegistrationForName(null);
338 Reference citation = getRepo().getReferenceService().find(workingset.getCitationId());
339 newRegistrationDTOWithExistingName = new RegistrationDTO(newRegistrationWithExistingName, typifiedName, citation);
340 workingset.add(newRegistrationDTOWithExistingName);
341 // tell the view to update the workingset
342 getView().setWorkingset(workingset);
343 getView().getAddExistingNameRegistrationButton().setEnabled(false);
344 getView().getAddExistingNameRegistrationButton().setDescription("You first need to add a type designation to the previously created registration.");
345 } else {
346 logger.error("Seletced name is NULL");
347 }
348
349 }
350
351 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).EDIT")
352 public void onTypeDesignationsEditorActionEdit(TypeDesignationWorkingsetEditorAction event) {
353
354 if(!checkFromOwnView(event)){
355 return;
356 }
357
358 if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET ){
359 SpecimenTypeDesignationWorkingsetPopupEditor popup = getNavigationManager().showInPopup(SpecimenTypeDesignationWorkingsetPopupEditor.class);
360 popup.withDeleteButton(true);
361 popup.loadInEditor(new TypeDesignationWorkingsetEditorIdSet(event.getRegistrationId(), event.getEntityId()));
362 if(event.getSourceComponent() != null){
363 popup.setReadOnly(event.getSourceComponent().isReadOnly());
364 }
365 } else {
366 // TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET
367 // FIXME implement NameTypeDesignationWorkingsetPopupEditor
368 }
369 }
370
371 @EventListener(condition = "#event.type == T(eu.etaxonomy.vaadin.event.EditorActionType).ADD && #event.sourceComponent == null")
372 public void onAddNewTypeDesignationWorkingset(TypeDesignationWorkingsetEditorAction event) {
373
374 if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET){
375 SpecimenTypeDesignationWorkingsetPopupEditor popup = getNavigationManager().showInPopup(SpecimenTypeDesignationWorkingsetPopupEditor.class);
376 TypeDesignationWorkingsetEditorIdSet identifierSet;
377 Integer typifiedNameId;
378 if(newRegistrationDTOWithExistingName != null){
379 typifiedNameId = newRegistrationDTOWithExistingName.getTypifiedNameRef().getId();
380 } else {
381 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationId()).get();
382 EntityReference typifiedNameRef = registrationDTO.getTypifiedNameRef();
383 if(typifiedNameRef != null){
384 // case for registrations without name, in which case the typifiedName is only defined via the typedesignations
385 typifiedNameId = typifiedNameRef.getId();
386 } else {
387 // case of registrations with a name in the nomenclatural act.
388 typifiedNameId = registrationDTO.getNameRef().getId();
389 }
390 }
391 identifierSet = new TypeDesignationWorkingsetEditorIdSet(
392 event.getRegistrationId(),
393 getView().getCitationID(),
394 typifiedNameId
395 );
396 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
397 popup.loadInEditor(identifierSet);
398 popup.withDeleteButton(true);
399 } else {
400 // TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET
401 // FIXME implement NameTypeDesignationWorkingsetPopupEditor
402 }
403 }
404
405 /**
406 * Performs final actions after a TypeDesignationEditor which has been
407 * opened to add a TypeDesignation to a Registration object which was
408 * created for an previously published name. Prior adding a typedesignation,
409 * the according Registration object is dangling, that has no association to
410 * any entity denoting an nomenclatural act which has a reference to a
411 * publication. This means that the registration object is not in the
412 * working set.
413 *
414 *
415 * @param event
416 * @throws RegistrationValidationException
417 */
418 @EventListener
419 public void onDoneWithTypeDesignationEditor(DoneWithPopupEvent event) throws RegistrationValidationException{
420 if(event.getPopup() instanceof SpecimenTypeDesignationWorkingsetPopupEditor){
421 if(event.getReason().equals(Reason.SAVE)){
422 refreshView(true);
423 } else if(event.getReason().equals(Reason.CANCEL)){
424 // clean up
425 if(newRegistrationDTOWithExistingName != null){
426 getRegistrationStore().deleteBean(newRegistrationDTOWithExistingName.registration());
427 }
428 }
429 // set newRegistrationDTOWithExistingName NULL in any case
430 newRegistrationDTOWithExistingName = null;
431 }
432 }
433
434
435 @EventListener(classes=ShowDetailsEvent.class, condition = "#event.type == T(eu.etaxonomy.cdm.vaadin.model.registration.RegistrationWorkingSet)")
436 public void onShowRegistrationWorkingSetMessages(ShowDetailsEvent<?,?> event) { // WARNING don't use more specific generic type arguments
437 List<String> messages = new ArrayList<>();
438 for(RegistrationDTO dto : workingset.getRegistrationDTOs()){
439 dto.getMessages().forEach(m -> messages.add(dto.getSummary() + ": " + m));
440 }
441 if(event.getProperty().equals("messages")){
442 getView().openDetailsPopup("Messages", messages);
443 }
444 }
445
446 @EventListener(classes=ShowDetailsEvent.class, condition = "#event.type == T(eu.etaxonomy.cdm.vaadin.view.registration.RegistrationDTO)")
447 public void onShowRegistrationMessages(ShowDetailsEvent<?,?> event) { // WARNING don't use more specific generic type arguments
448 RegistrationDTO regDto = workingSetService.loadDtoById((Integer)event.getIdentifier());
449 if(event.getProperty().equals("messages")){
450 if(getView() != null){
451 getView().openDetailsPopup("Messages", regDto.getMessages());
452 }
453 }
454 }
455
456 @EventListener
457 public void onEntityChangeEvent(EntityChangeEvent event){
458 if(workingset == null){
459 return;
460 }
461 if(Reference.class.isAssignableFrom(event.getEntityType())){
462 if(workingset.getCitationId().equals(event.getEntityId())){
463 refreshView(true);
464 }
465 } else
466 if(Registration.class.isAssignableFrom(event.getEntityType())){
467 if(workingset.getRegistrations().stream().anyMatch(reg -> reg.getId() == event.getEntityId())){
468 refreshView(true);
469 }
470 } else
471 if(TaxonName.class.isAssignableFrom(event.getEntityType())){
472 if(workingset.getRegistrationDTOs().stream().anyMatch(reg ->
473 reg.getTypifiedNameRef() != null
474 && reg.getTypifiedNameRef().getId() == event.getEntityId())){
475 refreshView(true);
476 }
477 } else
478 if(TypeDesignationBase.class.isAssignableFrom(event.getEntityType())){
479 if(workingset.getRegistrationDTOs().stream().anyMatch(
480 reg -> reg.getTypeDesignations().stream().anyMatch(
481 td -> td.getId() == event.getEntityId()
482 )
483 )
484 ){
485 refreshView(true);
486 }
487 }
488
489 }
490
491 }