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