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