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}