Project

General

Profile

Download (15.4 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.shared.AbstractFieldState;
24
import com.vaadin.ui.Button;
25
import com.vaadin.ui.Component;
26
import com.vaadin.ui.CssLayout;
27
import com.vaadin.ui.Field;
28
import com.vaadin.ui.themes.ValoTheme;
29

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

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

    
56
    private static final long serialVersionUID = 660806402243118112L;
57

    
58
    private static final String PRIMARY_STYLE = "v-team-or-person-field";
59

    
60
    private CssLayout root = new CssLayout();
61
    private CssLayout toolBar= new CssLayout();
62
    private CssLayout compositeWrapper = new CssLayout();
63

    
64
    private ReloadableLazyComboBox<TeamOrPersonBase> teamOrPersonSelect = new ReloadableLazyComboBox<TeamOrPersonBase>(TeamOrPersonBase.class);
65

    
66
    private Button removeButton = ButtonFactory.REMOVE_ALL_ITEMS.createButton();
67
    private Button personButton = new Button(FontAwesome.USER);
68
    private Button teamButton = new Button(FontAwesome.USERS);
69

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

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

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

    
80
    private CdmFilterablePagingProvider<AgentBase, Person> pagingProviderPerson;
81

    
82
    private TeamOrPersonBaseCaptionGenerator.CacheType cacheType;
83

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

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

    
88
        setCaption(caption);
89

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

    
93

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

    
101

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

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

    
111
        updateToolBarButtonStates();
112
    }
113

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

    
120
        teamOrPersonSelect.addValueChangeListener(e -> {
121
            setValue(teamOrPersonSelect.getValue(), false, true);
122
            updateToolBarButtonStates();
123
        });
124
        teamOrPersonSelect.setWidthUndefined();
125

    
126
        removeButton.addClickListener(e -> {
127
            setValue(null, false, true);
128
            updateToolBarButtonStates();
129
        });
130
        removeButton.setDescription("Remove");
131

    
132
        personButton.addClickListener(e -> {
133
            setValue(Person.NewInstance(), false, true);
134

    
135
        });
136
        personButton.setDescription("Add person");
137
        teamButton.addClickListener(e -> {
138
            setValue(Team.NewInstance(), false, true);
139
        });
140
        teamButton.setDescription("Add team");
141

    
142
        toolBar.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP + " toolbar");
143
        toolBar.addComponents(teamOrPersonSelect,  removeButton, personButton, teamButton);
144

    
145
        compositeWrapper.setStyleName("margin-wrapper");
146
        compositeWrapper.addComponent(toolBar);
147

    
148
        root.setPrimaryStyleName(PRIMARY_STYLE);
149
        root.addComponent(compositeWrapper);
150
        return root;
151
    }
152

    
153
    /**
154
     * {@inheritDoc}
155
     */
156
    @Override
157
    public Class getType() {
158
        return TeamOrPersonBase.class;
159
    }
160

    
161
    private void updateToolBarButtonStates(){
162
        TeamOrPersonBase<?> val = getInternalValue();
163
        boolean userCanCreate = VaadinUserHelper.fromSession().userHasPermission(Person.class, "CREATE");
164

    
165
        teamOrPersonSelect.setVisible(val == null);
166
        if(teamOrPersonSelect.getValue() != val){
167
            teamOrPersonSelect.clear();
168
        }
169
        removeButton.setVisible(val != null);
170
        personButton.setEnabled(userCanCreate && val == null);
171
        teamButton.setEnabled(userCanCreate && val == null);
172
    }
173

    
174
    /**
175
     * {@inheritDoc}
176
     */
177
    @Override
