Project

General

Profile

Download (15.3 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.event.ToOneRelatedEntityReloader;
38
import eu.etaxonomy.cdm.vaadin.permission.UserHelper;
39
import eu.etaxonomy.cdm.vaadin.util.TeamOrPersonBaseCaptionGenerator;
40
import eu.etaxonomy.cdm.vaadin.util.converter.CdmBaseDeproxyConverter;
41
import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
42
import eu.etaxonomy.vaadin.component.CompositeCustomField;
43
import eu.etaxonomy.vaadin.component.EntityFieldInstantiator;
44
import eu.etaxonomy.vaadin.component.ReloadableLazyComboBox;
45
import eu.etaxonomy.vaadin.component.SwitchableTextField;
46
import eu.etaxonomy.vaadin.component.ToManyRelatedEntitiesListSelect;
47

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

    
55
    private static final long serialVersionUID = 660806402243118112L;
56

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

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

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

    
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(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
            setValue(teamOrPersonSelect.getValue(), false, true);
121
            updateToolBarButtonStates();
122
        });
123
        teamOrPersonSelect.setWidthUndefined();
124

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

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

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

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

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

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

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

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

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

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

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

    
182
        newValue = HibernateProxyHelper.deproxy(newValue);
183

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

    
187
        if(newValue != null) {
188

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

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

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

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

    
208
                personsListEditor.registerParentFieldGroup(fieldGroup);
209

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

    
238

    
239
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
240

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

    
249

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

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

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

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

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

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

    
295
        super.commit();
296

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

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

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

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

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

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

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

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

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

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

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

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

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

    
384

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

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

    
400
    }
401

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

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

    
424

    
425

    
426
}
(6-6/8)