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