178
    protected void setInternalValue(TeamOrPersonBase<?> newValue) {
179

    
180
        TeamOrPersonBase<?> oldValue = getValue();
181
        super.setInternalValue(newValue);
182

    
183
        newValue = HibernateProxyHelper.deproxy(newValue);
184

    
185
        compositeWrapper.removeAllComponents();
186
        compositeWrapper.addComponent(toolBar);
187

    
188
        if(newValue != null) {
189

    
190
            if(Person.class.isAssignableFrom(newValue.getClass())){
191
                // value is a Person
192
                compositeWrapper.addComponent(personField);
193

    
194
                personField.setValue((Person) newValue);
195
                personField.registerParentFieldGroup(fieldGroup);
196

    
197
            }
198
            else if(Team.class.isAssignableFrom(newValue.getClass())){
199
                // otherwise it a Team
200
                compositeWrapper.addComponents(titleField, nomenclaturalTitleField, personsListEditor);
201

    
202
                titleField.bindTo(fieldGroup, "titleCache", "protectedTitleCache");
203
                nomenclaturalTitleField.bindTo(fieldGroup, "nomenclaturalTitle", "protectedNomenclaturalTitleCache");
204
                fieldGroup.setItemDataSource(new BeanItem<Team>((Team)newValue));
205
                boolean readonlyState = personsListEditor.isReadOnly();
206
                fieldGroup.bind(personsListEditor, "teamMembers"); // here personField is set readonly since setTeamMembers does not exist
207
                personsListEditor.setReadOnly(readonlyState); // fixing the readonly state
208

    
209
                personsListEditor.registerParentFieldGroup(fieldGroup);
210

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

    
239

    
240
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
241

    
242
        VaadinUserHelper userHelper = VaadinUserHelper.fromSession();
243
        boolean canEdit = teamOrPerson == null || !teamOrPerson.isPersited() || userHelper.userHasPermission(teamOrPerson, CRUD.UPDATE);
244
        if(!canEdit){
245
            getPropertyDataSource().setReadOnly(true);
246
            setReadOnlyComponents(true);
247
        }
248
    }
249

    
250

    
251
    /**
252
     * {@inheritDoc}
253
     */
254
    @Override
255
    protected void addDefaultStyles() {
256
        // no default styles here
257
    }
258

    
259
    /**
260
     * {@inheritDoc}
261
     */
262
    @Override
263
    public FieldGroup getFieldGroup() {
264
        return fieldGroup;
265
    }
266

    
267
    public Component[] getCachFields(){
268
        return new Component[]{titleField, nomenclaturalTitleField};
269
    }
270

    
271
    /**
272
     * @return the teamOrPersonSelect
273
     */
274
    public LazyComboBox<TeamOrPersonBase> getTeamOrPersonSelect() {
275
        return teamOrPersonSelect;
276
    }
277

    
278
    /**
279
     * {@inheritDoc}
280
     */
281
    @SuppressWarnings("unchecked")
282
    @Override
283
    public void commit() throws SourceException, InvalidValueException {
284

    
285
        //need to commit the subfields propagation through the fielGroups is not enough
286
        personField.commit();
287
        personsListEditor.commit();
288
        if(!getState(false).readOnly && getPropertyDataSource().isReadOnly()){
289
            // the TeamOrPersonBase Editor (remove, addPerson, addTeam) is not readonly
290
            // thus removing the TeamOrPerson is allowed. In case the datasource is readonly
291
            // due to missing user grants for the TeamOrPerson it must be set to readWrite to
292
            // make it possible to change the property of the parent
293
            getPropertyDataSource().setReadOnly(false);
294
        }
295

    
296
        super.commit();
297

    
298
        if(hasNullContent()){
299
            getPropertyDataSource().setValue(null);
300
            setValue(null);
301
        }
302

    
303
        TeamOrPersonBase<?> bean = getValue();
304
        if(bean != null && bean instanceof Team){
305
            boolean isUnsaved = bean.getId() == 0;
306
            if(isUnsaved){
307
                VaadinUserHelper.fromSession().createAuthorityForCurrentUser(bean, EnumSet.of(CRUD.UPDATE, CRUD.DELETE), null);
308
            }
309
        }
310
    }
311

    
312
    /**
313
     * {@inheritDoc}
314
     */
315
    @Override
316
    protected List<Field> nullValueCheckIgnoreFields() {
317

    
318
        List<Field> ignoreFields =  super.nullValueCheckIgnoreFields();
319
        ignoreFields.add(personField);
320
        ignoreFields.add(nomenclaturalTitleField.getUnlockSwitch());
321
        if(nomenclaturalTitleField.getUnlockSwitch().getValue().booleanValue() == false){
322
            ignoreFields.add(nomenclaturalTitleField.getTextField());
323
        }
324
        ignoreFields.add(titleField.getUnlockSwitch());
325
        if(titleField.getUnlockSwitch().getValue().booleanValue() == false){
326
            ignoreFields.add(titleField.getTextField());
327
        }
328
        return ignoreFields;
329
    }
330

    
331
    /**
332
     * {@inheritDoc}
333
     */
334
    @Override
335
    public boolean hasNullContent() {
336

    
337
        TeamOrPersonBase<?> bean = getValue();
338
        if(bean == null) {
339
            return true;
340
        }
341
        if(bean instanceof Team){
342
            // --- Team
343
            return super.hasNullContent();
344
        } else {
345
            // --- Person
346
            return personField.hasNullContent();
347
        }
348
    }
349

    
350
    public void setFilterableTeamPagingProvider(CdmFilterablePagingProvider<AgentBase, TeamOrPersonBase> pagingProvider, CachingPresenter cachingPresenter){
351
        teamOrPersonSelect.loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
352
        ToOneRelatedEntityReloader<TeamOrPersonBase> teamOrPersonReloader = new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter);
353
        teamOrPersonSelect.addValueChangeListener(teamOrPersonReloader );
354
    }
355

    
356
    public void setFilterablePersonPagingProvider(CdmFilterablePagingProvider<AgentBase, Person> pagingProvider, CachingPresenter cachingPresenter){
357

    
358
        teamOrPersonSelect.addValueChangeListener(new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter));
359

    
360
        personsListEditor.setEntityFieldInstantiator(new EntityFieldInstantiator<PersonField>() {
361

    
362
            @Override
363
            public PersonField createNewInstance() {
364
                PersonField f = new PersonField();
365
                f.setAllowNewEmptyEntity(true); // otherwise new entities can not be added to the personsListEditor
366
                f.getPersonSelect().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
367
                f.getPersonSelect().setCaptionGenerator(new TeamOrPersonBaseCaptionGenerator<Person>(cacheType));
368
                f.getPersonSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Person>(f.getPersonSelect(), cachingPresenter));
369
                return f;
370
            }
371
        });
372
    }
373

    
374
    @Override
375
    public void setValue(TeamOrPersonBase<?> newFieldValue) {
376
        // ignore readonly states of the datasource
377
        setValue(newFieldValue, false, !hasSharedStateReadOnly());
378
    }
379

    
380
    protected boolean hasSharedStateReadOnly(){
381
        AbstractFieldState sharedState = getState(false);
382
        return sharedState.readOnly;
383
    }
384

    
385

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

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

    
401
    }
402

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

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

    
425

    
426

    
427
}
(6-6/8)