001/*
002 *  Copyright 2014 Anyware Services
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 */
016package org.ametys.plugins.flipbook;
017
018import java.util.Arrays;
019import java.util.Iterator;
020import java.util.List;
021
022import org.apache.avalon.framework.logger.LogEnabled;
023import org.apache.avalon.framework.logger.Logger;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.components.ContextHelper;
027import org.apache.cocoon.environment.Request;
028
029import org.ametys.cms.data.Binary;
030import org.ametys.cms.repository.Content;
031import org.ametys.cms.transformation.URIResolver;
032import org.ametys.core.util.FilenameUtils;
033import org.ametys.core.util.URIUtils;
034import org.ametys.plugins.repository.AmetysObjectResolver;
035import org.ametys.plugins.repository.UnknownAmetysObjectException;
036import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
037import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater;
038import org.ametys.plugins.repository.data.type.ModelItemTypeConstants;
039import org.ametys.plugins.repository.metadata.MetadataAwareAmetysObject;
040import org.ametys.runtime.model.ModelItem;
041import org.ametys.runtime.plugin.component.PluginAware;
042import org.ametys.web.editor.MetadataURIResolver;
043
044/**
045 * {@link URIResolver} for type "metadata-flipbook".<br>
046 * These links point to a document file from the resource explorer converted to flash. 
047 */
048public class Metadata2FlipbookUriResolver extends MetadataURIResolver implements PluginAware, LogEnabled
049{
050    /** The plugin name. */
051    protected String _pluginName;
052    
053    /** The logger . */
054    protected Logger _logger;
055    
056    /** Metadata to images convertor. */
057    protected ConvertMetadata2ImagesComponent _metadataComponent;
058    
059    @Override
060    public void service(ServiceManager serviceManager) throws ServiceException
061    {
062        super.service(serviceManager);
063        _metadataComponent = (ConvertMetadata2ImagesComponent) serviceManager.lookup(ConvertMetadata2ImagesComponent.ROLE);
064    }
065    
066    @Override
067    public void setPluginInfo(String pluginName, String featureName, String id)
068    {
069        _pluginName = pluginName;
070    }
071    
072    @Override
073    public void enableLogging(Logger logger)
074    {
075        _logger = logger;
076    }
077    
078    @Override
079    public String getType()
080    {
081        return "metadata-flipbook";
082    }
083    
084    @Override
085    public String resolve(String uri, boolean download, boolean absolute, boolean internal)
086    {
087        Request request = ContextHelper.getRequest(_context);
088        
089        String metadataPath;
090        String fileName;
091        String siteName = (String) request.getAttribute("site");
092        MetadataAwareAmetysObject object;
093        try
094        {
095            MetaInfo metaInfo = _getMetaInfo(uri, request);
096            
097            metadataPath = metaInfo.getMetadataPath();
098            object = metaInfo.getAmetysObject();
099
100            fileName = object.getName();
101        }
102        catch (UnknownAmetysObjectException e)
103        {
104            return "";
105        }
106
107        Content content = (Content) object;
108        String resultPath = (absolute ? _prefixHandler.getAbsoluteUriPrefix(siteName) : _prefixHandler.getUriPrefix(siteName))
109                + "/_plugins/" + _pluginName + "/" + siteName
110                + "/_metadata-flipbook/"
111                + fileName
112                + "/" 
113                + metadataPath
114                + "/_contents"
115                + FilenameUtils.encodePath(content.getPath())
116                + "/book.html";
117        
118        Binary binary = _getBinary(metadataPath, content);
119        
120        if (!_metadataComponent.isMimeTypeSupported(binary.getMimeType()))
121        {
122            return super.resolve(uri, download, absolute, internal);
123        }
124       
125        new CacheThread(content.getId(), metadataPath, siteName, _resolver, _logger).start();
126        
127        // Encode twice
128        return URIUtils.encodePath(resultPath.toString());
129    }
130    
131    @Override
132    public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal)
133    {
134        Request request = ContextHelper.getRequest(_context);
135        
136        String metadataPath;
137        String siteName = (String) request.getAttribute("site");
138        MetadataAwareAmetysObject object;
139        try
140        {
141            MetaInfo metaInfo = _getMetaInfo(uri, request);
142            
143            metadataPath = metaInfo.getMetadataPath();
144            object = metaInfo.getAmetysObject();
145        }
146        catch (UnknownAmetysObjectException e)
147        {
148            return "";
149        }
150
151        Content content = (Content) object;
152        Binary binary = _getBinary(metadataPath, content);
153        
154        if (!_metadataComponent.isMimeTypeSupported(binary.getMimeType()))
155        { 
156            String defaultPath = (absolute ? _prefixHandler.getAbsoluteUriPrefix(siteName) : _prefixHandler.getUriPrefix(siteName))
157                    + "/_plugins/" + _pluginName + "/" + siteName
158                    + "/pages" 
159                    + "/error"
160                    + "/thumbnail_"
161                    + height + "x" + width
162                    + ".png";
163            
164            return URIUtils.encodePath(defaultPath);
165        }
166
167        String result = (absolute ? _prefixHandler.getAbsoluteUriPrefix(siteName) : _prefixHandler.getUriPrefix(siteName))
168                + "/_plugins/" + _pluginName + "/" + siteName
169                + "/contents/"
170                + content.getName()
171                + "/metadatas/"
172                + metadataPath
173                + "/"
174                + FilenameUtils.encodeName(binary.getFilename())
175                + "/pages" 
176                + "/thumbnail_"
177                + height + "x" + width
178                + ".png";
179        
180        // Encode twice
181        return URIUtils.encodePath(result);
182    }
183    
184    @Override
185    public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal)
186    {
187        if (maxHeight == 0 && maxWidth == 0)
188        {
189            return resolve(uri, download, absolute, internal);
190        }
191        
192        Request request = ContextHelper.getRequest(_context);
193        
194        String metadataPath;
195        String siteName = (String) request.getAttribute("site");
196        MetadataAwareAmetysObject object;
197        try
198        {
199            MetaInfo metaInfo = _getMetaInfo(uri, request);
200            
201            metadataPath = metaInfo.getMetadataPath();
202            object = metaInfo.getAmetysObject();
203        }
204        catch (UnknownAmetysObjectException e)
205        {
206            return "";
207        }
208
209        Content content = (Content) object;
210        Binary binary = _getBinary(metadataPath, content);
211        
212        if (!_metadataComponent.isMimeTypeSupported(binary.getMimeType()))
213        { 
214            String defaultPath = (absolute ? _prefixHandler.getAbsoluteUriPrefix(siteName) : _prefixHandler.getUriPrefix(siteName))
215                    + "/_plugins/" + _pluginName + "/" + siteName
216                    + "/pages" 
217                    + "/error"
218                    + "/thumbnail_max"
219                    + maxHeight + "x" + maxWidth
220                    + ".png";
221            
222            return URIUtils.encodePath(defaultPath);
223        }
224
225        String result = (absolute ? _prefixHandler.getAbsoluteUriPrefix(siteName) : _prefixHandler.getUriPrefix(siteName))
226                + "/_plugins/" + _pluginName + "/" + siteName
227                + "/contents/"
228                + content.getName()
229                + "/metadatas/"
230                + metadataPath
231                + "/"
232                + FilenameUtils.encodeName(binary.getFilename())
233                + "/pages" 
234                + "/thumbnail_max"
235                + maxHeight + "x" + maxWidth
236                + ".png";
237        
238        // Encode twice
239        return URIUtils.encodePath(result);
240    }
241    
242    private Binary _getBinary(String dataPath, Content content)
243    {
244        if (dataPath.contains("["))
245        {
246            // The path contains a repeater entry with the new syntax ("repeater[1]/myBinaryData"), the getValue method can be used
247            return content.getValue(dataPath);
248        }
249        else
250        {
251            // FILPBOOK-63: The path could contain a repeater with the old syntax ("repeater/1/myBinaryData"), look over each path segment to get the binary data
252            // TODO NEWATTRIBUTEAPI: When all generators have been migrated to the new API, this code is unnecessary
253            List<String> pathSegments = Arrays.asList(dataPath.split(ModelItem.ITEM_PATH_SEPARATOR));
254            Iterator<String> pathSegmentsIt = pathSegments.iterator();
255            
256            ModelAwareDataHolder dataHolder = content;
257            Binary binary = null;
258            while (pathSegmentsIt.hasNext())
259            {
260                String pathSegment = pathSegmentsIt.next();
261                
262                if (pathSegmentsIt.hasNext())
263                {
264                    // Not the last segment: it is a repeater or a composite
265                    if (dataHolder.getType(pathSegment).getId().equals(ModelItemTypeConstants.REPEATER_TYPE_ID))
266                    {
267                        ModelAwareRepeater repeater = dataHolder.getRepeater(pathSegment);
268                        int repeaterPosition = Integer.valueOf(pathSegmentsIt.next());
269                        dataHolder = repeater.getEntry(repeaterPosition);
270                    }
271                    else
272                    {
273                        dataHolder = dataHolder.getComposite(pathSegment);
274                    }
275                }
276                else
277                {
278                    binary = dataHolder.getValue(pathSegment);
279                }
280            }
281            
282            return binary;
283        }
284    }
285    
286    private class CacheThread extends Thread 
287    {
288        private String _contentId;
289        private String _metadataPath;
290        private String _siteName;
291        private Logger _cacheLogger;
292        private AmetysObjectResolver _cacheResolver;
293        
294        public CacheThread(String contentId, String metadataPath, String siteName, AmetysObjectResolver cacheResolver, Logger cacheLogger) 
295        {
296            _contentId = contentId;
297            _metadataPath = metadataPath;
298            _siteName = siteName;
299            _cacheLogger = cacheLogger;
300            _cacheResolver = cacheResolver;
301            setDaemon(true);
302            setName("FlipbookCacheMetadataCreator");
303        }
304        
305        @Override
306        public void run() 
307        {
308            try
309            {
310                Content content = _cacheResolver.resolveById(_contentId);
311                _metadataComponent.doCache(content, _metadataPath, _siteName);
312            }
313            catch (Exception e)
314            {
315                _cacheLogger.error("An error occurred during creating cache for content : " + _contentId);
316            }
317        }
318    }
319
320}