cleanup
[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.Arrays;
13 import java.util.Collection;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.Stack;
23 import java.util.UUID;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.springframework.beans.factory.annotation.Autowired;
28 import org.vaadin.spring.events.EventScope;
29 import org.vaadin.spring.events.annotation.EventBusListenerMethod;
30
31 import com.vaadin.event.ShortcutAction.KeyCode;
32 import com.vaadin.spring.annotation.SpringComponent;
33 import com.vaadin.spring.annotation.ViewScope;
34 import com.vaadin.ui.AbstractField;
35 import com.vaadin.ui.Alignment;
36 import com.vaadin.ui.Button;
37 import com.vaadin.ui.Label;
38 import com.vaadin.ui.UI;
39 import com.vaadin.ui.VerticalLayout;
40 import com.vaadin.ui.Window;
41
42 import eu.etaxonomy.cdm.api.service.config.RegistrationStatusTransitions;
43 import eu.etaxonomy.cdm.api.service.dto.RegistrationDTO;
44 import eu.etaxonomy.cdm.api.service.dto.RegistrationWorkingSet;
45 import eu.etaxonomy.cdm.api.service.exception.TypeDesignationSetException;
46 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSet.TypeDesignationSetType;
47 import eu.etaxonomy.cdm.api.service.registration.IRegistrationWorkingSetService;
48 import eu.etaxonomy.cdm.api.util.UserHelper;
49 import eu.etaxonomy.cdm.cache.CdmTransientEntityWithUuidCacher;
50 import eu.etaxonomy.cdm.model.ICdmEntityUuidCacher;
51 import eu.etaxonomy.cdm.model.common.CdmBase;
52 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
53 import eu.etaxonomy.cdm.model.name.Rank;
54 import eu.etaxonomy.cdm.model.name.Registration;
55 import eu.etaxonomy.cdm.model.name.RegistrationStatus;
56 import eu.etaxonomy.cdm.model.name.TaxonName;
57 import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
58 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
59 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
60 import eu.etaxonomy.cdm.model.permission.CRUD;
61 import eu.etaxonomy.cdm.model.reference.Reference;
62 import eu.etaxonomy.cdm.persistence.permission.PermissionDeniedException;
63 import eu.etaxonomy.cdm.ref.TypedEntityReference;
64 import eu.etaxonomy.cdm.service.CdmBeanItemContainerFactory;
65 import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
66 import eu.etaxonomy.cdm.service.CdmFilterablePagingProviderFactory;
67 import eu.etaxonomy.cdm.service.CdmStore;
68 import eu.etaxonomy.cdm.service.IRegistrationWorkflowService;
69 import eu.etaxonomy.cdm.service.UserHelperAccess;
70 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationItem;
71 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusFieldInstantiator;
72 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusSelect;
73 import eu.etaxonomy.cdm.vaadin.event.EditorActionContext;
74 import eu.etaxonomy.cdm.vaadin.event.EditorActionTypeFilter;
75 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
76 import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction;
77 import eu.etaxonomy.cdm.vaadin.event.RegistrationEditorAction;
78 import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEvent;
79 import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEventEntityTypeFilter;
80 import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction;
81 import eu.etaxonomy.cdm.vaadin.event.TypeDesignationSetEditorAction;
82 import eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction;
83 import eu.etaxonomy.cdm.vaadin.ui.RegistrationUI;
84 import eu.etaxonomy.cdm.vaadin.ui.RegistrationUIDefaults;
85 import eu.etaxonomy.cdm.vaadin.ui.config.TaxonNamePopupEditorConfig;
86 import eu.etaxonomy.cdm.vaadin.util.CdmTitleCacheCaptionGenerator;
87 import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
88 import eu.etaxonomy.cdm.vaadin.view.name.NameTypeDesignationPopupEditor;
89 import eu.etaxonomy.cdm.vaadin.view.name.NameTypeDesignationSetIds;
90 import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationSetIds;
91 import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationSetPopupEditor;
92 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNameEditorPresenter;
93 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditor;
94 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditorMode;
95 import eu.etaxonomy.cdm.vaadin.view.reference.ReferencePopupEditor;
96 import eu.etaxonomy.vaadin.mvp.AbstractPopupEditor;
97 import eu.etaxonomy.vaadin.mvp.AbstractPresenter;
98 import eu.etaxonomy.vaadin.mvp.AbstractView;
99 import eu.etaxonomy.vaadin.mvp.BeanInstantiator;
100 import eu.etaxonomy.vaadin.ui.navigation.NavigationEvent;
101 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
102 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
103 import eu.etaxonomy.vaadin.ui.view.PopupView;
104
105 /**
106 * @author a.kohlbecker
107 * @since Mar 3, 2017
108 */
109 @SpringComponent
110 @ViewScope
111 public class RegistrationWorkingsetPresenter
112 extends AbstractPresenter<RegistrationWorkingsetPresenter,RegistrationWorkingsetView>
113 implements CachingPresenter {
114
115 private static final long serialVersionUID = 2618456456539802265L;
116
117 private static final Logger logger = LogManager.getLogger();
118
119 @Autowired
120 private IRegistrationWorkingSetService regWorkingSetService;
121
122 @Autowired
123 private IRegistrationWorkflowService registrationWorkflowService;
124
125 @Autowired
126 private CdmFilterablePagingProviderFactory pagingProviderFactory;
127
128 @Autowired
129 private CdmBeanItemContainerFactory selectFieldFactory;
130
131 @Autowired
132 private CdmStore cdmStore;
133
134 /**
135 * @return the regWorkingSetService
136 */
137 public IRegistrationWorkingSetService getWorkingSetService() {
138 return regWorkingSetService;
139 }
140
141 private RegistrationWorkingSet workingset;
142
143 /**
144 * Contains the poupeditor which has been opened to start the registration of a new name as long as it has not been saved or canceled.
145 * There can always only be one popup editor for this purpose.
146 */
147 private TaxonNamePopupEditor newNameForRegistrationPopupEditor = null;
148
149 /**
150 * Contains
151 */
152 private List<Registration> newNameBlockingRegistrations = new ArrayList<>();
153
154 /**
155 * TODO is this still needed? The registration UUID should be accessible in the popup editor context,
156 * see findRegistrationInContext()
157 */
158 private Map<NameTypeDesignationPopupEditor, UUID> nameTypeDesignationPopupEditorRegistrationUUIDMap = new HashMap<>();
159
160
161 private ICdmEntityUuidCacher cache;
162
163 private Collection<CdmBase> rootEntities = new HashSet<>();
164
165
166 public RegistrationWorkingsetPresenter() {
167 }
168
169 /**
170 * @param doReload reload the workingset from the persistent storage.
171 * Workingsets which are not yet persisted are preserved.
172 */
173 protected void refreshView(boolean doReload) {
174
175 if(workingset == null){
176 return; // nothing to do
177 }
178 if(doReload){
179 if(logger.isDebugEnabled()){
180 logger.debug("refreshView() - workingset:\n" + workingset.toString());
181 }
182 List<RegistrationDTO> unpersisted = new ArrayList<>();
183 for(RegistrationDTO regDto : workingset.getRegistrationDTOs()){
184 if(!regDto.registration().isPersisted()){
185 unpersisted.add(regDto);
186 }
187 }
188 loadWorkingSet(workingset.getCitationUuid());
189 for(RegistrationDTO regDtoUnpersisted : unpersisted){
190 if(!workingset.getRegistrationDTOs().stream().anyMatch(dto -> dto.getUuid().equals(regDtoUnpersisted.getUuid()))){
191 // only add if the regDtoUnpersisted has not been persisted meanwhile
192 try {
193 workingset.add(regDtoUnpersisted);
194 } catch (TypeDesignationSetException e) {
195 // would never happen here //
196 }
197 }
198 }
199 if(logger.isDebugEnabled()){
200 logger.debug("refreshView() - workingset reloaded:\n" + workingset.toString());
201 }
202 }
203 applyWorkingset();
204 }
205
206 @Override
207 public void handleViewEntered() {
208 super.handleViewEntered();
209 // TODO currently cannot specify type more precisely, see AbstractSelect
210 // FIXME externalize into class file!!!!!!!!!!!!
211 getView().setStatusComponentInstantiator(new RegistrationStatusFieldInstantiator<Object>(){
212
213 private static final long serialVersionUID = 7099181280977511048L;
214
215 @Override
216 public AbstractField<Object> create(RegistrationDTO regDto) {
217
218 // submitters have GrantedAuthorities like REGISTRATION(PREPARATION).[UPDATE]{ab4459eb-3b96-40ba-bfaa-36915107d59e}
219 UserHelper userHelper = UserHelperAccess.userHelper().withCache(getCache());
220 Set<RegistrationStatus> availableStatus = new HashSet<>();
221
222 boolean canChangeStatus = userHelper.userHasPermission(regDto.registration(), CRUD.UPDATE);
223 availableStatus.add(regDto.getStatus());
224 if(canChangeStatus){
225 if(userHelper.userIsAdmin()){
226 availableStatus.addAll(Arrays.asList(RegistrationStatus.values()));
227 } else {
228 availableStatus.addAll(RegistrationStatusTransitions.possibleTransitions(regDto.getStatus()));
229 }
230 }
231
232 RegistrationStatusSelect select = new RegistrationStatusSelect(null, selectFieldFactory.buildEnumTermItemContainer(
233 RegistrationStatus.class,
234 availableStatus.toArray(new RegistrationStatus[availableStatus.size()]))
235 );
236 select.setValue(regDto.getStatus());
237 select.addValueChangeListener(e -> saveRegistrationStatusChange(regDto.getUuid(), e.getProperty().getValue()));
238 select.setEnabled(canChangeStatus);
239 select.setNullSelectionAllowed(false);
240 return select;
241 }
242 });
243
244 loadWorkingSet(getView().getCitationUuid());
245 applyWorkingset();
246
247 }
248
249 private void applyWorkingset(){
250 if (workingset != null) {
251 getView().setWorkingset(workingset);
252 // PagingProviders and CacheGenerator for the existingNameCombobox
253 activateComboboxes();
254 }
255 }
256
257 protected void activateComboboxes() {
258 CdmTitleCacheCaptionGenerator<TaxonName> titleCacheGenerator = new CdmTitleCacheCaptionGenerator<TaxonName>();
259 getView().getExistingNameCombobox().setCaptionGenerator(titleCacheGenerator);
260 CdmFilterablePagingProvider<TaxonName, TaxonName> pagingProvider = pagingProviderFactory.taxonNamesWithoutOrthophicIncorrect();
261 getView().getExistingNameCombobox().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
262 }
263
264 protected void loadWorkingSet(UUID referenceUuid) {
265
266 try {
267 workingset = getWorkingSetService().loadWorkingSetByReferenceUuid(referenceUuid, true);
268 } catch (PermissionDeniedException error) {
269 logger.warn(error);
270 showErrorDialog("Permission denied", "You are not allowed to access this working set.");
271 return;
272 } catch (TypeDesignationSetException error) {
273 logger.error(error);
274 showErrorDialog("Validation Error", error.getMessage());
275 //NOTE by AM: should we return here, too, or is this error not so
276 }
277 cache = new CdmTransientEntityWithUuidCacher(this);
278 for(Registration registration : workingset.getRegistrations()) {
279 addRootEntity(registration);
280 }
281 }
282
283 public void showErrorDialog(String errorDialogCaption, String errorMessage) {
284
285 final Window errorDialog = new Window(errorDialogCaption);
286 errorDialog.setModal(true);
287 errorDialog.setClosable(false);
288 errorDialog.setResizable(false);
289 VerticalLayout subContent = new VerticalLayout();
290 subContent.setSpacing(true);
291 subContent.setMargin(true);
292 errorDialog.setContent(subContent);
293 subContent.addComponent(new Label(errorMessage));
294
295 //close button (quick & dirty by AM, to fix #10373)
296 Button cancelButton = new Button("Close");
297 subContent.addComponent(cancelButton);
298 cancelButton.setWidth("-1px");
299 cancelButton.setHeight("-1px");
300 subContent.addComponent(cancelButton);
301 subContent.setComponentAlignment(cancelButton, new Alignment(48));
302 cancelButton.addClickListener((ev)->errorDialog.close());
303 cancelButton.setClickShortcut(KeyCode.ENTER, null);
304 cancelButton.focus();
305
306 UI.getCurrent().addWindow(errorDialog);
307 }
308
309 private void saveRegistrationStatusChange(UUID uuid, Object value) {
310 Registration reg = getRepo().getRegistrationService().load(uuid);
311 if(reg == null){
312 // registration was not yet persisted, ignore
313 return;
314 }
315 if(value != null && value instanceof RegistrationStatus){
316 if(!Objects.equals(value, reg.getStatus())){
317 reg.updateStatusAndDate((RegistrationStatus)value);
318 cdmStore.saveBean(reg, (AbstractView<?,?>)getView());
319 refreshView(true);
320 }
321 } else {
322 // only log here as error
323 logger.error("Ivalid attempt to set RegistrationStatus to " + Objects.toString(value.toString(), "NULL"));
324 }
325 }
326
327 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
328 public void onReferenceEditorActionAdd(ReferenceEditorAction event) {
329
330 if(!checkFromOwnView(event)){
331 return;
332 }
333
334 ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
335 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
336 popup.loadInEditor(null);
337 }
338
339 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
340 public void onReferenceEditorActionEdit(ReferenceEditorAction event) {
341
342 if(!checkFromOwnView(event)){
343 return;
344 }
345 ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
346 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
347 popup.withDeleteButton(true);
348 popup.loadInEditor(event.getEntityUuid());
349 }
350
351 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
352 public void onRegistrationEditorAction(RegistrationEditorAction event) {
353
354 if(!checkFromOwnView(event)){
355 return;
356 }
357
358 RegistrationPopupEditor popup = openPopupEditor(RegistrationPopupEditor.class, event);
359 popup.loadInEditor(event.getEntityUuid());
360 }
361
362 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
363 public void onTaxonNameEditorActionEdit(TaxonNameEditorAction event) {
364
365 if(!checkFromOwnView(event)){
366 return;
367 }
368
369 boolean isRegistrationForExistingName = event.getTarget() != null && event.getTarget().equals(getView().getExistingNameCombobox());
370
371 TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
372
373 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
374 popup.withDeleteButton(!isRegistrationForExistingName);
375 TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
376 if(isRegistrationForExistingName){
377 // allow saving even if the name parts are not valid
378 // the user will need to fix this in a later step
379 popup.disableMode(TaxonNamePopupEditorMode.VALIDATE_AGAINST_HIGHER_NAME_PART);
380 getView().getAddExistingNameRegistrationButton().setEnabled(false);
381 popup.addDetachListener(ev ->
382 getView().getAddExistingNameRegistrationButton().setEnabled(true)
383 );
384 }
385 popup.loadInEditor(event.getEntityUuid());
386 if(event.hasSource() && event.getSource().isReadOnly()){
387 // avoid resetting read-only to false
388 logger.info("Set popup to read-only as event source is read only");
389 popup.setReadOnly(true);
390 }
391
392 boolean hasNomRef = popup.getBean().getNomenclaturalReference() != null;
393 if(isRegistrationForExistingName){
394 popup.setAllFieldsReadOnly(true);
395 popup.removeStatusMessage(RegistrationUI.CHECK_IN_SEARCH_INDEX);
396
397 if(!hasNomRef){
398 //#10269 for now we do not allow registrations for names with no nom. ref.
399 // Old code was:
400 // // only allow editing the nomenclatural reference, all other
401 // // editing need to be done another way.
402 // // Otherwise we would need to be prepared for creating blocking registrations
403 // // in turn of creation, modification of related taxon names.
404 // popup.disableMode(TaxonNamePopupEditorMode.NOMENCLATURALREFERENCE_SECTION_EDITING_ONLY);
405 // popup.getNomReferenceCombobox().setReadOnly(false);
406 // popup.getNomenclaturalReferenceDetail().setReadOnly(false);
407 // popup.addStatusMessage("The chosen name needs to be completed before it can be used. "
408 // + "Please add the nomenclatural reference and click on \"Save\" to proceed with entering the type of this name.");
409
410 //instead we show status message:
411 // popup.setToCancelOnly();
412 popup.addStatusMessage("<p style='color:red;'><strong>The data entry is aborted "
413 + "due to a data issue that should be fixed "
414 + "by the curator</strong>.<BR>"
415 + "Please send an e-mail with the scientific name "
416 + "to <i>curation@phycobank.org</i></p>");
417 } else {
418 popup.setToSelect(); //sets the save button to "save & select"
419 popup.addStatusMessage("You are about to create a registration for this name. "
420 + "This editor is for reviewing the name only. Therefore, all fields have "
421 + "been switched to read-only state. "
422 + "Click on \"Save\" to proceed with entering the type of this name.");
423 }
424 }
425 }
426
427 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
428 public void onTaxonNameEditorActionAdd(TaxonNameEditorAction event) {
429
430 if(!checkFromOwnView(event)){
431 return;
432 }
433
434 getView().getAddNewNameRegistrationButton().setEnabled(false);
435 if(newNameForRegistrationPopupEditor == null){
436 TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
437 newNameForRegistrationPopupEditor = popup;
438 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
439 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE,CRUD.DELETE));
440 popup.withDeleteButton(true);
441 popup.setCdmEntityInstantiator(new BeanInstantiator<TaxonName>() {
442
443 @Override
444 public TaxonName createNewBean() {
445 TaxonName newTaxonName = TaxonNameFactory.NewNameInstance(RegistrationUIDefaults.NOMENCLATURAL_CODE, Rank.SPECIES());
446 newTaxonName.setNomenclaturalReference(getRepo().getReferenceService().load(workingset.getCitationUuid(), TaxonNameEditorPresenter.REFERENCE_INIT_STRATEGY ));
447 return newTaxonName;
448 }
449 });
450 TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
451 popup.loadInEditor(null);
452 }
453 }
454
455 /**
456 * Creates a new <code>Registration</code> for a new name that has just been edited
457 * using a <code>TaxonNamePopupEditor</code>. The popup editor which has been opened to
458 * edit the new name was remembered in <code>newNameForRegistrationPopupEditor</code>.
459 * Any blocking registrations which have been created while editing the new name are
460 * temporarily stored in <code>newNameBlockingRegistrations</code> until the registration
461 * for the first name has been created. Additional new names are created for example
462 * when a new name as basionym, replaced synonym, etc to the new name is created.
463 * <p>
464 * See also {@link #onTaxonNameEditorActionAdd(TaxonNameEditorAction)}).
465 *
466 * @param event
467 * @throws TypeDesignationSetException
468 * passes on the Exception which may come from onRegistrationWorkflowEventActionStart()
469 */
470 @EventBusListenerMethod
471 public void onDoneWithTaxonnameEditor(DoneWithPopupEvent event) throws TypeDesignationSetException {
472
473 if(!isFromOwnView(event)){
474 return;
475 }
476
477 if(event.getPopup() instanceof TaxonNamePopupEditor){
478
479 EditorActionContext rootContext = editorActionContextRoot(event.getPopup());
480 boolean isAddExistingNameRegistration = rootContext.getTargetField() != null && rootContext.getTargetField().equals(getView().getExistingNameCombobox());
481
482 if(isAddExistingNameRegistration){
483 if(event.getReason().equals(Reason.SAVE)){
484 onRegistrationWorkflowEventActionStart(new RegistrationWorkingsetAction(workingset.getCitationUuid(),
485 RegistrationWorkingsetAction.Action.start));
486 }
487 // just ignore on CANCEL
488 } else {
489 Optional<Registration> registrationOpt = Optional.ofNullable(null);
490 if(newNameForRegistrationPopupEditor != null && event.getPopup().equals(newNameForRegistrationPopupEditor)){
491 if(event.getReason().equals(Reason.SAVE)){
492 try {
493 TaxonName taxonName = newNameForRegistrationPopupEditor.getBean().cdmEntity();
494 registrationOpt = Optional.of(registrationWorkflowService.createRegistration(taxonName, newNameBlockingRegistrations));
495 loadWorkingSet(workingset.getCitationUuid());
496 } finally {
497 clearSession();
498 getView().getAddNewNameRegistrationButton().setEnabled(true);
499 }
500 }
501 // nullify and clear the memory on this popup editor in any case (SAVE, DELETE, CANCEL)
502 newNameForRegistrationPopupEditor = null;
503 newNameBlockingRegistrations.clear();
504 getView().getAddNewNameRegistrationButton().setEnabled(true);
505 }
506
507 if(event.getReason().equals(Reason.SAVE)){
508 if(!registrationOpt.isPresent()){
509 // no new registration has been created above, so there must be an existing one.
510 registrationOpt = findRegistrationInContext(event.getPopup());
511 }
512
513 // Check if the other names used in the context of the name are registered yet.
514 TaxonNamePopupEditor nameEditor = (TaxonNamePopupEditor)event.getPopup();
515 Set<TaxonName> namesToCheck = new HashSet<>();
516
517 namesToCheck.addAll(nameEditor.getBasionymComboboxSelect().getValue());
518 namesToCheck.addAll(nameEditor.getReplacedSynonymsComboboxSelect().getValue());
519 namesToCheck.add(nameEditor.getValidationField().getRelatedNameComboBox().getValue());
520 namesToCheck.add(nameEditor.getOrthographicVariantField().getRelatedNameComboBox().getValue());
521 // NOTE: according to https://dev.e-taxonomy.eu/redmine/issues/8049#note-2 we will not create blocking
522 // registrations for names in WeaklyRelatedEntityFields
523
524 for(TaxonName name : namesToCheck){
525 if(name != null){
526 clearSession();
527 assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
528 }
529 }
530 } else if (event.getReason().equals(Reason.DELETE)){
531 //FIXME handle delete: need to remove blocking registrations?
532 }
533 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
534 refreshView(isAtContextRoot(event.getPopup()));
535 }
536 }
537 }
538
539 /**
540 * Creates a new Registration for an exiting (previously published) name.
541 */
542 @EventBusListenerMethod
543 public void onRegistrationWorkflowEventActionStart(RegistrationWorkingsetAction event) throws TypeDesignationSetException {
544
545 if(!event.isStart()){
546 return;
547 }
548
549 getView().getExistingNameCombobox().commit(); // update the chosen value in the datasource
550 TaxonName typifiedName = getView().getExistingNameCombobox().getValue();
551 if(typifiedName != null){
552 boolean doReloadWorkingSet = false;
553 try {
554 doReloadWorkingSet = registrationWorkflowService.createRegistrationforExistingName(workingset, typifiedName);
555 } finally {
556 clearSession();
557 refreshView(doReloadWorkingSet);
558 getView().getAddExistingNameRegistrationButton().setEnabled(false);
559 }
560 } else {
561 logger.error("Seletced name is NULL");
562 }
563
564 }
565
566 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
567 public void onTypeDesignationsEditorActionEdit(TypeDesignationSetEditorAction event) {
568
569 if(!checkFromOwnView(event)){
570 return;
571 }
572
573 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationUuid()).get();
574
575 if(event.getWorkingSetType() == TypeDesignationSetType.SPECIMEN_TYPE_DESIGNATION_SET ){
576 SpecimenTypeDesignationSetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationSetPopupEditor.class, event);
577 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
578 popup.withDeleteButton(true);
579 popup.loadInEditor(new SpecimenTypeDesignationSetIds(
580 workingset.getCitationUuid(),
581 event.getRegistrationUuid(),
582 CdmBase.deproxy(event.getBaseEntity(), FieldUnit.class), null));
583 if(event.hasSource()){
584 // propagate readonly state from source button to popup
585 popup.setReadOnly(event.getSource().isReadOnly());
586 }
587 } else {
588 NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
589 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
590 popup.withDeleteButton(true);
591 popup.loadInEditor(NameTypeDesignationSetIds.forExistingTypeDesignation(
592 registrationDTO.getCitationUuid(),
593 CdmBase.deproxy(event.getBaseEntity(), NameTypeDesignation.class))
594 );
595 popup.getTypifiedNamesComboboxSelect().setEnabled(false);
596 if(event.hasSource()){
597 // propagate readonly state from source button to popup
598 popup.setReadOnly(event.getSource().isReadOnly());
599 }
600 nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
601 }
602 }
603
604 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
605 public void onTypeDesignationSetAdd(TypeDesignationSetEditorAction event) {
606
607 if(!event.hasSource()){
608 return;
609 }
610
611 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationUuid()).get();
612
613 if(event.getWorkingSetType() == TypeDesignationSetType.SPECIMEN_TYPE_DESIGNATION_SET){
614 SpecimenTypeDesignationSetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationSetPopupEditor.class, event);
615 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
616 TypedEntityReference<TaxonName> typifiedNameRef;
617 if(registrationDTO.getTypifiedNameRef() != null){
618 // case for registrations without name, in which case the typifiedName is only defined via the typedesignations
619 typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getTypifiedNameRef().getUuid());
620 } else {
621 // case of registrations with a name in the nomenclatural act.
622 typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getNameRef().getUuid());
623 }
624
625 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
626 popup.withDeleteButton(false);
627 popup.loadInEditor(new SpecimenTypeDesignationSetIds(
628 workingset.getCitationUuid(),
629 event.getRegistrationUuid(),
630 null,
631 typifiedNameRef.getUuid()
632 )
633 );
634 if(event.hasSource()){
635 // propagate readonly state from source component to popup
636 popup.setReadOnly(event.getSource().isReadOnly());
637 }
638 } else {
639 NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
640 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
641 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
642 nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
643 popup.setBeanInstantiator(new BeanInstantiator<NameTypeDesignation>() {
644
645 @Override
646 public NameTypeDesignation createNewBean() {
647
648 TaxonName typifiedName = getRepo().getNameService().load(event.getTypifiedNameUuid(), Arrays.asList(new String[]{
649 "typeDesignations",
650 "homotypicalGroup",
651 "nomenclaturalSource.citation"
652 }));
653 NameTypeDesignation nameTypeDesignation = NameTypeDesignation.NewInstance();
654 nameTypeDesignation.getTypifiedNames().add(typifiedName);
655 return nameTypeDesignation;
656 }
657 });
658 popup.withDeleteButton(false);
659 popup.loadInEditor(NameTypeDesignationSetIds.forNewTypeDesignation(
660 registrationDTO.getCitationUuid(),
661 event.getTypifiedNameUuid()
662 )
663 );
664 popup.getTypifiedNamesComboboxSelect().setEnabled(false);
665 if(event.hasSource()){
666 // propagate readonly state from source component to popup
667 popup.setReadOnly(event.getSource().isReadOnly());
668 }
669 }
670 }
671
672 /**
673 * Performs final actions after a TypeDesignationEditor which has been
674 * opened to add a TypeDesignation to a Registration object which was
675 * created for an previously published name. Prior adding a typedesignation,
676 * the according Registration object is dangling, that has no association to
677 * any entity denoting an nomenclatural act which has a reference to a
678 * publication. This means that the registration object is not in the
679 * working set.
680 *
681 *
682 * @param event
683 * @throws TypeDesignationSetException
684 */
685 @EventBusListenerMethod
686 public void onDoneWithTypeDesignationEditor(DoneWithPopupEvent event) {
687
688 if(!isFromOwnView(event)){
689 return;
690 }
691
692 if(event.getPopup() instanceof SpecimenTypeDesignationSetPopupEditor){
693 if(event.getReason().equals(Reason.SAVE)){
694 // NOTE: adding the SpecimenTypeDesignations to the registration is done in the
695 // SpecimenTypeDesignationSetServiceImpl.save(SpecimenTypeDesignationSetDTO dto) method
696 }
697 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
698 refreshView(isAtContextRoot(event.getPopup()));
699 } else if(event.getPopup() instanceof NameTypeDesignationPopupEditor){
700 if(event.getReason().equals(Reason.SAVE)){
701 Optional<Registration> registrationOpt = Optional.ofNullable(null);
702 NameTypeDesignationPopupEditor popup = ((NameTypeDesignationPopupEditor)event.getPopup());
703 UUID typeDesignationUuid = popup.getBean().getUuid();
704 try {
705 clearSession();
706 registrationOpt = findRegistrationInContext(popup);
707 registrationOpt.ifPresent(reg -> {
708 registrationWorkflowService.addTypeDesignation(typeDesignationUuid, reg);
709 nameTypeDesignationPopupEditorRegistrationUUIDMap.remove(popup);
710 });
711
712 } finally {
713 clearSession();
714 }
715
716 // Check if other names used in the context of the name are registered yet.
717 Set<TaxonName> namesToCheck = new HashSet<>();
718
719 namesToCheck.add(popup.getTypeNameField().getValue());
720
721 for(TaxonName name : namesToCheck){
722 if(name != null){
723 assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
724 }
725 }
726
727 } else if(event.getReason().equals(Reason.CANCEL)){
728 // noting to do
729 }
730 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
731 refreshView(isAtContextRoot(event.getPopup()));
732
733 }
734 // ignore other editors
735 }
736
737 private void assocciateOrQueueBlockingRegistration(Optional<Registration> registrationOpt, UUID nameUuid) {
738 registrationOpt.ifPresent(reg -> registrationWorkflowService.addBlockingRegistration(nameUuid, reg));
739 if(!registrationOpt.isPresent()){
740 // not present!
741 Registration blockingRegistration = registrationWorkflowService.prepareBlockingRegistration(nameUuid);
742 if(blockingRegistration != null){
743 newNameBlockingRegistrations.add(blockingRegistration);
744 logger.debug("Blocking registration created and queued for later association with the main registration.");
745 }
746 }
747 }
748
749
750 public void clearSession() {
751 getRepo().clearSession();
752 }
753
754 @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationWorkingSet.class)
755 public void onShowDetailsEventForRegistrationWorkingSet(ShowDetailsEvent<RegistrationWorkingSet,?> event) {
756
757 if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
758 List<String> messages = new ArrayList<>();
759 for(RegistrationDTO dto : workingset.getRegistrationDTOs()){
760 dto.getValidationProblems().forEach(m -> messages.add(dto.getSummary() + ": " + m));
761 }
762 getView().openDetailsPopup("Validation Problems", messages);
763 }
764 }
765
766 @EventBusListenerMethod
767 public void onEntityChangeEvent(EntityChangeEvent event){
768
769 if(!isFromOwnView(event)){
770 return;
771 }
772
773 if(workingset == null){
774 return;
775 }
776 if(Reference.class.isAssignableFrom(event.getEntityType())){
777
778 if(workingset.getCitationUuid().equals(event.getEntityUuid())){
779 if(event.isRemovedType()){
780 viewEventBus.publish(EventScope.UI, this, new NavigationEvent(StartRegistrationViewBean.NAME));
781 } else {
782 refreshView(true);
783 }
784 }
785
786 } else
787 if(Registration.class.isAssignableFrom(event.getEntityType())){
788 if(workingset.getRegistrations().stream().anyMatch(reg -> reg.getUuid() == event.getEntityUuid())){
789 refreshView(true);
790 }
791 } else
792 if(TaxonName.class.isAssignableFrom(event.getEntityType())){
793 if(event.getType().equals(EntityChangeEvent.Type.CREATED)){
794 Stack<EditorActionContext>context = ((AbstractPopupEditor)event.getSourceView()).getEditorActionContext();
795 EditorActionContext rootContext = context.get(0);
796 if(rootContext.getParentView().equals(getView()) && event.getSourceView() != newNameForRegistrationPopupEditor){
797 try {
798 clearSession();
799 // create a blocking registration
800 UUID taxonNameUUID = event.getEntityUuid();
801 Optional<Registration> registrationOpt = findRegistrationInContext(context);
802 assocciateOrQueueBlockingRegistration(registrationOpt, taxonNameUUID);
803 } finally {
804 clearSession();
805 }
806
807 } else {
808 // in case of creating a new name for a registration the parent view is the TaxonNamePopupEditor
809 // this is set
810 logger.debug("Non blocking registration, since a new name for a new registration has been created");
811 }
812 }
813 if(workingset.getRegistrationDTOs().stream().anyMatch(reg ->
814 reg.getTypifiedNameRef() != null
815 && reg.getTypifiedNameRef().getUuid().equals(event.getEntityUuid()))){
816 //refreshView(true);
817 }
818 } else
819 if(TypeDesignationBase.class.isAssignableFrom(event.getEntityType())){
820 if(workingset.getRegistrationDTOs().stream().anyMatch(
821 reg -> reg.typeDesignations() != null && reg.typeDesignations().stream().anyMatch(
822 td -> td.getUuid() == event.getEntityUuid()
823 )
824 )
825 ){
826 //refreshView(true);
827 }
828 }
829 }
830
831 public Optional<Registration> findRegistrationInContext(PopupView popupView) {
832 Stack<EditorActionContext>context = ((AbstractPopupEditor)popupView).getEditorActionContext();
833 return findRegistrationInContext(context);
834 }
835
836 /**
837 * Finds the Registration in the EditorContext stack
838 */
839 public Optional<Registration> findRegistrationInContext(Stack<EditorActionContext> context) {
840 EditorActionContext rootCtx = context.get(0);
841 TypedEntityReference<Registration> regReference = (TypedEntityReference<Registration>)rootCtx.getParentEntity();
842 Optional<RegistrationDTO> registrationDTOOptional = workingset.getRegistrationDTO(regReference.getUuid());
843 Optional<Registration> registrationOptional;
844 if(!registrationDTOOptional.isPresent()){
845 logger.debug("No RegistrationDTO in found rootCtx -> user is about to create a registration for a new name.");
846 registrationOptional = Optional.ofNullable(null);
847 }
848
849 Optional<Registration> regOpt;
850 if(registrationDTOOptional.isPresent()){
851 regOpt = Optional.of(registrationDTOOptional.get().registration());
852 } else {
853 regOpt = Optional.ofNullable(null);
854 }
855
856 return regOpt;
857 }
858
859 @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationDTO.class)
860 public void onShowDetailsEventForRegistrationDTO(ShowDetailsEvent<RegistrationDTO, UUID> event) {
861
862 // FIXME check from own view!!!
863 if(getView() == null){
864 return;
865 }
866
867 UUID registrationUuid = event.getIdentifier();
868
869 RegistrationDTO regDto = workingset.getRegistrationDTO(registrationUuid).get();
870 if(event.getProperty().equals(RegistrationItem.BLOCKED_BY)){
871
872 Set<RegistrationDTO> blockingRegs;
873 if(regDto.registration().isPersisted()){
874 blockingRegs = getWorkingSetService().loadBlockingRegistrations(registrationUuid);
875 } else {
876 blockingRegs = new HashSet<RegistrationDTO>(getWorkingSetService().makeDTOs(regDto.registration().getBlockedBy()));
877 }
878 getView().setBlockingRegistrations(registrationUuid, blockingRegs);
879 } else if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
880 getView().openDetailsPopup("Validation Problems", regDto.getValidationProblems());
881 }
882 }
883
884 @Override
885 public ICdmEntityUuidCacher getCache() {
886 return cache;
887 }
888
889 @Override
890 public void addRootEntity(CdmBase entity) {
891 rootEntities.add(entity);
892 cache.load(entity);
893 }
894
895 @Override
896 public Collection<CdmBase> getRootEntities() {
897 return rootEntities;
898 }
899
900 @Override
901 public void destroy() throws Exception {
902 super.destroy();
903 disposeCache();
904 }
905
906 @Override
907 public void disposeCache() {
908 cache.dispose();
909 }
910
911 public boolean canCreateNameRegistrationFor(TaxonName name) {
912 return registrationWorkflowService.canCreateNameRegistrationFor(workingset, name);
913 }
914
915 public boolean checkWokingsetContainsProtologe(TaxonName name) {
916 return registrationWorkflowService.checkWokingsetContainsProtologe(workingset, name);
917 }
918 }