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