Project

General

Profile

Download (9.55 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.vaadin.mvp;
10

    
11
import java.util.Collection;
12
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.UUID;
15

    
16
import org.apache.log4j.Logger;
17
import org.hibernate.HibernateException;
18

    
19
import eu.etaxonomy.cdm.api.service.IService;
20
import eu.etaxonomy.cdm.cache.CdmTransientEntityCacher;
21
import eu.etaxonomy.cdm.debug.PersistentContextAnalyzer;
22
import eu.etaxonomy.cdm.model.ICdmCacher;
23
import eu.etaxonomy.cdm.model.common.CdmBase;
24
import eu.etaxonomy.cdm.model.common.User;
25
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
26
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
27
import eu.etaxonomy.cdm.service.CdmStore;
28
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
29
import eu.etaxonomy.cdm.vaadin.permission.UserHelper;
30
import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
31
import eu.etaxonomy.vaadin.mvp.event.EditorPreSaveEvent;
32
import eu.etaxonomy.vaadin.mvp.event.EditorSaveEvent;
33

    
34
/**
35
 * Provides generic save operations of modified cdm entities.
36
 *
37
 * @author a.kohlbecker
38
 * @since Apr 5, 2017
39
 *
40
 */
41
public abstract class CdmEditorPresenterBase<DTO, CDM extends CdmBase, V extends ApplicationView<?>> extends AbstractEditorPresenter<DTO, V>
42
    implements CachingPresenter {
43

    
44
    private static final long serialVersionUID = 2218185546277084261L;
45

    
46
    private static final Logger logger = Logger.getLogger(CdmEditorPresenterBase.class);
47

    
48
    /**
49
     * if not null, this CRUD set is to be used to create a CdmAuthoritiy for the base entity which will be
50
     * granted to the current use as long this grant is not assigned yet.
51
     */
52
    protected EnumSet<CRUD> crud = null;
53

    
54

    
55
    private ICdmCacher cache;
56

    
57
    private java.util.Collection<CdmBase> rootEntities = new HashSet<>();
58

    
59
    public CdmEditorPresenterBase() {
60
        super();
61
        logger.trace(this._toString() + " constructor");
62
    }
63

    
64
    CdmStore<CDM, IService<CDM>> store ;
65

    
66
    protected CdmAuthority newAuthorityCreated;
67

    
68
    protected CdmStore<CDM, IService<CDM>> getStore() {
69
        if(store == null){
70
            store = new CdmStore<>(getRepo(), getService());
71
        }
72
        return store;
73
    }
74

    
75
    @Override
76
    protected DTO loadBeanById(Object identifier) {
77

    
78
        CDM cdmEntitiy;
79
        if(identifier != null) {
80
            UUID uuidIdentifier = (UUID)identifier;
81
            // CdmAuthority is needed before the bean is loaded into the session.
82
            // otherwise adding the authority to the user would cause a flush
83
            cdmEntitiy = loadCdmEntity(uuidIdentifier);
84
            guaranteePerEntityCRUDPermissions(cdmEntitiy);
85
        } else {
86
            cdmEntitiy = loadCdmEntity(null);
87
            if(cdmEntitiy != null){
88
                guaranteePerEntityCRUDPermissions(cdmEntitiy);
89
            }
90
        }
91
        cache = new CdmTransientEntityCacher(this);
92
        // need to use load but put see #7214
93
        cdmEntitiy = cache.load(cdmEntitiy);
94
        addRootEntity(cdmEntitiy);
95

    
96
        DTO dto = createDTODecorator(cdmEntitiy);
97

    
98
        return dto;
99
    }
100

    
101
    /**
102
     * @param cdmEntitiy
103
     * @return
104
     */
105
    protected abstract DTO createDTODecorator(CDM cdmEntitiy);
106

    
107
    /**
108
     * @param cdmEntitiy
109
     */
110
    @Override
111
    protected void adaptToUserPermission(DTO dto) {
112

    
113
        CDM cdmEntitiy = cdmEntity(dto);
114

    
115
        UserHelper userHelper = UserHelper.fromSession();
116
        boolean canDelte = userHelper.userHasPermission(cdmEntitiy, CRUD.DELETE);
117
        boolean canEdit = userHelper.userHasPermission(cdmEntitiy, CRUD.UPDATE);
118

    
119
        User user = userHelper.user();
120

    
121
        if(AbstractCdmPopupEditor.class.isAssignableFrom(getView().getClass())){
122
            AbstractCdmPopupEditor popupView = ((AbstractCdmPopupEditor)getView());
123

    
124
            if(!canEdit){
125
                popupView.setReadOnly(true); // never reset true to false here!
126
                logger.debug("setting editor to readonly");
127
            }
128
            if(!canDelte){
129
                popupView.withDeleteButton(false);
130
                logger.debug("removing delete button");
131
            }
132
        }
133

    
134
    }
135

    
136
    /**
137
     * @param dto
138
     * @return
139
     */
140
    protected abstract CDM cdmEntity(DTO dto);
141

    
142
    /**
143
     * @param identifier
144
     * @return
145
     */
146
    protected abstract CDM loadCdmEntity(UUID uuid);
147

    
148
    /**
149
     * Grant per entity CdmAuthority to the current user <b>for the bean which is not yet loaded</b>
150
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
151
     */
152
    protected abstract void guaranteePerEntityCRUDPermissions(UUID identifier);
153

    
154
    /**
155
     * Grant per entity CdmAuthority to the current user for the bean which is loaded
156
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
157
     */
158
     protected abstract void guaranteePerEntityCRUDPermissions(CDM bean);
159

    
160
    /**
161
     * @return
162
     */
163
    protected abstract IService<CDM> getService();
164

    
165
    @SuppressWarnings("unchecked")
166
    @Override
167
    // @EventBusListenerMethod // already annotated at super class
168
    public void onEditorPreSaveEvent(EditorPreSaveEvent preSaveEvent){
169

    
170
        if(!isFromOwnView(preSaveEvent)){
171
            return;
172
        }
173
        super.onEditorPreSaveEvent(preSaveEvent);
174
    }
175

    
176
    @Override
177
    // @EventBusListenerMethod // already annotated at super class
178
    public void onEditorSaveEvent(EditorSaveEvent<DTO> saveEvent){
179

    
180
        if(!isFromOwnView(saveEvent)){
181
            return;
182
        }
183

    
184
        // the bean is now updated with the changes made by the user
185
        DTO dto = saveEvent.getBean();
186
        CDM cdmEntity = cdmEntity(dto);
187

    
188
        if(logger.isTraceEnabled()){
189
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
190
            pca.printEntityGraph(System.err);
191
            pca.printCopyEntities(System.err);
192
        }
193

    
194
        if(logger.isTraceEnabled()){
195
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
196
            pca.printEntityGraph(System.err);
197
            pca.printCopyEntities(System.err);
198
        }
199
        EntityChangeEvent<?> changeEvent = null;
200
        try {
201
            dto = preSaveBean(dto);
202
            changeEvent = getStore().saveBean(cdmEntity, (AbstractView<?>) getView());
203

    
204
            if(changeEvent != null){
205
                viewEventBus.publish(this, changeEvent);
206
            }
207
        } catch (HibernateException e){
208
            if(newAuthorityCreated != null){
209
                UserHelper.fromSession().removeAuthorityForCurrentUser(newAuthorityCreated);
210
            }
211
            throw e;
212
        } finally {
213
            postSaveBean(changeEvent);
214
        }
215
    }
216

    
217
    /**
218
     * This method is intended to be used for the following purposes:
219
     * <ol>
220
     *   <li>
221
     *   EditorPresenters for beans with transient properties can overwrite this method to
222
     *   update the beanItem with the changes made to the transient properties.
223
     *   This can be necessary because Vaadin MethodProperties are readonly when no setter is
224
     *   available. This can be the case with transient properties. Automatic updating
225
     *   of the property during the fieldGroup commit does not work in this case.
226
     *   Presenters, however, should <b>operate on DTOs instead, which can implement the missing setter</b>.</li>
227
     *
228
     *   <li>When modifying a bi-directional relation between two instances the user would
229
     *   need to have GrantedAuthorities for both sides of the relationship. This, however is not
230
     *   always possible. As a temporary solution the user can be granted the missing authority just
231
     *   for the time of saving the new relationship. You may also want to implement
232
     *   {@link #postSaveBean(EntityChangeEvent)} in this case.
233
     *   See {@link https://dev.e-taxonomy.eu/redmine/issues/7390 #7390}
234
     *   </li>
235
     * </ol>
236
     *
237
     */
238
    protected DTO preSaveBean(DTO bean) {
239
        // blank implementation, to be implemented by sub classes if needed
240
        return bean;
241
    }
242

    
243
    @Override
244
    protected
245
    final void saveBean(DTO bean){
246
        // blank implementation, since this is not needed in this or any sub class
247
        // see onEditorSaveEvent() instead
248
    }
249

    
250
    /**
251
     * Called after saving the DTO to the persistent storage.
252
     * This method is called in any case even if the save operation failed.
253
     * See {@link  #postSaveBean(EntityChangeEvent)}.
254
     *
255
     * @param changeEvent may be null in case of errors during the save operation
256
     */
257
    protected void postSaveBean(EntityChangeEvent changeEvent) {
258
        // blank implementation, to be implemented by sub classes if needed
259
    }
260

    
261
    @Override
262
    protected void deleteBean(DTO bean){
263
        CDM cdmEntity = cdmEntity(bean);
264
        EntityChangeEvent changeEvent = getStore().deleteBean(cdmEntity, (AbstractView) getView());
265
        if(changeEvent != null){
266
            viewEventBus.publish(this, changeEvent);
267
        }
268

    
269
    }
270

    
271
    /**
272
     * @param crud
273
     */
274
    public void setGrantsForCurrentUser(EnumSet<CRUD> crud) {
275
        this.crud = crud;
276

    
277
    }
278

    
279
    /**
280
     * {@inheritDoc}
281
     */
282
    @Override
283
    public ICdmCacher getCache() {
284
        return cache;
285
    }
286

    
287
    /**
288
     * {@inheritDoc}
289
     */
290
    @Override
291
    public void addRootEntity(CdmBase entity) {
292
        rootEntities.add(entity);
293
    }
294

    
295

    
296
    /**
297
     * {@inheritDoc}
298
     */
299
    @Override
300
    public Collection<CdmBase> getRootEntities() {
301
        return rootEntities;
302
    }
303

    
304

    
305
}
(12-12/12)