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    /** @since 5.6 */
075    @In(create = true, required = false)
076    protected transient CoreSession documentManager;
077
078    @In(create = true)
079    protected transient ContentViewActions contentViewActions;
080
081    @In(required = false, create = true)
082    protected transient DocumentsListsManager documentsListsManager;
083
084    // only store 20 entries that cache chosen layout for a given document
085    protected LRUCachingMap<String, String> docTolistings = new LRUCachingMap<String, String>(20);
086
087    protected String currentListingLayoutName = null;
088
089    protected List<String> currentAvailableListingLayoutNames = null;
090
091    // API for current layout in listing mode
092
093    /**
094     * @deprecated this information is now held by content views
095     */
096    @Deprecated
097    public String getLayoutForDocument(DocumentModel doc) {
098        if (doc != null) {
099            String id = doc.getId();
100            if (docTolistings.containsKey(id)) {
101                return docTolistings.get(id);
102            }
103            List<String> availableLayouts = getAvailableLayoutsForDocument(doc);
104            if (availableLayouts != null && !availableLayouts.isEmpty()) {
105                return availableLayouts.get(0);
106            }
107        }
108        return DEFAULT_LISTING_LAYOUT;
109    }
110
111    /**
112     * @deprecated this information is now held by content views
113     */
114    @Deprecated
115    public void setLayoutForDocument(DocumentModel doc, String layoutName) {
116        if (doc == null) {
117            log.error("Cannot set listing layout for null document");
118            return;
119        }
120        String id = doc.getId();
121        docTolistings.put(id, layoutName);
122    }
123
124    /**
125     * @deprecated this information is now held by content views
126     */
127    @Deprecated
128    @Factory(value = "currentListingLayoutName", scope = EVENT)
129    public String getLayoutForCurrentDocument() {
130        if (currentListingLayoutName == null) {
131            DocumentModel currentDocument = navigationContext.getCurrentDocument();
132            currentListingLayoutName = getLayoutForDocument(currentDocument);
133        }
134        return currentListingLayoutName;
135    }
136
137    /**
138     * @deprecated this information is now held by content views
139     */
140    @Deprecated
141    public void setLayoutForCurrentDocument(String layoutName) {
142        DocumentModel currentDocument = navigationContext.getCurrentDocument();
143        setLayoutForDocument(currentDocument, layoutName);
144        currentListingLayoutName = layoutName;
145    }
146
147    /**
148     * @deprecated this information is now held by content views
149     */
150    @Deprecated
151    public List<String> getAvailableLayoutsForDocument(DocumentModel doc) {
152        if (doc == null) {
153            return Collections.emptyList();
154        }
155        TypeInfo typeInfo = doc.getAdapter(TypeInfo.class);
156        String[] layoutNames = typeInfo.getLayouts(BuiltinModes.LISTING, null);
157        List<String> res = new ArrayList<String>();
158        if (layoutNames != null && layoutNames.length > 0) {
159            res.addAll(Arrays.asList(layoutNames));
160        } else {
161            res.add(DEFAULT_LISTING_LAYOUT);
162        }
163        return res;
164    }
165
166    /**
167     * @deprecated this information is now held by content views
168     */
169    @Deprecated
170    @Factory(value = "currentAvailableListingLayoutNames", scope = EVENT)
171    public List<String> getAvailableLayoutsForCurrentDocument() {
172        if (currentAvailableListingLayoutNames == null) {
173            DocumentModel currentDocument = navigationContext.getCurrentDocument();
174            currentAvailableListingLayoutNames = getAvailableLayoutsForDocument(currentDocument);
175        }
176        return currentAvailableListingLayoutNames;
177    }
178
179    /**
180     * @deprecated this information is now held by content views
181     */
182    @Observer(value = { EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED }, create = false)
183    @BypassInterceptors
184    @Deprecated
185    public void documentChanged() {
186        currentListingLayoutName = null;
187        currentAvailableListingLayoutNames = null;
188    }
189
190    // API for AJAX selection in listings of content views
191
192    @SuppressWarnings("unchecked")
193    protected List<DocumentModel> getCurrentPageDocuments(String contentViewName) {
194        List<DocumentModel> documents = null;
195        ContentView cView = contentViewActions.getContentView(contentViewName);
196        if (cView != null) {
197            PageProvider<?> cProvider = cView.getCurrentPageProvider();
198            if (cProvider != null) {
199                List<?> items = cProvider.getCurrentPage();
200                try {
201                    documents = (List<DocumentModel>) items;
202                } catch (ClassCastException e) {
203                    throw new NuxeoException(e);
204                }
205            }
206        }
207        return documents;
208    }
209
210    public void processSelectPage(String contentViewName, String listName, Boolean selection) {
211        List<DocumentModel> documents = getCurrentPageDocuments(contentViewName);
212        if (documents != null) {
213            String lName = (listName == null) ? DocumentsListsManager.CURRENT_DOCUMENT_SELECTION : listName;
214            if (Boolean.TRUE.equals(selection)) {
215                documentsListsManager.addToWorkingList(lName, documents);
216            } else {
217                documentsListsManager.removeFromWorkingList(lName, documents);
218            }
219        }
220    }
221
222    /**
223     * Handle complete table selection event after having ensured that the navigation context stills points to
224     * currentDocumentRef to protect against browsers' back button errors
225     */
226    public void checkCurrentDocAndProcessSelectPage(String contentViewName, String listName, Boolean selection,
227            String currentDocRef) {
228        DocumentRef currentDocumentRef = new IdRef(currentDocRef);
229        if (!currentDocumentRef.equals(navigationContext.getCurrentDocument().getRef())) {
230            navigationContext.navigateToRef(currentDocumentRef);
231        }
232        processSelectPage(contentViewName, listName, selection);
233    }
234
235    public void processSelectRow(String docRef, String contentViewName, String listName, Boolean selection)
236            {
237        List<DocumentModel> documents = getCurrentPageDocuments(contentViewName);
238        DocumentModel doc = null;
239        if (documents != null) {
240            for (DocumentModel pagedDoc : documents) {
241                if (pagedDoc.getRef().toString().equals(docRef)) {
242                    doc = pagedDoc;
243                    break;
244                }
245            }
246        }
247        if (doc == null) {
248            log.error(String.format("could not find doc '%s' in the current page of "
249                    + "content view page provider '%s'", docRef, contentViewName));
250            return;
251        }
252        String lName = (listName == null) ? DocumentsListsManager.CURRENT_DOCUMENT_SELECTION : listName;
253        if (Boolean.TRUE.equals(selection)) {
254            documentsListsManager.addToWorkingList(lName, doc);
255        } else {
256            documentsListsManager.removeFromWorkingList(lName, doc);
257        }
258    }
259
260    /**
261     * Handle row selection event after having ensured that the navigation context stills points to currentDocumentRef
262     * to protect against browsers' back button errors
263     */
264    public void checkCurrentDocAndProcessSelectRow(String docRef, String providerName, String listName,
265            Boolean selection, String requestedCurrentDocRef) {
266        DocumentRef requestedCurrentDocumentRef = new IdRef(requestedCurrentDocRef);
267        DocumentRef currentDocumentRef = null;
268        DocumentModel currentDocument = navigationContext.getCurrentDocument();
269        if (currentDocument != null) {
270            currentDocumentRef = currentDocument.getRef();
271        }
272        if (!requestedCurrentDocumentRef.equals(currentDocumentRef)) {
273            navigationContext.navigateToRef(requestedCurrentDocumentRef);
274        }
275        processSelectRow(docRef, providerName, listName, selection);
276    }
277
278    /**
279     * Handle version row selection event after having ensured that the navigation context stills points to
280     * currentDocumentRef to protect against browsers' back button errors.
281     *
282     * @param versionModelSelection the version model selection
283     * @param requestedCurrentDocRef the requested current doc ref
284     * @since 5.6
285     */
286    public void checkCurrentDocAndProcessVersionSelectRow(PageSelection<VersionModel> versionModelSelection,
287            String requestedCurrentDocRef) {
288
289        DocumentRef requestedCurrentDocumentRef = new IdRef(requestedCurrentDocRef);
290        DocumentRef currentDocumentRef = null;
291        DocumentModel currentDocument = navigationContext.getCurrentDocument();
292        if (currentDocument != null) {
293            currentDocumentRef = currentDocument.getRef();
294        }
295        if (!requestedCurrentDocumentRef.equals(currentDocumentRef)) {
296            navigationContext.navigateToRef(requestedCurrentDocumentRef);
297        }
298        processVersionSelectRow(versionModelSelection);
299    }
300
301    /**
302     * Processes the version selection row.
303     *
304     * @param versionModelSelection the version model selection
305     */
306    protected final void processVersionSelectRow(PageSelection<VersionModel> versionModelSelection)
307            {
308
309        DocumentModel currentDocument = navigationContext.getCurrentDocument();
310        if (currentDocument == null) {
311            throw new NuxeoException("Cannot process version select row since current document is null.");
312        }
313
314        DocumentModel version = documentManager.getDocumentWithVersion(currentDocument.getRef(),
315                versionModelSelection.getData());
316        if (version == null) {
317            throw new NuxeoException("Cannot process version select row since selected version document is null.");
318        }
319
320        if (Boolean.TRUE.equals(versionModelSelection.isSelected())) {
321            documentsListsManager.addToWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION, version);
322        } else {
323            documentsListsManager.removeFromWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION, version);
324        }
325    }
326
327}