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.IOException;
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.Map;
029
030import javax.faces.application.FacesMessage;
031import javax.faces.context.FacesContext;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.jboss.seam.annotations.Create;
036import org.jboss.seam.annotations.Destroy;
037import org.jboss.seam.annotations.In;
038import org.jboss.seam.annotations.Name;
039import org.jboss.seam.annotations.Observer;
040import org.jboss.seam.annotations.Scope;
041import org.jboss.seam.annotations.intercept.BypassInterceptors;
042import org.jboss.seam.annotations.remoting.WebRemote;
043import org.jboss.seam.annotations.web.RequestParameter;
044import org.jboss.seam.core.Events;
045import org.jboss.seam.faces.FacesMessages;
046import org.jboss.seam.international.StatusMessage;
047import org.nuxeo.ecm.core.api.Blob;
048import org.nuxeo.ecm.core.api.CoreSession;
049import org.nuxeo.ecm.core.api.DocumentLocation;
050import org.nuxeo.ecm.core.api.DocumentModel;
051import org.nuxeo.ecm.core.api.IdRef;
052import org.nuxeo.ecm.core.api.model.Property;
053import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService;
054import org.nuxeo.ecm.platform.commandline.executor.api.CommandAvailability;
055import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService;
056import org.nuxeo.ecm.platform.picture.api.adapters.PictureResourceAdapter;
057import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
058import org.nuxeo.ecm.platform.ui.web.api.UserAction;
059import org.nuxeo.ecm.platform.ui.web.rest.RestHelper;
060import org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions;
061import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
062import org.nuxeo.ecm.platform.url.api.DocumentView;
063import org.nuxeo.ecm.platform.url.codec.DocumentFileCodec;
064import org.nuxeo.ecm.platform.util.RepositoryLocation;
065import org.nuxeo.ecm.webapp.helpers.EventNames;
066import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
067import org.nuxeo.runtime.api.Framework;
068
069/**
070 * @author <a href="mailto:ldoguin@nuxeo.com">Laurent Doguin</a>
071 */
072@Name("pictureManager")
073@Scope(CONVERSATION)
074public class PictureManagerBean implements PictureManager, Serializable {
075
076    private static final long serialVersionUID = 1L;
077
078    private static final Log log = LogFactory.getLog(PictureManagerBean.class);
079
080    protected static Boolean imageMagickAvailable;
081
082    @In(create = true, required = false)
083    protected transient CoreSession documentManager;
084
085    @RequestParameter
086    protected String fileFieldFullName;
087
088    @In(required = true, create = true)
089    protected transient NavigationContext navigationContext;
090
091    @In(create = true, required = false)
092    protected ResourcesAccessor resourcesAccessor;
093
094    protected String fileurlPicture;
095
096    protected String filename;
097
098    protected Blob fileContent;
099
100    protected Integer index;
101
102    protected String cropCoords;
103
104    protected ArrayList<Map<String, Object>> selectItems;
105
106    @Override
107    @Create
108    public void initialize() {
109        log.debug("Initializing...");
110        index = 0;
111    }
112
113    protected DocumentModel getCurrentDocument() {
114        return navigationContext.getCurrentDocument();
115    }
116
117    @Override
118    @SuppressWarnings("unchecked")
119    public String getFileurlPicture() {
120        ArrayList<Map<String, Object>> views = (ArrayList) getCurrentDocument().getProperty("picture", "views");
121        return views.get(index).get("title") + ":content";
122    }
123
124    @Override
125    public void setFileurlPicture(String fileurlPicture) {
126        this.fileurlPicture = fileurlPicture;
127    }
128
129    @SuppressWarnings({ "unchecked", "rawtypes" })
130    protected void initSelectItems() {
131        selectItems = new ArrayList<Map<String, Object>>();
132        DocumentModel doc = getCurrentDocument();
133        ArrayList<Map<String, Object>> views = (ArrayList) doc.getProperty("picture", "views");
134        for (int i = 0; i < views.size(); i++) {
135            Map<String, Object> map = new HashMap<String, Object>();
136            map.put("title", views.get(i).get("title"));
137            map.put("idx", i);
138            selectItems.add(map);
139        }
140    }
141
142    @Override
143    public ArrayList getSelectItems() {
144        if (selectItems == null) {
145            initSelectItems();
146            return selectItems;
147        } else {
148            return selectItems;
149        }
150    }
151
152    @Override
153    public void setSelectItems(ArrayList selectItems) {
154        this.selectItems = selectItems;
155    }
156
157    @Override
158    @Deprecated
159    @SuppressWarnings("unchecked")
160    public String addPicture() {
161        PathSegmentService pss = Framework.getService(PathSegmentService.class);
162        DocumentModel doc = navigationContext.getChangeableDocument();
163
164        String parentPath;
165        if (getCurrentDocument() == null) {
166            // creating item at the root
167            parentPath = documentManager.getRootDocument().getPathAsString();
168        } else {
169            parentPath = navigationContext.getCurrentDocument().getPathAsString();
170        }
171
172        String title = (String) doc.getProperty("dublincore", "title");
173        if (title == null) {
174            title = "";
175        }
176        // set parent path and name for document model
177        doc.setPathInfo(parentPath, pss.generatePathSegment(doc));
178        try {
179            DocumentModel parent = getCurrentDocument();
180            ArrayList<Map<String, Object>> pictureConversions = null;
181            if (parent.getType().equals("PictureBook")) {
182                // Use PictureBook Properties
183                pictureConversions = (ArrayList<Map<String, Object>>) parent.getProperty("picturebook",
184                        "picturetemplates");
185            }
186            PictureResourceAdapter picture = doc.getAdapter(PictureResourceAdapter.class);
187            boolean status = picture.fillPictureViews(fileContent, filename, title, pictureConversions);
188            if (!status) {
189                documentManager.cancel();
190                log.info("Picture type unsupported.");
191                FacesMessages.instance().add(StatusMessage.Severity.ERROR,
192                        resourcesAccessor.getMessages().get("label.picture.upload.error"));
193
194                return navigationContext.getActionResult(navigationContext.getCurrentDocument(), UserAction.VIEW);
195            } else {
196                doc = documentManager.createDocument(doc);
197                documentManager.saveDocument(doc);
198
199                Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, parent);
200
201                documentManager.save();
202            }
203        } catch (IOException e) {
204            log.error("Picture Creation failed", e);
205            documentManager.cancel();
206            FacesMessage message = FacesMessages.createFacesMessage(FacesMessage.SEVERITY_ERROR,
207                    resourcesAccessor.getMessages().get("label.picture.upload.error"));
208            FacesMessages.instance().add(message);
209            return navigationContext.getActionResult(navigationContext.getCurrentDocument(), UserAction.VIEW);
210        }
211        return navigationContext.getActionResult(doc, UserAction.AFTER_CREATE);
212    }
213
214    @Override
215    public String rotate90left() throws IOException {
216        DocumentModel doc = getCurrentDocument();
217        PictureResourceAdapter picture = doc.getAdapter(PictureResourceAdapter.class);
218        picture.doRotate(-90);
219        documentManager.saveDocument(doc);
220        documentManager.save();
221        navigationContext.setCurrentDocument(doc);
222        return null;
223    }
224
225    @Override
226    public String rotate90right() throws IOException {
227        DocumentModel doc = getCurrentDocument();
228        PictureResourceAdapter picture = doc.getAdapter(PictureResourceAdapter.class);
229        picture.doRotate(90);
230        documentManager.saveDocument(doc);
231        documentManager.save();
232        navigationContext.setCurrentDocument(doc);
233        return null;
234    }
235
236    @Override
237    public String crop() throws IOException {
238        if (cropCoords != null && !cropCoords.equals("")) {
239            DocumentModel doc = getCurrentDocument();
240            PictureResourceAdapter picture = doc.getAdapter(PictureResourceAdapter.class);
241            picture.doCrop(cropCoords);
242            documentManager.saveDocument(doc);
243            documentManager.save();
244            navigationContext.setCurrentDocument(doc);
245        }
246        return null;
247    }
248
249    @Override
250    @Observer(value = { EventNames.DOCUMENT_SELECTION_CHANGED, EventNames.DOCUMENT_CHANGED })
251    @BypassInterceptors
252    public void resetFields() {
253        filename = "";
254        fileContent = null;
255        selectItems = null;
256        index = 0;
257        cropCoords = null;
258    }
259
260    @WebRemote
261    public String remoteDownload(String patternName, String docID, String blobPropertyName, String filename)
262            {
263        IdRef docref = new IdRef(docID);
264        DocumentModel doc = documentManager.getDocument(docref);
265        return DocumentModelFunctions.fileUrl(patternName, doc, blobPropertyName, filename);
266    }
267
268    @WebRemote
269    public static String urlPopup(String url) {
270        return RestHelper.addCurrentConversationParameters(url);
271    }
272
273    @Override
274    public void download(DocumentView docView) {
275        if (docView != null) {
276            DocumentLocation docLoc = docView.getDocumentLocation();
277            // fix for NXP-1799
278            if (documentManager == null) {
279                RepositoryLocation loc = new RepositoryLocation(docLoc.getServerName());
280                navigationContext.setCurrentServerLocation(loc);
281                documentManager = navigationContext.getOrCreateDocumentManager();
282            }
283            DocumentModel doc = documentManager.getDocument(docLoc.getDocRef());
284            if (doc == null) {
285                return;
286            }
287            String path = docView.getParameter(DocumentFileCodec.FILE_PROPERTY_PATH_KEY);
288            String[] propertyPath = path.split(":");
289            String title = null;
290            String field = null;
291            Property datamodel = null;
292            if (propertyPath.length == 2) {
293                title = propertyPath[0];
294                field = propertyPath[1];
295                datamodel = doc.getProperty("picture:views");
296            } else if (propertyPath.length == 3) {
297                String schema = propertyPath[0];
298                title = propertyPath[1];
299                field = propertyPath[2];
300                datamodel = doc.getProperty(schema + ":" + "views");
301            }
302            Property view = null;
303            for (Property property : datamodel) {
304                if (property.get("title").getValue().equals(title)) {
305                    view = property;
306                }
307            }
308
309            if (view == null) {
310                for (Property property : datamodel) {
311                    if (property.get("title").getValue().equals("Thumbnail")) {
312                        view = property;
313                    }
314                }
315            }
316            if (view == null) {
317                return;
318            }
319            Blob blob = (Blob) view.getValue(field);
320            String filename = (String) view.getValue("filename");
321            // download
322            ComponentUtils.download(doc, path, blob, filename, "picture");
323        }
324    }
325
326    @Override
327    @Destroy
328    public void destroy() {
329        log.debug("Removing Seam action listener...");
330        fileurlPicture = null;
331        filename = null;
332        fileContent = null;
333        index = null;
334        selectItems = null;
335    }
336
337    @Override
338    public String getFilename() {
339        return filename;
340    }
341
342    @Override
343    public void setFilename(String filename) {
344        this.filename = filename;
345    }
346
347    @Override
348    public Blob getFileContent() {
349        return fileContent;
350    }
351
352    @Override
353    public void setFileContent(Blob fileContent) {
354        this.fileContent = fileContent;
355    }
356
357    @Override
358    public Integer getIndex() {
359        return index;
360    }
361
362    @Override
363    public void setIndex(Integer index) {
364        this.index = index;
365    }
366
367    @Override
368    public String getCropCoords() {
369        return cropCoords;
370    }
371
372    @Override
373    public void setCropCoords(String cropCoords) {
374        this.cropCoords = cropCoords;
375    }
376
377    public Boolean isImageMagickAvailable() {
378        if (imageMagickAvailable == null) {
379            CommandLineExecutorService cles = Framework.getService(CommandLineExecutorService.class);
380            CommandAvailability ca = cles.getCommandAvailability("cropAndResize");
381            imageMagickAvailable = ca.isAvailable();
382        }
383        return imageMagickAvailable;
384    }
385}