001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Anahide Tchertchian
018 *     Antoine Taillefer
019 */
020package org.nuxeo.ecm.webapp.contentbrowser;
021
022import static org.jboss.seam.ScopeType.CONVERSATION;
023import static org.jboss.seam.ScopeType.EVENT;
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collections;
029import java.util.List;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.jboss.seam.annotations.Factory;
034import org.jboss.seam.annotations.In;
035import org.jboss.seam.annotations.Name;
036import org.jboss.seam.annotations.Observer;
037import org.jboss.seam.annotations.Scope;
038import org.jboss.seam.annotations.intercept.BypassInterceptors;
039import org.nuxeo.ecm.core.api.CoreSession;
040import org.nuxeo.ecm.core.api.DocumentModel;
041import org.nuxeo.ecm.core.api.DocumentRef;
042import org.nuxeo.ecm.core.api.IdRef;
043import org.nuxeo.ecm.core.api.NuxeoException;
044import org.nuxeo.ecm.core.api.VersionModel;
045import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
046import org.nuxeo.ecm.platform.contentview.seam.ContentViewActions;
047import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes;
048import org.nuxeo.ecm.platform.query.api.PageProvider;
049import org.nuxeo.ecm.platform.query.api.PageSelection;
050import org.nuxeo.ecm.platform.types.adapter.TypeInfo;
051import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
052import org.nuxeo.ecm.platform.ui.web.cache.LRUCachingMap;
053import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
054import org.nuxeo.ecm.webapp.helpers.EventNames;
055
056/**
057 * Manages document listings rendering.
058 *
059 * @author Anahide Tchertchian
060 */
061@Name("documentListingActions")
062@Scope(CONVERSATION)
063public class DocumentListingActionsBean implements Serializable {
064
065    private static final long serialVersionUID = 1L;
066
067    private static final Log log = LogFactory.getLog(DocumentListingActionsBean.class);
068
069    public static String DEFAULT_LISTING_LAYOUT = "document_listing";
070
071    @In(create = true)
072    protected transient NavigationContext navigationContext;
073
074    /**
075     * @since 5.6
076     */
077    @In(create = true, required = false)
078    protected transient CoreSession documentManager;
079
080    @In(create = true)
081    protected transient ContentViewActions contentViewActions;
082
083    @In(required = false, create = true)
084    protected transient DocumentsListsManager documentsListsManager;
085
086    // only store 20 entries that cache chosen layout for a given document
087    protected LRUCachingMap<String, String> docTolistings = new LRUCachingMap<String, String>(20);
088
089    protected String currentListingLayoutName = null;
090
091    protected List<String> currentAvailableListingLayoutNames = null;
092
093    // API for current layout in listing mode
094
095    /**
096     * @deprecated this information is now held by content views
097     */
098    @Deprecated
099    public String getLayoutForDocument(DocumentModel doc) {
100        if (doc != null) {
101            String id = doc.getId();
102            if (docTolistings.containsKey(id)) {
103                return docTolistings.get(id);
104            }
105            List<String> availableLayouts = getAvailableLayoutsForDocument(doc);
106            if (availableLayouts != null && !availableLayouts.isEmpty()) {
107                return availableLayouts.get(0);
108            }
109        }
110        return DEFAULT_LISTING_LAYOUT;
111    }
112
113    /**
114     * @deprecated this information is now held by content views
115     */
116    @Deprecated
117    public void setLayoutForDocument(DocumentModel doc, String layoutName) {
118        if (doc == null) {
119            log.error("Cannot set listing layout for null document");
120            return;
121        }
122        String id = doc.getId();
123        docTolistings.put(id, layoutName);
124    }
125
126    /**
127     * @deprecated this information is now held by content views
128     */
129    @Deprecated
130    @Factory(value = "currentListingLayoutName", scope = EVENT)
131    public String getLayoutForCurrentDocument() {
132        if (currentListingLayoutName == null) {
133            DocumentModel currentDocument = navigationContext.getCurrentDocument();
134            currentListingLayoutName = getLayoutForDocument(currentDocument);
135        }
136        return currentListingLayoutName;
137    }
138
139    /**
140     * @deprecated this information is now held by content views
141     */
142    @Deprecated
143    public void setLayoutForCurrentDocument(String layoutName) {
144        DocumentModel currentDocument = navigationContext.getCurrentDocument();
145        setLayoutForDocument(currentDocument, layoutName);
146        currentListingLayoutName = layoutName;
147    }
148
149    /**
150     * @deprecated this information is now held by content views
151     */
152    @Deprecated
153    public List<String> getAvailableLayoutsForDocument(DocumentModel doc) {
154        if (doc == null) {
155            return Collections.emptyList();
156        }
157        TypeInfo typeInfo = doc.getAdapter(TypeInfo.class);
158        String[] layoutNames = typeInfo.getLayouts(BuiltinModes.LISTING, null);
159        List<String> res = new ArrayList<String>();
160        if (layoutNames != null && layoutNames.length > 0) {
161            res.addAll(Arrays.asList(layoutNames));
162        } else {
163            res.add(DEFAULT_LISTING_LAYOUT);
164        }
165        return res;
166    }
167
168    /**
169     * @deprecated this information is now held by content views
170     */
171    @Deprecated
172    @Factory(value = "currentAvailableListingLayoutNames", scope = EVENT)
173    public List<String> getAvailableLayoutsForCurrentDocument() {
174        if (currentAvailableListingLayoutNames == null) {
175            DocumentModel currentDocument = navigationContext.getCurrentDocument();
176            currentAvailableListingLayoutNames = getAvailableLayoutsForDocument(currentDocument);
177        }
178        return currentAvailableListingLayoutNames;
179    }
180
181    /**
182     * @deprecated this information is now held by content views
183     */
184    @Observer(value = { EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED }, create = false)
185    @BypassInterceptors
186    @Deprecated
187    public void documentChanged() {
188        currentListingLayoutName = null;
189        currentAvailableListingLayoutNames = null;
190    }
191
192    // API for AJAX selection in listings of content views
193
194    @SuppressWarnings("unchecked")
195    protected List<DocumentModel> getCurrentPageDocuments(String contentViewName) {
196        List<DocumentModel> documents = null;
197        ContentView cView = contentViewActions.getContentView(contentViewName);
198        if (cView != null) {
199            PageProvider<?> cProvider = cView.getCurrentPageProvider();
200            if (cProvider != null) {
201                List<?> items = cProvider.getCurrentPage();
202                try {
203                    documents = (List<DocumentModel>) items;
204                } catch (ClassCastException e) {
205                    throw new NuxeoException(e);
206                }
207            }
208        }
209        return documents;
210    }
211
212    public void processSelectPage(String contentViewName, String listName, Boolean selection) {
213        List<DocumentModel> documents = getCurrentPageDocuments(contentViewName);
214        if (documents != null) {
215            String lName = (listName == null) ? DocumentsListsManager.CURRENT_DOCUMENT_SELECTION : listName;
216            if (Boolean.TRUE.equals(selection)) {
217                documentsListsManager.addToWorkingList(lName, documents);
218            } else {
219                documentsListsManager.removeFromWorkingList(lName, documents);
220            }
221        }
222    }
223
224    /**
225     * Handle complete table selection event after having ensured that the navigation context stills points to
226     * currentDocumentRef to protect against browsers' back button errors
227     */
228    public void checkCurrentDocAndProcessSelectPage(String contentViewName, String listName, Boolean selection,
229            String currentDocRef) {
230        DocumentRef currentDocumentRef = new IdRef(currentDocRef);
231        if (!currentDocumentRef.equals(navigationContext.getCurrentDocument().getRef())) {
232            navigationContext.navigateToRef(currentDocumentRef);
233        }
234        processSelectPage(contentViewName, listName, selection);
235    }
236
237    public void processSelectRow(String docRef, String contentViewName, String listName, Boolean selection) {
238        List<DocumentModel> documents = getCurrentPageDocuments(contentViewName);
239        DocumentModel doc = null;
240        if (documents != null) {
241            for (DocumentModel pagedDoc : documents) {
242                if (pagedDoc.getRef().toString().equals(docRef)) {
243                    doc = pagedDoc;
244                    break;
245                }
246            }
247        }
248        if (doc == null) {
249            log.error(
250                    String.format("could not find doc '%s' in the current page of content view page provider '%s'",
251                            docRef, contentViewName));
252            return;
253        }
254        String lName = (listName == null) ? DocumentsListsManager.CURRENT_DOCUMENT_SELECTION : listName;
255        if (Boolean.TRUE.equals(selection)) {
256            documentsListsManager.addToWorkingList(lName, doc);
257        } else {
258            documentsListsManager.removeFromWorkingList(lName, doc);
259        }
260    }
261
262    /**
263     * Handle row selection event after having ensured that the navigation context stills points to currentDocumentRef
264     * to protect against browsers' back button errors
265     */
266    public void checkCurrentDocAndProcessSelectRow(String docRef, String providerName, String listName,
267            Boolean selection, String requestedCurrentDocRef) {
268        DocumentRef requestedCurrentDocumentRef = new IdRef(requestedCurrentDocRef);
269        DocumentRef currentDocumentRef = null;
270        DocumentModel currentDocument = navigationContext.getCurrentDocument();
271        if (currentDocument != null) {
272            currentDocumentRef = currentDocument.getRef();
273        }
274        if (!requestedCurrentDocumentRef.equals(currentDocumentRef)) {
275            navigationContext.navigateToRef(requestedCurrentDocumentRef);
276        }
277        processSelectRow(docRef, providerName, listName, selection);
278    }
279
280    /**
281     * Handle version row selection event after having ensured that the navigation context stills points to
282     * currentDocumentRef to protect against browsers' back button errors.
283     *
284     * @param versionModelSelection the version model selection
285     * @param requestedCurrentDocRef the requested current doc ref
286     * @since 5.6
287     */
288    public void checkCurrentDocAndProcessVersionSelectRow(PageSelection<VersionModel> versionModelSelection,
289            String requestedCurrentDocRef) {
290
291        DocumentRef requestedCurrentDocumentRef = new IdRef(requestedCurrentDocRef);
292        DocumentRef currentDocumentRef = null;
293        DocumentModel currentDocument = navigationContext.getCurrentDocument();
294        if (currentDocument != null) {
295            currentDocumentRef = currentDocument.getRef();
296        }
297        if (!requestedCurrentDocumentRef.equals(currentDocumentRef)) {
298            navigationContext.navigateToRef(requestedCurrentDocumentRef);
299        }
300        processVersionSelectRow(versionModelSelection);
301    }
302
303    /**
304     * Processes the version selection row.
305     *
306     * @param versionModelSelection the version model selection
307     */
308    protected final void processVersionSelectRow(PageSelection<VersionModel> versionModelSelection) {
309
310        DocumentModel currentDocument = navigationContext.getCurrentDocument();
311        if (currentDocument == null) {
312            throw new NuxeoException("Cannot process version select row since current document is null.");
313        }
314
315        DocumentModel version = documentManager.getDocumentWithVersion(currentDocument.getRef(),
316                versionModelSelection.getData());
317        if (version == null) {
318            throw new NuxeoException("Cannot process version select row since selected version document is null.");
319        }
320
321        if (Boolean.TRUE.equals(versionModelSelection.isSelected())) {
322            documentsListsManager.addToWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION, version);
323        } else {
324            documentsListsManager.removeFromWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION, version);
325        }
326    }
327
328}