001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.picture.web;
023
024import static org.jboss.seam.ScopeType.CONVERSATION;
025
026import java.io.BufferedOutputStream;
027import java.io.IOException;
028import java.io.Serializable;
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033import java.util.zip.ZipException;
034import java.util.zip.ZipOutputStream;
035
036import javax.faces.context.FacesContext;
037import javax.faces.model.SelectItem;
038import javax.servlet.http.HttpServletResponse;
039
040import org.apache.commons.logging.Log;
041import org.apache.commons.logging.LogFactory;
042import org.jboss.seam.annotations.Create;
043import org.jboss.seam.annotations.Destroy;
044import org.jboss.seam.annotations.In;
045import org.jboss.seam.annotations.Name;
046import org.jboss.seam.annotations.Observer;
047import org.jboss.seam.annotations.Scope;
048import org.jboss.seam.annotations.intercept.BypassInterceptors;
049import org.jboss.seam.core.Events;
050import org.nuxeo.common.utils.ZipUtils;
051import org.nuxeo.ecm.core.api.Blob;
052import org.nuxeo.ecm.core.api.CoreSession;
053import org.nuxeo.ecm.core.api.DocumentModel;
054import org.nuxeo.ecm.core.api.PathRef;
055import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
056import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService;
057import org.nuxeo.ecm.platform.picture.api.adapters.AbstractPictureAdapter;
058import org.nuxeo.ecm.platform.picture.api.adapters.PictureBlobHolder;
059import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
060import org.nuxeo.ecm.platform.ui.web.api.UserAction;
061import org.nuxeo.ecm.webapp.base.InputController;
062import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
063import org.nuxeo.ecm.webapp.helpers.EventNames;
064import org.nuxeo.runtime.api.Framework;
065
066/**
067 * Provide Picture Book related Actions.
068 *
069 * @author <a href="mailto:ldoguin@nuxeo.com">Laurent Doguin</a>
070 * @deprecated since 6.0. See NXP-15370.
071 */
072@Name("pictureBookManager")
073@Scope(CONVERSATION)
074@Deprecated
075public class PictureBookManagerBean extends InputController implements PictureBookManager, Serializable {
076
077    private static final long serialVersionUID = 1L;
078
079    private static final Log log = LogFactory.getLog(PictureBookManagerBean.class);
080
081    @In(create = true)
082    protected CoreSession documentManager;
083
084    protected Integer maxsize;
085
086    protected ArrayList<Map<String, Object>> views;
087
088    protected String title;
089
090    protected String viewtitle;
091
092    protected String tag;
093
094    protected String description;
095
096    protected List<SelectItem> selectItems;
097
098    protected String[] selectedViews = { "OriginalJpeg" };
099
100    @In(create = true)
101    protected transient NavigationContext navigationContext;
102
103    @In(create = true)
104    protected transient DocumentsListsManager documentsListsManager;
105
106    protected static final int BUFFER = 2048;
107
108    protected DocumentModel getCurrentDocument() {
109        return navigationContext.getCurrentDocument();
110    }
111
112    @Override
113    @Create
114    public void initialize() {
115        log.debug("Initializing...");
116        initViews();
117    }
118
119    protected void initViews() {
120        // Sets the default views original, thumbnail and medium.
121        views = new ArrayList<Map<String, Object>>();
122        Map<String, Object> map = new HashMap<String, Object>();
123        map.put("title", "Medium");
124        map.put("maxsize", AbstractPictureAdapter.MEDIUM_SIZE);
125        map.put("tag", "medium");
126        map.put("description", "MediumSize Picture");
127        views.add(map);
128        map = new HashMap<String, Object>();
129        map.put("title", "Thumbnail");
130        map.put("maxsize", AbstractPictureAdapter.THUMB_SIZE);
131        map.put("tag", "thumbnail");
132        map.put("description", "ThumbnailSize Picture");
133        views.add(map);
134        map = new HashMap<String, Object>();
135        map.put("title", "OriginalJpeg");
136        map.put("maxsize", null);
137        map.put("tag", "originalJpeg");
138        map.put("description", "Original Picture in JPEG format");
139        views.add(map);
140    }
141
142    @Destroy
143    @BypassInterceptors
144    public void destroy() {
145        title = null;
146        viewtitle = null;
147        maxsize = null;
148        tag = null;
149        description = null;
150        views = null;
151        log.debug("Destroy");
152    }
153
154    @Override
155    public String createPictureBook() {
156        PathSegmentService pss = Framework.getService(PathSegmentService.class);
157        DocumentModel doc = navigationContext.getChangeableDocument();
158
159        String parentPath;
160        if (getCurrentDocument() == null) {
161            // creating item at the root
162            parentPath = documentManager.getRootDocument().getPathAsString();
163        } else {
164            parentPath = navigationContext.getCurrentDocument().getPathAsString();
165        }
166
167        doc.setProperty("picturebook", "picturetemplates", views);
168
169        Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED,
170                documentManager.getDocument(new PathRef(parentPath)));
171        doc.setPathInfo(parentPath, pss.generatePathSegment(doc));
172        doc = documentManager.createDocument(doc);
173        documentManager.saveDocument(doc);
174        documentManager.save();
175
176        return navigationContext.getActionResult(doc, UserAction.AFTER_CREATE);
177    }
178
179    @Override
180    public void addView() {
181        Map<String, Object> map = new HashMap<String, Object>();
182        map.put("title", viewtitle);
183        map.put("maxsize", maxsize);
184        map.put("tag", tag);
185        map.put("description", description);
186        views.add(map);
187    }
188
189    @Override
190    @Observer({ EventNames.DOCUMENT_SELECTION_CHANGED })
191    @BypassInterceptors
192    public void reset() {
193        title = null;
194        maxsize = null;
195        viewtitle = null;
196        tag = null;
197        description = null;
198        selectItems = null;
199        selectedViews = new String[] { "Original" };
200        initViews();
201    }
202
203    @Override
204    public String downloadSelectedBook() throws IOException {
205        List<DocumentModel> list = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
206        return createZip(list);
207    }
208
209    @Override
210    public String downloadAll() throws IOException {
211        DocumentModel currentDoc = navigationContext.getCurrentDocument();
212        if (currentDoc != null) {
213            List<DocumentModel> list = documentManager.getChildren(currentDoc.getRef());
214            return createZip(list);
215        }
216        return null;
217    }
218
219    protected boolean isEmptyFolder(DocumentModel doc) {
220        List<DocumentModel> docList = documentManager.getChildren(doc.getRef());
221        for (DocumentModel docChild : docList) {
222            BlobHolder bh = docChild.getAdapter(BlobHolder.class);
223            if (docChild.isFolder()) {
224                return isEmptyFolder(docChild);
225            } else if (bh != null) {
226                return false;
227            }
228        }
229        return true;
230    }
231
232    protected String formatFileName(String filename, String count) {
233        StringBuilder sb = new StringBuilder();
234        CharSequence name = filename.subSequence(0, filename.lastIndexOf(""));
235        CharSequence extension = filename.subSequence(filename.lastIndexOf(""), filename.length());
236        sb.append(name).append(count).append(extension);
237        return sb.toString();
238    }
239
240    protected void addBlobHolderToZip(String path, ZipOutputStream out, byte[] data, PictureBlobHolder bh)
241            throws IOException {
242        List<Blob> blobs;
243        if (selectedViews != null) {
244            blobs = bh.getBlobs(selectedViews);
245        } else {
246            blobs = bh.getBlobs();
247        }
248        for (Blob content : blobs) {
249            String fileName = content.getFilename();
250            if (content != null) {
251                // Workaround to deal with duplicate file names.
252                int tryCount = 0;
253                while (true) {
254                    try {
255                        if (tryCount == 0) {
256                            ZipUtils._zip(path + fileName, content.getStream(), out);
257                        } else {
258                            ZipUtils._zip(path + formatFileName(fileName, "(" + tryCount + ")"), content.getStream(),
259                                    out);
260                        }
261                        break;
262                    } catch (ZipException e) {
263                        tryCount++;
264                    }
265                }
266            }
267        }
268    }
269
270    protected void addFolderToZip(String path, ZipOutputStream out, DocumentModel doc, byte[] data)
271            throws IOException {
272
273        String title = (String) doc.getProperty("dublincore", "title");
274        List<DocumentModel> docList = documentManager.getChildren(doc.getRef());
275        for (DocumentModel docChild : docList) {
276
277            // NXP-2334 : skip deleted docs
278            if (docChild.getCurrentLifeCycleState().equals("delete")) {
279                continue;
280            }
281
282            BlobHolder bh = docChild.getAdapter(BlobHolder.class);
283            if (docChild.isFolder() && !isEmptyFolder(docChild)) {
284                addFolderToZip(path + title + "/", out, docChild, data);
285            } else if (bh != null) {
286                addBlobHolderToZip(path + title + "/", out, data, (PictureBlobHolder) bh);
287            }
288        }
289    }
290
291    protected String createZip(List<DocumentModel> documents) throws IOException {
292
293        FacesContext context = FacesContext.getCurrentInstance();
294        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
295
296        BufferedOutputStream buff = new BufferedOutputStream(response.getOutputStream());
297        ZipOutputStream out = new ZipOutputStream(buff);
298        out.setMethod(ZipOutputStream.DEFLATED);
299        out.setLevel(9);
300        byte[] data = new byte[BUFFER];
301        for (DocumentModel doc : documents) {
302
303            // first check if DM is attached to the core
304            if (doc.getSessionId() == null) {
305                // refetch the doc from the core
306                doc = documentManager.getDocument(doc.getRef());
307            }
308
309            // NXP-2334 : skip deleted docs
310            if (doc.getCurrentLifeCycleState().equals("delete")) {
311                continue;
312            }
313
314            BlobHolder bh = doc.getAdapter(BlobHolder.class);
315            if (doc.isFolder() && !isEmptyFolder(doc)) {
316                addFolderToZip("", out, doc, data);
317            } else if (bh != null) {
318                addBlobHolderToZip("", out, data, (PictureBlobHolder) bh);
319            }
320        }
321        try {
322            out.close();
323        } catch (ZipException e) {
324            // empty zip file, do nothing
325            setFacesMessage("label.clipboard.emptyDocuments");
326            return null;
327        }
328        response.setHeader("Content-Disposition", "attachment; filename=\"" + "clipboard.zip" + "\";");
329        response.setContentType("application/gzip");
330        response.flushBuffer();
331        context.responseComplete();
332        return null;
333    }
334
335    protected void initSelectItems() {
336        DocumentModel doc = getCurrentDocument();
337        List<Map<String, Object>> views = (List) doc.getProperty("picturebook", "picturetemplates");
338        selectItems = new ArrayList<SelectItem>(views.size());
339        String label;
340        SelectItem selectItem;
341        for (Map<String, Object> map : views) {
342            label = (String) map.get("title");
343            selectItem = new SelectItem(label, label);
344            selectItems.add(selectItem);
345        }
346    }
347
348    @Override
349    public List<SelectItem> getSelectItems() {
350        if (selectItems == null) {
351            initSelectItems();
352            return selectItems;
353        } else {
354            return selectItems;
355        }
356    }
357
358    @Override
359    public void setSelectItems(List<SelectItem> selectItems) {
360        this.selectItems = selectItems;
361    }
362
363    @Override
364    public String[] getSelectedViews() {
365        return selectedViews;
366    }
367
368    @Override
369    public void setSelectedViews(String[] selectedViews) {
370        this.selectedViews = selectedViews;
371    }
372
373    @Override
374    public Integer getMaxsize() {
375        return maxsize;
376    }
377
378    @Override
379    public void setMaxsize(Integer maxsize) {
380        this.maxsize = maxsize;
381    }
382
383    @Override
384    public String getTitle() {
385        return title;
386    }
387
388    @Override
389    public void setTitle(String title) {
390        this.title = title;
391    }
392
393    @Override
394    public String getTag() {
395        return tag;
396    }
397
398    @Override
399    public void setTag(String tag) {
400        this.tag = tag;
401    }
402
403    @Override
404    public String getDescription() {
405        return description;
406    }
407
408    @Override
409    public void setDescription(String description) {
410        this.description = description;
411    }
412
413    @Override
414    public String getViewtitle() {
415        return viewtitle;
416    }
417
418    @Override
419    public void setViewtitle(String viewtitle) {
420        this.viewtitle = viewtitle;
421    }
422
423    @Override
424    public ArrayList<Map<String, Object>> getViews() {
425        return views;
426    }
427
428    @Override
429    public void setViews(ArrayList<Map<String, Object>> views) {
430        this.views = views;
431    }
432
433}