Project

General

Profile

Download (16 KB) Statistics
| Branch: | Tag: | Revision:
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.component.common;
10

    
11
import java.util.Arrays;
12
import java.util.EnumSet;
13
import java.util.List;
14

    
15
import org.vaadin.viritin.fields.LazyComboBox;
16

    
17
import com.vaadin.data.Validator.InvalidValueException;
18
import com.vaadin.data.fieldgroup.BeanFieldGroup;
19
import com.vaadin.data.fieldgroup.FieldGroup;
20
import com.vaadin.data.util.BeanItem;
21
import com.vaadin.server.FontAwesome;
22
import com.vaadin.server.UserError;
23
import com.vaadin.ui.Button;
24
import com.vaadin.ui.Component;
25
import com.vaadin.ui.CssLayout;
26
import com.vaadin.ui.Field;
27
import com.vaadin.ui.themes.ValoTheme;
28

    
29
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
30
import eu.etaxonomy.cdm.model.agent.AgentBase;
31
import eu.etaxonomy.cdm.model.agent.Person;
32
import eu.etaxonomy.cdm.model.agent.Team;
33
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
34
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
35
import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
36
import eu.etaxonomy.cdm.vaadin.event.ToOneRelatedEntityReloader;
37
import eu.etaxonomy.cdm.vaadin.permission.UserHelper;
38
import eu.etaxonomy.cdm.vaadin.util.TeamOrPersonBaseCaptionGenerator;
39
import eu.etaxonomy.cdm.vaadin.util.converter.CdmBaseDeproxyConverter;
40
import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
41
import eu.etaxonomy.vaadin.component.CompositeCustomField;
42
import eu.etaxonomy.vaadin.component.EntityFieldInstantiator;
43
import eu.etaxonomy.vaadin.component.ReloadableLazyComboBox;
44
import eu.etaxonomy.vaadin.component.SwitchableTextField;
45
import eu.etaxonomy.vaadin.component.ToManyRelatedEntitiesListSelect;
46

    
47
/**
48
 * @author a.kohlbecker
49
 * @since May 11, 2017
50
 *
51
 */
