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
        });
146
        teamButton.setDescription("Add team");
147

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

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

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

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

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

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

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

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

    
187
        newValue = HibernateProxyHelper.deproxy(newValue);
188

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

    
192
        if(newValue != null) {
193

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

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

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

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

    
213
                personsListEditor.registerParentFieldGroup(fieldGroup);
214

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

    
243

    
244
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
245

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

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

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

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

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

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

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

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

    
297
        //need to commit the subfields propagation through the fielGroups is not enough
298
        personField.commit();
299
        personsListEditor.commit();
300
        super.commit();
301

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

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

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

    
315
        }
316
    }
317

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

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

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

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

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

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

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

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

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

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

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

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

    
411

    
412

    
413
}
(4-4/5)