Project

General

Profile

Download (15.5 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.api.utility.UserHelper;
31
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
32
import eu.etaxonomy.cdm.model.agent.AgentBase;
33
import eu.etaxonomy.cdm.model.agent.Person;
34
import eu.etaxonomy.cdm.model.agent.Team;
35
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
36
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
37
import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
38
import eu.etaxonomy.cdm.service.UserHelperAccess;
39
import eu.etaxonomy.cdm.vaadin.component.ButtonFactory;
40
import eu.etaxonomy.cdm.vaadin.event.ToOneRelatedEntityReloader;
41
import eu.etaxonomy.cdm.vaadin.util.TeamOrPersonBaseCaptionGenerator;
42
import eu.etaxonomy.cdm.vaadin.util.converter.CdmBaseDeproxyConverter;
43
import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
44
import eu.etaxonomy.vaadin.component.CompositeCustomField;
45
import eu.etaxonomy.vaadin.component.EntityFieldInstantiator;
46
import eu.etaxonomy.vaadin.component.ReloadableLazyComboBox;
47
import eu.etaxonomy.vaadin.component.SwitchableTextField;
48
import eu.etaxonomy.vaadin.component.ToManyRelatedEntitiesListSelect;
49

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

    
57
    private static final long serialVersionUID = 660806402243118112L;
58

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

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

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

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

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

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

    
79
    private BeanFieldGroup<Team> fieldGroup  = new BeanFieldGroup<>(Team.class);
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
        });
122
        teamOrPersonSelect.setWidthUndefined();
123

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

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

    
134
        });
135
        personButton.setDescription("Add person");
136
        teamButton.addClickListener(e -> {
137
            setValue(Team.NewInstance(), false, true);
138
            resetReadOnlyComponents();
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

    
163
        TeamOrPersonBase<?> val = getInternalValue();
164
        boolean userCanCreate = UserHelperAccess.userHelper().userHasPermission(Person.class, "CREATE");
165

    
166
        teamOrPersonSelect.setVisible(val == null);
167
        removeButton.setVisible(val != null);
168
        personButton.setEnabled(userCanCreate && val == null);
169
        teamButton.setEnabled(userCanCreate && val == null);
170
    }
171

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

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

    
181
        newValue = HibernateProxyHelper.deproxy(newValue);
182

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

    
186
        if(newValue != null) {
187

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

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

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

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

    
207
                personsListEditor.registerParentFieldGroup(fieldGroup);
208

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

    
237

    
238
    private void adaptToUserPermissions(TeamOrPersonBase teamOrPerson) {
239

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

    
248

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

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

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

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

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

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

    
294
        super.commit();
295

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

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

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

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

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

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

    
348
    public void setFilterableTeamPagingProvider(CdmFilterablePagingProvider<AgentBase, TeamOrPersonBase> pagingProvider, CachingPresenter cachingPresenter){
349
        teamOrPersonSelect.loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
350
        // NOTE:
351
        //   it is important to add the ToOneRelatedEntityReloader to the TeamOrPersonField directly
352
        //   since the value of the select will be immediately passed to the TeamOrPersonField
353
        ToOneRelatedEntityReloader<TeamOrPersonBase<?>> teamOrPersonReloader = new ToOneRelatedEntityReloader<TeamOrPersonBase<?>>(this, cachingPresenter);
354
        this.addValueChangeListener(teamOrPersonReloader);
355
    }
356

    
357
    public void setFilterablePersonPagingProvider(CdmFilterablePagingProvider<AgentBase, Person> pagingProvider, CachingPresenter 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
                // NOTE:
368
                //   it is important to add the ToOneRelatedEntityReloader to the PersonField directly
369
                //   since the value of the select will be immediately passed to the PersonField:
370
                f.addValueChangeListener(new ToOneRelatedEntityReloader<Person>(f, cachingPresenter));
371
                return f;
372
            }
373
        });
374
    }
375

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

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

    
387

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

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

    
403
    }
404

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

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

    
427

    
428

    
429
}
(6-6/8)