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