52
public class TeamOrPersonField extends CompositeCustomField<TeamOrPersonBase<?>> {
53

    
54
    private static final long serialVersionUID = 660806402243118112L;
55

    
56
    private static final String PRIMARY_STYLE = "v-team-or-person-field";
57

    
58
    private CssLayout root = new CssLayout();
59
    private CssLayout toolBar= new CssLayout();
60
    private CssLayout compositeWrapper = new CssLayout();
61

    
62
    private ReloadableLazyComboBox<TeamOrPersonBase> teamOrPersonSelect = new ReloadableLazyComboBox<TeamOrPersonBase>(TeamOrPersonBase.class);
63

    
64
    private Button selectConfirmButton = new Button("OK");
65
    private Button removeButton = new Button(FontAwesome.REMOVE);
66
    private Button personButton = new Button(FontAwesome.USER);
67
    private Button teamButton = new Button(FontAwesome.USERS);
68

    
69
    // Fields for case when value is a Person
70
    private PersonField personField = new PersonField();
71

    
72
    // Fields for case when value is a Team
73
    private SwitchableTextField titleField = new SwitchableTextField("Team (bibliographic)");
74
    private SwitchableTextField nomenclaturalTitleField = new SwitchableTextField("Team (nomenclatural)");
75
    private ToManyRelatedEntitiesListSelect<Person, PersonField> personsListEditor = new ToManyRelatedEntitiesListSelect<Person, PersonField>(Person.class, PersonField.class, "Team members");
76

    
77
    private BeanFieldGroup<Team> fieldGroup  = new BeanFieldGroup<>(Team.class);
78

    
79
    private CdmFilterablePagingProvider<AgentBase, Person> pagingProviderPerson;
80

    
81
    private TeamOrPersonBaseCaptionGenerator.CacheType cacheType;
82

    
83
    protected List<Component> editorComponents = Arrays.asList(removeButton, personButton, teamButton, teamOrPersonSelect);
84

    
85
    public TeamOrPersonField(String caption, TeamOrPersonBaseCaptionGenerator.CacheType cacheType){
86

    
87
        setCaption(caption);
88

    
89
        this.cacheType = cacheType;
90
        teamOrPersonSelect.setCaptionGenerator(new TeamOrPersonBaseCaptionGenerator<TeamOrPersonBase>(cacheType));
91

    
92

    
93
        addStyledComponent(teamOrPersonSelect);
94
        addStyledComponent(personField);
95
        addStyledComponent(titleField);
96
        addStyledComponent(nomenclaturalTitleField);
97
        addStyledComponent(personsListEditor);
98
        addStyledComponents(selectConfirmButton, removeButton, personButton, teamButton);
99

    
100

    
101
        addSizedComponent(root);
102
        addSizedComponent(compositeWrapper);
103
        addSizedComponent(personField);
104
        addSizedComponent(titleField);
105
        addSizedComponent(nomenclaturalTitleField);
106
        addSizedComponent(personsListEditor);
107

    
108
        setConverter(new CdmBaseDeproxyConverter<TeamOrPersonBase<?>>());
109

    
110
        updateToolBarButtonStates();
111
    }
112

    
113
    /**
114
     * {@inheritDoc}
115
     */
116
    @Override
117
    protected Component initContent() {
118

    
119
        teamOrPersonSelect.addValueChangeListener(e -> {
120
            selectConfirmButton.setEnabled(teamOrPersonSelect.getValue() != null);
121
            selectConfirmButton.addStyleName(ValoTheme.BUTTON_PRIMARY);
122
        });
123
        teamOrPersonSelect.setWidthUndefined();
124

    
125
        selectConfirmButton.setEnabled(teamOrPersonSelect.getValue() != null);
126
        selectConfirmButton.addClickListener(e -> {
127
            // new entitiy being set, reset the readonly state
128
//            resetReadOnlyComponents();
129
//            getPropertyDataSource().setReadOnly(false);
130
            setValue(teamOrPersonSelect.getValue(), false, true);
131
            teamOrPersonSelect.clear();
132
            updateToolBarButtonStates();
133
        });
134
        removeButton.addClickListener(e -> {
135
//            resetReadOnlyComponents();
136
//            getPropertyDataSource().setReadOnly(false);
137
            setValue(null, false, true);
138
            updateToolBarButtonStates();
139
        });
140
        removeButton.setDescription("Remove");
141

    
142
        personButton.addClickListener(e -> {
143
            setValue(Person.NewInstance(), false, true); // FIXME add SelectField or open select dialog, use ToOneSelect field!!
144

    
145
        });
146
        personButton.setDescription("Add person");
147
        teamButton.addClickListener(e -> {
148
            setValue(Team.NewInstance(), false, true); // FIXME add SelectField or open select dialog, use ToOneSelect field!!
149
        });
150
        teamButton.setDescription("Add team");
151

    
152
        toolBar.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP + " toolbar");
153
        toolBar.addComponents(teamOrPersonSelect, selectConfirmButton,  removeButton, personButton, teamButton);
154

    
155
        compositeWrapper.setStyleName("margin-wrapper");
156
        compositeWrapper.addComponent(toolBar);
157

    
158
        root.setPrimaryStyleName(PRIMARY_STYLE);
159
        root.addComponent(compositeWrapper);
160
        return root;
161
    }
162

    
163
    /**
164
     * {@inheritDoc}
165
     */
166
    @Override
167
    public Class getType() {
168
        return TeamOrPersonBase.class;
169
    }
170

    
171
    private void updateToolBarButtonStates(){
172
        TeamOrPersonBase<?> val = getInternalValue();
173
        boolean userCanCreate = UserHelper.fromSession().userHasPermission(Person.class, "CREATE");
174

    
175
        teamOrPersonSelect.setVisible(val == null);
176
        selectConfirmButton.setVisible(val == null);
177
        removeButton.setVisible(val != null);
178
        personButton.setEnabled(userCanCreate && val == null);
179
        teamButton.setEnabled(userCanCreate && val == null);
180
    }
181

    
182
    /**
183
     * {@inheritDoc}
184
     */
185
    @Override
