Project

General

Profile

Download (15 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
    public TeamOrPersonField(String caption, TeamOrPersonBaseCaptionGenerator.CacheType cacheType){
84

    
85
        setCaption(caption);
86

    
87
        this.cacheType = cacheType;
88
        teamOrPersonSelect.setCaptionGenerator(new TeamOrPersonBaseCaptionGenerator<TeamOrPersonBase>(cacheType));
89

    
90

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

    
98

    
99
        addSizedComponent(root);
100
        addSizedComponent(compositeWrapper);
101
        addSizedComponent(personField);
102
        addSizedComponent(titleField);
103
        addSizedComponent(nomenclaturalTitleField);
104
        addSizedComponent(personsListEditor);
105

    
106
        setConverter(new CdmBaseDeproxyConverter<TeamOrPersonBase<?>>());
107

    
108
        updateToolBarButtonStates();
109
    }
110

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

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

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

    
138
        personButton.addClickListener(e -> {
139
            setValue(Person.NewInstance()); // FIXME add SelectField or open select dialog, use ToOneSelect field!!
140

    
141
        });
142
        personButton.setDescription("Add person");
143
        teamButton.addClickListener(e -> {
144
            setValue(Team.NewInstance()); // FIXME add SelectField or open select dialog, use ToOneSelect field!!
145
            // updateToolBarButtonStates();
146
        });
147
        teamButton.setDescription("Add team");
148

    
149
        toolBar.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP + " toolbar");
150
        toolBar.addComponents(teamOrPersonSelect, selectConfirmButton,  removeButton, personButton, teamButton);
151

    
152
        compositeWrapper.setStyleName("margin-wrapper");
153
        compositeWrapper.addComponent(toolBar);
154

    
155
        root.setPrimaryStyleName(PRIMARY_STYLE);
156
        root.addComponent(compositeWrapper);
157
        return root;
158
    }
159

    
160
    /**
161
     * {@inheritDoc}
162
     */
163
    @Override
164
    public Class getType() {
165
        return TeamOrPersonBase.class;
166
    }
167

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

    
172
        teamOrPersonSelect.setVisible(val == null);
173
        selectConfirmButton.setVisible(val == null);
174
        removeButton.setVisible(val != null);
175
        personButton.setEnabled(userCanCreate && val == null);
176
        teamButton.setEnabled(userCanCreate && val == null);
177
    }
178

    
179
    /**
180
     * {@inheritDoc}
181
     */
182
    @Override
