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    private String _getResourceExcerpt(Resource resource, Logger logger)
108    {
109        try (InputStream is = resource.getInputStream())
110        {
111            Tika tika = _resourceReturnable._tikaProvider.getTika();
112            String value = tika.parseToString(is);
113            if (StringUtils.isNotBlank(value))
114            {
115                int summaryEndIndex = value.lastIndexOf(' ', 200);
116                if (summaryEndIndex == -1)
117                {
118                    summaryEndIndex = value.length();
119                }
120                return value.substring(0, summaryEndIndex) + (summaryEndIndex != value.length() ? "…" : "");
121            }
122        }
123        catch (Exception e)
124        {
125            logger.error("Unable to index resource at " + resource.getPath(), e);
126        }
127        return null;
128    }
129    
130    private Page _getResourcePage(Resource resource)
131    {
132        if (resource != null)
133        {
134            AmetysObject parent = resource.getParent();
135            while (parent != null)
136            {
137                if (parent instanceof Page)
138                {
139                    // We have gone up to the page
140                    return (Page) parent;
141                }
142                parent = parent.getParent();
143            }
144        }
145        
146        return null;
147    }
148    
149    private Content _getResourceContent(Resource resource)
150    {
151        if (resource != null)
152        {
153            AmetysObject parent = resource.getParent();
154            while (parent != null)
155            {
156                if (parent instanceof Content)
157                {
158                    // We have gone up to the content
159                    return (Content) parent;
160                }
161                parent = parent.getParent();
162            }
163        }
164        
165        return null;
166    }
167    
168    private void _saxUri(ContentHandler contentHandler, Resource resource, Page page, Content content) throws SAXException
169    {
170        String type;
171        if (page != null)
172        {
173            type = "attachment-page";
174        }
175        else if (content != null)
176        {
177            type = "attachment-content";
178        }
179        else
180        {
181            type = "explorer";
182        }
183        AttributesImpl atts = new AttributesImpl();
184        atts.addCDATAAttribute("type", type);
185        atts.addCDATAAttribute("id", resource.getId());
186        // for legacy purpose, the uri is also resolved here, and thus ready to be used 
187        String uri = ResolveURIComponent.resolve(type, resource.getId(), true);
188        XMLUtils.createElement(contentHandler, "uri", atts, uri);
189    }
190    
191    private void _saxSize(ContentHandler contentHandler, long size) throws SAXException
192    {
193        XMLUtils.startElement(contentHandler, "size");
194        if (size < 1024)
195        {
196            // Bytes
197            List<String> params = new ArrayList<>();
198            params.add(String.valueOf(size));
199            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_BYTES", params);
200            i18nSize.toSAX(contentHandler);
201        }
202        else if (size < 1024 * 1024)
203        {
204            // Kb
205            int sizeInKb = Math.round(size * 10 / 1024 / 10);
206            List<String> params = new ArrayList<>();
207            params.add(String.valueOf(sizeInKb));
208            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_KB", params);
209            i18nSize.toSAX(contentHandler);
210
211        }
212        else
213        {
214            // Mb
215            int sizeInMb = Math.round(size * 10 / (1024 * 1024) / 10);
216            List<String> params = new ArrayList<>();
217            params.add(String.valueOf(sizeInMb));
218            I18nizableText i18nSize = new I18nizableText("plugin.web", "ATTACHMENTS_FILE_SIZE_MB", params);
219            i18nSize.toSAX(contentHandler);
220        }
221        XMLUtils.endElement(contentHandler, "size");
222    }
223    
224    private void _saxIcon(ContentHandler contentHandler, String filename) throws SAXException
225    {
226        int index = filename.lastIndexOf('.');
227        String extension = filename.substring(index + 1);
228
229        XMLUtils.createElement(contentHandler, "icon", "plugins/explorer/icon-medium/" + extension + ".png");
230    }
231}