186
    protected void setInternalValue(TeamOrPersonBase<?> newValue) {
187

    
188
        TeamOrPersonBase<?> oldValue = getValue();
189
        super.setInternalValue(newValue);
190

    
191
        newValue = HibernateProxyHelper.deproxy(newValue);
192

    
193
        compositeWrapper.removeAllComponents();
194
        compositeWrapper.addComponent(toolBar);
195

    
196
        if(newValue != null) {
197

    
198
            if(Person.class.isAssignableFrom(newValue.getClass())){
199
                // value is a Person
200
                compositeWrapper.addComponent(personField);
201

    
202
                personField.setValue((Person) newValue);
203
                personField.registerParentFieldGroup(fieldGroup);
204

    
205
            }
206
            else if(Team.class.isAssignableFrom(newValue.getClass())){
207
                // otherwise it a Team
208
                compositeWrapper.addComponents(titleField, nomenclaturalTitleField, personsListEditor);
209

    
210
                titleField.bindTo(fieldGroup, "titleCache", "protectedTitleCache");
211
                nomenclaturalTitleField.bindTo(fieldGroup, "nomenclaturalTitle", "protectedNomenclaturalTitleCache");
212
                fieldGroup.setItemDataSource(new BeanItem<Team>((Team)newValue));
213
                boolean readonlyState = personsListEditor.isReadOnly();
214
                fieldGroup.bind(personsListEditor, "teamMembers"); // here personField is set readonly since setTeamMembers does not exist
215
                personsListEditor.setReadOnly(readonlyState); // fixing the readonly state
216

    
217
                personsListEditor.registerParentFieldGroup(fieldGroup);
218

    
219
            } else {
220
                setComponentError(new UserError("TeamOrPersonField Error: Unsupported value type: " + newValue.getClass().getName()));
221
            }
222
        } else {
223
            if(oldValue != null){
224
                // value is null --> clean up all nested fields
225
                // allow replacing old content in the editor by null
226
                setReadOnlyComponents(false);
227
                if(oldValue instanceof Person){
228
                    personField.unregisterParentFieldGroup(fieldGroup);
229
                    personField.setReadOnly(false);
230
                    personField.setValue((Person) null);
231
                } else {
232
                    titleField.unbindFrom(fieldGroup);
233
                    nomenclaturalTitleField.unbindFrom(fieldGroup);
234
                    fieldGroup.unbind(personsListEditor);
235
                    fieldGroup.setItemDataSource((Team)null);
236
                    personsListEditor.registerParentFieldGroup(null);
237
                    personsListEditor.setReadOnly(false);
238
                    personsListEditor.setValue(null);
239
                    personsListEditor.registerParentFieldGroup(null);
240
                }
241
            }
242
        }
243
        adaptToUserPermissions(newValue);
244
        updateToolBarButtonStates();
245
    }
246

    
247

    
248
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
249

    
250
        UserHelper userHelper = UserHelper.fromSession();
251
        boolean canEdit = teamOrPerson == null || !teamOrPerson.isPersited() || userHelper.userHasPermission(teamOrPerson, CRUD.UPDATE);
252
        if(!canEdit){
253
            getPropertyDataSource().setReadOnly(true);
254
            setReadOnlyComponents(true);
255
        }
256
    }
257

    
258
//    private void checkUserPermissions(TeamOrPersonBase<?> newValue) {
259
//        boolean userCanEdit = UserHelper.fromSession().userHasPermission(newValue, "DELETE", "UPDATE");
260
//        setEnabled(userCanEdit);
261
//        personsListEditor.setEnabled(userCanEdit);
262
//    }
263

    
264
    /**
265
     * {@inheritDoc}
266
     */
267
    @Override
268
    protected void addDefaultStyles() {
269
        // no default styles here
270
    }
271

    
272
    /**
273
     * {@inheritDoc}
274
     */
275
    @Override
276
    public FieldGroup getFieldGroup() {
277
        return fieldGroup;
278
    }
279

    
280
    public Component[] getCachFields(){
281
        return new Component[]{titleField, nomenclaturalTitleField};
282
    }
283

    
284
    /**
285
     * @return the teamOrPersonSelect
286
     */
287
    public LazyComboBox<TeamOrPersonBase> getTeamOrPersonSelect() {
288
        return teamOrPersonSelect;
289
    }
290

    
291
    /**
292
     * {@inheritDoc}
293
     */
294
    @SuppressWarnings("unchecked")
295
    @Override
296
    public void commit() throws SourceException, InvalidValueException {
297

    
298
        //need to commit the subfields propagation through the fielGroups is not enough
299
        personField.commit();
300
        personsListEditor.commit();
301
        if(!getState(false).readOnly && getPropertyDataSource().isReadOnly()){
302
            // the TeamOrPersonBase Editor (remove, addPerson, addTeam) is not readonly
303
            // thus removing the TeamOrPerson is allowed. In case the datasource is readonly
304
            // due to missing user grants for the TeamOrPerson it must be set to readWrite to
305
            // make it possible to change the property of the parent
306
            getPropertyDataSource().setReadOnly(false);
307
        }
308

    
309
        super.commit();
310

    
311
        if(hasNullContent()){
312
            getPropertyDataSource().setValue(null);
313
            setValue(null);
314
        }
315

    
316
        TeamOrPersonBase<?> bean = getValue();
317
        if(bean != null && bean instanceof Team){
318
            boolean isUnsaved = bean.getId() == 0;
319
            if(isUnsaved){
320
                UserHelper.fromSession().createAuthorityForCurrentUser(bean, EnumSet.of(CRUD.UPDATE, CRUD.DELETE), null);
321
            }
322
        }
323
    }
