001/*
002 *  Copyright 2018 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.web.frontoffice.search.metamodel.impl;
017
018import java.io.InputStream;
019import java.util.ArrayList;
020import java.util.Date;
021import java.util.List;
022
023import org.apache.cocoon.xml.AttributesImpl;
024import org.apache.cocoon.xml.XMLUtils;
025import org.apache.commons.lang.StringUtils;
026import org.apache.tika.Tika;
027import org.slf4j.Logger;
028import org.xml.sax.ContentHandler;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.transformation.xslt.ResolveURIComponent;
033import org.ametys.core.util.DateUtils;
034import org.ametys.plugins.explorer.resources.Resource;
035import org.ametys.plugins.repository.AmetysObject;
036import org.ametys.runtime.i18n.I18nizableText;
037import org.ametys.web.frontoffice.search.metamodel.ReturnableSaxer;
038import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments;
039import org.ametys.web.repository.page.Page;
040import org.ametys.web.repository.site.Site;
041
042/**
043 * {@link ReturnableSaxer} for {@link ResourceReturnable}
044 */
045public class ResourceSaxer implements ReturnableSaxer
046{
047    /** The associated returnable on resources */
048    protected ResourceReturnable _resourceReturnable;
049    
050    /**
051     * Constructor
052     * @param resourceReturnable The associated returnable on resources
053     */
054    public ResourceSaxer(ResourceReturnable resourceReturnable)
055    {
056        _resourceReturnable = resourceReturnable;
057    }
058    
059    @Override
060    public boolean canSax(AmetysObject hit, Logger logger, SearchComponentArguments args)
061    {
062        return hit instanceof Resource;
063    }
064
065    @Override
066    public void sax(ContentHandler contentHandler, AmetysObject hit, Logger logger, SearchComponentArguments args) throws SAXException
067    {
068        Resource resource = (Resource) hit;
069        String filename = resource.getName();
070        XMLUtils.createElement(contentHandler, "filename", filename);
071        XMLUtils.createElement(contentHandler, "title", StringUtils.substringBeforeLast(resource.getName(), "."));
072        
073        String dcDescription = resource.getDCDescription();
074        String excerpt = _getResourceExcerpt(resource, logger);
075        if (StringUtils.isNotBlank(dcDescription))
076        {
077            XMLUtils.createElement(contentHandler, "excerpt", dcDescription);
078        }
079        else if (StringUtils.isNotBlank(excerpt))
080        {
081            XMLUtils.createElement(contentHandler, "excerpt", excerpt + "...");
082        }
083        
084        XMLUtils.createElement(contentHandler, "type", "resource");
085        
086        Page page = _getResourcePage(resource);
087        Content content = _getResourceContent(resource);
088        _saxUri(contentHandler, resource, page, content);
089        XMLUtils.createElement(contentHandler, "mime-types", resource.getMimeType());
090        _saxSize(contentHandler, resource.getLength());
091        _saxIcon(contentHandler, filename);
092        
093        Date lastModified = resource.getLastModified();
094        if (lastModified != null)
095        {
096            XMLUtils.createElement(contentHandler, "lastModified", DateUtils.dateToString(lastModified));
097        }
098        if (page != null)
099        {
100            Site site = page.getSite();
101            XMLUtils.createElement(contentHandler, "siteName", site.getName());
102            XMLUtils.createElement(contentHandler, "siteTitle", site.getTitle());
103            XMLUtils.createElement(contentHandler, "siteUrl", site.getUrl());
104        }
105    }
106    
107    /**
108     * Get the excerpt of the resource
109     * @param resource the resource
110     * @param logger the logger to use in case of error
111     * @return the resource excerpt
112     */
113    protected String _getResourceExcerpt(Resource resource, Logger logger)
114    {
115        try (InputStream is = resource.getInputStream())
116        {
117            Tika tika = _resourceReturnable._tikaProvider.getTika();
118            String value = tika.parseToString(is);
119            if (StringUtils.isNotBlank(value))
120            {
121                int summaryEndIndex = value.lastIndexOf(' ', 200);
122                if (summaryEndIndex == -1)
123                {
124                    summaryEndIndex = value.length();
125                }
126                return value.substring(0, summaryEndIndex) + (summaryEndIndex != value.length() ? "…" : "");
127            }
128        }
129        catch (Exception e)
130        {
131            logger.error("Unable to index resource at " + resource.getPath(), e);
132        }
133        return null;
134    }
135    
136
137    /**
138     * Get the page containing the resource
139     * @param resource the resource
140     * @return the page containing the resource, null if the resource is not inside a page
141     */
142    protected Page _getResourcePage(Resource resource)
143    {
144        if (resource != null)
145        {
146            AmetysObject parent = resource.getParent();
147            while (parent != null)
148            {
149                if (parent instanceof Page)
150                {
151                    // We have gone up to the page
152                    return (Page) parent;
153                }
154                parent = parent.getParent();
155            }
156        }
157        
158        return null;
159    }
160    
161
162    /**
163     * Get the content containing the resource
164     * @param resource the resource
165     * @return the content containing the resource, null if the resource is not inside a content
166     */
167    protected Content _getResourceContent(Resource resource)
168    {
169        if (resource != null)
170        {
171            AmetysObject parent = resource.getParent();
172            while (parent != null)
173            {
174                if (parent instanceof Content)
175                {
176                    // We have gone up to the content
177                    return (Content) parent;
178                }
179                parent = parent.getParent();
180            }
181        }
182        
183        return null;
184    }
185    
186    /**
187     * Sax the resource URI
188     * @param contentHandler the content handler where to SAX into.
189     * @param resource the resource used to resolve the URI
190     * @param page the page used to resolve the URI
191     * @param content the content used to resolve the URI
192     * @throws SAXException if an errors occurs during the value writing
193     */
194    protected void _saxUri(ContentHandler contentHandler, Resource resource, Page page, Content content) throws SAXException
195    {
196        String type;
197        if (page != null)
198        {
199            type = "attachment-page";
200        }
201        else if (content != null)
202        {
203            type = "attachment-content";
204        }
205        else
206        {
207            type = "explorer";
208        }
209        AttributesImpl atts = new AttributesImpl();
210        atts.addCDATAAttribute("type", type);
211        atts.addCDATAAttribute("id", resource.getId());
212        // for legacy purpose, the uri is also resolved here, and thus ready to be used 
213        String uri = ResolveURIComponent.resolve(type, resource.getId(), true);
214        XMLUtils.createElement(contentHandler, "uri", atts, uri);
215    }
216    
217    /**
218     * Sax the resource size
219     * @param contentHandler the content handler where to SAX into.
220     * @param size the size to sax
221     * @throws SAXException if an errors occurs during the value writing
222     */
223    protected void _saxSize(ContentHandler contentHandler, long size) throws SAXException
224    {
225        XMLUtils.startElement(contentHandler, "size");
226        if (size < 1024)
227        {
228            // Bytes
229            List<String> params = new ArrayList<>();
230            params.add(String.valueOf(size));
231            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_BYTES", params);
232            i18nSize.toSAX(contentHandler);
233        }
234        else if (size < 1024 * 1024)
235        {
236            // Kb
237            int sizeInKb = Math.round(size * 10 / 1024 / 10);
238            List<String> params = new ArrayList<>();
239            params.add(String.valueOf(sizeInKb));
240            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_KB", params);
241            i18nSize.toSAX(contentHandler);
242
243        }
244        else
245        {
246            // Mb
247            int sizeInMb = Math.round(size * 10 / (1024 * 1024) / 10);
248            List<String> params = new ArrayList<>();
249            params.add(String.valueOf(sizeInMb));
250            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_MB", params);
251            i18nSize.toSAX(contentHandler);
252        }
253        XMLUtils.endElement(contentHandler, "size");
254    }
255    
256    /**
257     * Sax the resource icon
258     * @param contentHandler the content handler where to SAX into.
259     * @param filename the name of the resource
260     * @throws SAXException if an errors occurs during the value writing
261     */
262    protected void _saxIcon(ContentHandler contentHandler, String filename) throws SAXException
263    {
264        int index = filename.lastIndexOf('.');
265        String extension = filename.substring(index + 1);
266
267        XMLUtils.createElement(contentHandler, "icon", "plugins/explorer/icon-medium/" + extension + ".png");
268    }
269}