183
    protected void setInternalValue(TeamOrPersonBase<?> newValue) {
184

    
185
        TeamOrPersonBase<?> oldValue = getValue();
186
        super.setInternalValue(newValue);
187

    
188
        newValue = HibernateProxyHelper.deproxy(newValue);
189

    
190
        compositeWrapper.removeAllComponents();
191
        compositeWrapper.addComponent(toolBar);
192

    
193
        if(newValue != null) {
194

    
195
            if(Person.class.isAssignableFrom(newValue.getClass())){
196
                // value is a Person
197
                compositeWrapper.addComponent(personField);
198

    
199
                personField.setValue((Person) newValue);
200
                personField.registerParentFieldGroup(fieldGroup);
201

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

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

    
214
                personsListEditor.registerParentFieldGroup(fieldGroup);
215

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

    
244

    
245
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
246

    
247
        if(teamOrPerson == null){
248
            return;
249
        }
250

    
251
        UserHelper userHelper = UserHelper.fromSession();
252
        boolean canEdit = !teamOrPerson.isPersited() || userHelper.userHasPermission(teamOrPerson, CRUD.UPDATE);
253
        if(!canEdit){
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
        super.commit();
302

    
303
        TeamOrPersonBase<?> bean = getValue();
304
        if(bean != null && bean instanceof Team){
305

    
306
            boolean isUnsaved = bean.getId() == 0;
307
            if(isUnsaved){
308
                UserHelper.fromSession().createAuthorityForCurrentUser(bean, EnumSet.of(CRUD.UPDATE, CRUD.DELETE), null);
309
            }
310
        }
311

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

    
316
        }
317
    }
318

    
319
    /**
320
     * {@inheritDoc}
321
     */
322
    @Override
323
    protected List<Field> nullValueCheckIgnoreFields() {
324

    
325
        List<Field> ignoreFields =  super.nullValueCheckIgnoreFields();
326
        ignoreFields.add(personField);
327
        ignoreFields.add(nomenclaturalTitleField.getUnlockSwitch());
328
        if(nomenclaturalTitleField.getUnlockSwitch().getValue().booleanValue() == false){
329
            ignoreFields.add(nomenclaturalTitleField.getTextField());
330
        }
331
        ignoreFields.add(titleField.getUnlockSwitch());
332
        if(titleField.getUnlockSwitch().getValue().booleanValue() == false){
333
            ignoreFields.add(titleField.getTextField());
334
        }
335
        return ignoreFields;
336
    }
337

    
338
    /**
339
     * {@inheritDoc}
340
     */
341
    @Override
342
    public boolean hasNullContent() {
343

    
344
        TeamOrPersonBase<?> bean = getValue();
345
        if(bean == null) {
346
            return true;
347
        }
348
        if(bean instanceof Team){
349
            // --- Team
350
            return super.hasNullContent();
351
        } else {
352
            // --- Person
353
            return personField.hasNullContent();
354
        }
355
    }
356

    
357
    public void setFilterableTeamPagingProvider(CdmFilterablePagingProvider<AgentBase, TeamOrPersonBase> pagingProvider, CachingPresenter cachingPresenter){
358
        teamOrPersonSelect.loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
359
        ToOneRelatedEntityReloader<TeamOrPersonBase> teamOrPersonReloader = new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter);
360
        teamOrPersonSelect.addValueChangeListener(teamOrPersonReloader );
361
    }
362

    
363
    public void setFilterablePersonPagingProvider(CdmFilterablePagingProvider<AgentBase, Person> pagingProvider, CachingPresenter cachingPresenter){
364

    
365
        teamOrPersonSelect.addValueChangeListener(new ToOneRelatedEntityReloader<TeamOrPersonBase>(teamOrPersonSelect, cachingPresenter));
366

    
367
        personsListEditor.setEntityFieldInstantiator(new EntityFieldInstantiator<PersonField>() {
368

    
369
            @Override
370
            public PersonField createNewInstance() {
371
                PersonField f = new PersonField();
372
                f.setAllowNewEmptyEntity(true); // otherwise new entities can not be added to the personsListEditor
373
                f.getPersonSelect().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
374
                f.getPersonSelect().setCaptionGenerator(new TeamOrPersonBaseCaptionGenerator<Person>(cacheType));
375
                f.getPersonSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Person>(f.getPersonSelect(), cachingPresenter));
376
                return f;
377
            }
378
        });
379
    }
380

    
381
    /**
382
     * {@inheritDoc}
383
     */
384
    @Override
385
    public void setReadOnly(boolean readOnly) {
386
        super.setReadOnly(readOnly);
387
        setReadOnlyComponents(readOnly);
388
    }
389

    
390
    /**
391
     * Reset the readonly state of nested components to <code>false</code>.
392
     */
393
    protected void resetReadOnlyComponents() {
394
        if(!isReadOnly()){
395
            setReadOnlyComponents(false);
396
        }
397
    }
398

    
399
    /**
400
     * Set the nested components (team or person fields) to read only but
401
     * keep the state of the <code>TeamOrPersonField</code> untouched so
402
     * that the <code>teamOrPersonSelect</code>, <code>removeButton</code>,
403
     * <code>personButton</code> and <code>teamButton</code> stay operational.
404
     *
405
     * @param readOnly
406
     */
407
    public void setReadOnlyComponents(boolean readOnly) {
408
        setDeepReadOnly(readOnly, getContent(), Arrays.asList(removeButton, personButton, teamButton, teamOrPersonSelect));
409
        updateCaptionReadonlyNotice();
410
    }
411

    
412

    
413

    
414
}
(4-4/5)