324

    
325
    /**
326
     * {@inheritDoc}
327
     */
328
    @Override
329
    protected List<Field> nullValueCheckIgnoreFields() {
330

    
331
        List<Field> ignoreFields =  super.nullValueCheckIgnoreFields();
332
        ignoreFields.add(personField);
333
        ignoreFields.add(nomenclaturalTitleField.getUnlockSwitch());
334
        if(nomenclaturalTitleField.getUnlockSwitch().getValue().booleanValue() == false){
335
            ignoreFields.add(nomenclaturalTitleField.getTextField());
336
        }
337
        ignoreFields.add(titleField.getUnlockSwitch());
338
        if(titleField.getUnlockSwitch().getValue().booleanValue() == false){
339
            ignoreFields.add(titleField.getTextField());
340
        }
341
        return ignoreFields;
342
    }
343

    
344
    /**
345
     * {@inheritDoc}
346
     */
347
    @Override
348
    public boolean hasNullContent() {
349

    
350
        TeamOrPersonBase<?> bean = getValue();
351
        if(bean == null) {
352
            return true;
353
        }
354
        if(bean instanceof Team){
355
            // --- Team
356
            return super.hasNullContent();
357
        } else {
358
            // --- Person
359
            return personField.hasNullContent();
360
        }
361
    }
362

    
363
    public void setFilterableTeamPagingProvider(CdmFilterablePagingProvider<AgentBase, TeamOrPersonBase> pagingProvider, CachingPresenter cachingPresenter){
364
        teamOrPersonSelect.loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
365
        ToOneRelatedEntityReloader<TeamOrPersonBase> teamOrPersonReloader = new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter);
366
        teamOrPersonSelect.addValueChangeListener(teamOrPersonReloader );
367
    }
368

    
369
    public void setFilterablePersonPagingProvider(CdmFilterablePagingProvider<AgentBase, Person> pagingProvider, CachingPresenter cachingPresenter){
370

    
371
        teamOrPersonSelect.addValueChangeListener(new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter));
372

    
373
        personsListEditor.setEntityFieldInstantiator(new EntityFieldInstantiator<PersonField>() {
374

    
375
            @Override
376
            public PersonField createNewInstance() {
377
                PersonField f = new PersonField();
378
                f.setAllowNewEmptyEntity(true); // otherwise new entities can not be added to the personsListEditor
379
                f.getPersonSelect().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
380
                f.getPersonSelect().setCaptionGenerator(new TeamOrPersonBaseCaptionGenerator<Person>(cacheType));
381
                f.getPersonSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Person>(f.getPersonSelect(), cachingPresenter));
382
                return f;
383
            }
384
        });
385
    }
386

    
387
    /**
388
     * {@inheritDoc}
389
     */
390
    @Override
391
    public void setReadOnly(boolean readOnly) {
392
//        super.setReadOnly(readOnly); // moved into setEditorReadOnly()
393
        setReadOnlyComponents(readOnly);
394
    }
395

    
396
    public void setEditorReadOnly(boolean readOnly) {
397
        super.setReadOnly(readOnly);
398
        for(Component c : editorComponents){
399
            applyReadOnlyState(c, readOnly);
400
        }
401

    
402
    }
403

    
404
    /**
405
     * Reset the readonly state of nested components to <code>false</code>.
406
     */
407
    protected void resetReadOnlyComponents() {
408
        if(!isReadOnly()){
409
            setReadOnlyComponents(false);
410
        }
411
    }
412

    
413
    /**
414
     * Set the nested components (team or person fields) to read only but
415
     * keep the state of the <code>TeamOrPersonField</code> untouched so
416
     * that the <code>teamOrPersonSelect</code>, <code>removeButton</code>,
417
     * <code>personButton</code> and <code>teamButton</code> stay operational.
418
     *
419
     * @param readOnly
420
     */
421
    protected void setReadOnlyComponents(boolean readOnly) {
422
        setDeepReadOnly(readOnly, getContent(), editorComponents);
423
        updateCaptionReadonlyNotice(readOnly);
424
    }
425

    
426

    
427

    
428
}
(4-4/5)