001/* 002 * Copyright 2016 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.externaldata.data; 017 018import java.io.IOException; 019import java.util.Collection; 020import java.util.Enumeration; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Map.Entry; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.ProcessingException; 028import org.apache.cocoon.environment.ObjectModelHelper; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.generation.ServiceableGenerator; 031import org.apache.cocoon.xml.AttributesImpl; 032import org.apache.cocoon.xml.XMLUtils; 033import org.apache.commons.lang.StringUtils; 034import org.xml.sax.SAXException; 035 036import org.ametys.web.repository.page.Page; 037import org.ametys.web.repository.page.ZoneItem; 038 039/** 040 * Generator for external data search service 041 * 042 */ 043public class ExternalSearchGenerator extends ServiceableGenerator 044{ 045 private static final String __SEARCH_CRITERIA_PREFIX = "external-data-search-"; 046 047 /** The Query DAO. */ 048 protected QueryDao _queryDao; 049 /** Datasource factory handler */ 050 protected DataSourceFactoryExtensionPoint _dataSourceFactoryEP; 051 052 @Override 053 public void service(ServiceManager smanager) throws ServiceException 054 { 055 super.service(smanager); 056 _queryDao = (QueryDao) smanager.lookup(QueryDao.ROLE); 057 _dataSourceFactoryEP = (DataSourceFactoryExtensionPoint) smanager.lookup(DataSourceFactoryExtensionPoint.ROLE); 058 } 059 060 @Override 061 public void generate() throws IOException, SAXException, ProcessingException 062 { 063 Request request = ObjectModelHelper.getRequest(objectModel); 064 065 String currentSiteName = null; 066 String lang = null; 067 Page page = (Page) request.getAttribute(Page.class.getName()); 068 if (page != null) 069 { 070 currentSiteName = page.getSiteName(); 071 lang = page.getSitemapName(); 072 } 073 074 String siteName = (String) request.getAttribute("site"); 075 ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName()); 076 077 String idQuery = zoneItem.getServiceParameters().getValue("datasource-query"); 078 int limit = Math.toIntExact(zoneItem.getServiceParameters().getValue("limit")); 079 int pageNum = getPageIndex(request); 080 081 contentHandler.startDocument(); 082 XMLUtils.startElement(contentHandler, "search"); 083 084 // The search url 085 XMLUtils.createElement(contentHandler, "url", page != null ? lang + "/" + page.getPathInSitemap() + ".html" : lang + "/_plugins/" + currentSiteName + "/" + lang + "/service/search.html"); 086 087 try 088 { 089 Query query = _queryDao.getQuery(siteName, idQuery); 090 Map<String, String> parameterValues = getParameterValues(request, query); 091 092 saxFormParameters (request, query, parameterValues); 093 094 boolean submit = request.getParameter("external-data-search-submit") != null; 095 if (submit) 096 { 097 098 XMLUtils.startElement(contentHandler, "search-result"); 099 executeQuery(query, parameterValues, limit, pageNum); 100 XMLUtils.endElement(contentHandler, "search-result"); 101 } 102 103 } 104 catch (DataInclusionException e) 105 { 106 getLogger().error("An error occurred : impossible to get query with id : " + idQuery, e); 107 } 108 XMLUtils.endElement(contentHandler, "search"); 109 contentHandler.endDocument(); 110 } 111 112 /** 113 * Get the search criteria values from request 114 * @param request The request 115 * @param query The LDAP query 116 * @return the values 117 */ 118 protected Map<String, String> getParameterValues (Request request, Query query) 119 { 120 Map<String, String> paramValues = new HashMap<>(); 121 122 Map<String, String> parametersName = query.getParameters(); 123 for (String paramName : parametersName.keySet()) 124 { 125 String value = request.getParameter(__SEARCH_CRITERIA_PREFIX + paramName); 126 if (StringUtils.isNotBlank(value)) 127 { 128 paramValues.put(paramName, value); 129 } 130 } 131 return paramValues; 132 } 133 134 /** 135 * SAX the search parameters from the request parameters and query 136 * @param request The request 137 * @param query The LDAP query 138 * @param values The extracted form values 139 * @throws SAXException If an error occurs while SAXing 140 */ 141 protected void saxFormParameters (Request request, Query query, Map<String, String> values) throws SAXException 142 { 143 XMLUtils.startElement(contentHandler, "form"); 144 145 XMLUtils.startElement(contentHandler, "fields"); 146 saxFormFields (query); 147 XMLUtils.endElement(contentHandler, "fields"); 148 149 boolean submit = request.getParameter("external-data-search-submit") != null; 150 if (submit) 151 { 152 XMLUtils.startElement(contentHandler, "values"); 153 saxFormValues(request, values); 154 XMLUtils.endElement(contentHandler, "values"); 155 } 156 157 XMLUtils.endElement(contentHandler, "form"); 158 } 159 160 /** 161 * SAX the form search criteria 162 * @param query The LDAP query 163 * @throws SAXException if an error occurs while SAXing 164 */ 165 protected void saxFormFields (Query query) throws SAXException 166 { 167 Map<String, String> parametersName = query.getParameters(); 168 for (Entry<String, String> entry : parametersName.entrySet()) 169 { 170 String parameterName = entry.getKey(); 171 String paramaterLabel = entry.getValue(); 172 AttributesImpl att = new AttributesImpl(); 173 att.addCDATAAttribute("id", parameterName); 174 175 XMLUtils.createElement(contentHandler, "field", att, paramaterLabel); 176 } 177 } 178 179 /** 180 * SAX the form search criteria values 181 * @param request The request 182 * @param values The extracted form values 183 * @throws SAXException if an error occurs while SAXing 184 */ 185 protected void saxFormValues (Request request, Map<String, String> values) throws SAXException 186 { 187 for (Entry<String, String> entry : values.entrySet()) 188 { 189 String paramName = entry.getKey(); 190 String paramValue = entry.getValue(); 191 192 XMLUtils.createElement(contentHandler, paramName, paramValue); 193 } 194 } 195 196 /** 197 * Execute ldap query 198 * @param query the ldap query 199 * @param parameterValues the search criteria 200 * @param nbResultsPerPage the number of result per page 201 * @param page the page number 202 * @throws DataInclusionException if an error occurred 203 * @throws SAXException If an error occurred 204 */ 205 protected void executeQuery(Query query, Map<String, String> parameterValues, int nbResultsPerPage, int page) throws DataInclusionException, SAXException 206 { 207 String factoryId = query.getFactory(); 208 DataSourceFactory<Query, QueryResult> dsFactory = _dataSourceFactoryEP.getExtension(factoryId); 209 210 int offset = (page - 1) * nbResultsPerPage; 211 212 QueryResult result = dsFactory.execute(query, parameterValues, offset, nbResultsPerPage); 213 214 saxResult(query, result); 215 216 boolean hasMoreElmts = false; 217 int nbResults = result.getSize(); 218 if (nbResults >= nbResultsPerPage) 219 { 220 // Test if there has more elements 221 hasMoreElmts = dsFactory.execute(query, parameterValues, page * nbResultsPerPage, 1).getSize() > 0; 222 } 223 224 saxPagination(page, offset, nbResultsPerPage, result.getSize(), hasMoreElmts); 225 } 226 227 /** 228 * SAX the pagination 229 * @param page the current page 230 * @param offset the offset 231 * @param limit the number of results per page 232 * @param nbResult the number result of the page 233 * @param hasMoreResult true if there are less one page more 234 * @throws SAXException if an error occurred 235 */ 236 protected void saxPagination(int page, int offset, int limit, int nbResult, boolean hasMoreResult) throws SAXException 237 { 238 AttributesImpl atts = new AttributesImpl(); 239 atts.addCDATAAttribute("start", String.valueOf(offset)); 240 atts.addCDATAAttribute("end", String.valueOf(offset + nbResult)); 241 XMLUtils.startElement(contentHandler, "pagination", atts); 242 243 int nbPage = page; 244 if (hasMoreResult) 245 { 246 nbPage++; 247 } 248 249 for (int i = 1; i <= nbPage; i++) 250 { 251 AttributesImpl attPage = new AttributesImpl(); 252 attPage.addCDATAAttribute("index", String.valueOf(i)); 253 attPage.addCDATAAttribute("start", String.valueOf((i - 1) * limit)); 254 XMLUtils.createElement(contentHandler, "page", attPage); 255 } 256 257 XMLUtils.endElement(contentHandler, "pagination"); 258 } 259 260 /** 261 * Sax a query result. 262 * @param query the query. 263 * @param result the result to generate. 264 * @throws SAXException if an error occurs while saxing 265 */ 266 protected void saxResult(Query query, QueryResult result) throws SAXException 267 { 268 String queryId = query.getId(); 269 try 270 { 271 // Others enhancement handlers should not touch the generated HTML 272 contentHandler.processingInstruction("ametys-unmodifiable", "start"); 273 274 AttributesImpl atts = new AttributesImpl(); 275 atts.addCDATAAttribute("class", "data " + queryId); 276 XMLUtils.startElement(contentHandler, "table", atts); 277 Collection<String> colNames = result.getColumnNames(); 278 279 if (!colNames.isEmpty()) 280 { 281 XMLUtils.startElement(contentHandler, "tr"); 282 for (String columnName : colNames) 283 { 284 XMLUtils.createElement(contentHandler, "th", columnName); 285 } 286 XMLUtils.endElement(contentHandler, "tr"); 287 } 288 289 int index = 0; 290 for (QueryResultRow row : result) 291 { 292 AttributesImpl attr = new AttributesImpl(); 293 if (index % 2 != 0) 294 { 295 attr.addAttribute("", "class", "class", "CDATA", "odd"); 296 } 297 else 298 { 299 attr.addAttribute("", "class", "class", "CDATA", "even"); 300 } 301 XMLUtils.startElement(contentHandler, "tr", attr); 302 303 for (String columnName : colNames) 304 { 305 String value = row.get(columnName); 306 XMLUtils.createElement(contentHandler, "td", StringUtils.defaultString(value)); 307 } 308 XMLUtils.endElement(contentHandler, "tr"); 309 index++; 310 } 311 312 XMLUtils.endElement(contentHandler, "table"); 313 314 contentHandler.processingInstruction("ametys-unmodifiable", "end"); 315 } 316 catch (DataInclusionException e) 317 { 318 String message = "Unable to get the query results (query ID : " + queryId + ")"; 319 getLogger().error(message, e); 320 throw new SAXException(message, e); 321 } 322 finally 323 { 324 result.close(); 325 } 326 } 327 /** 328 * Get the page index 329 * @param request The request 330 * @return The page index 331 */ 332 protected int getPageIndex (Request request) 333 { 334 Enumeration paramNames = request.getParameterNames(); 335 while (paramNames.hasMoreElements()) 336 { 337 String param = (String) paramNames.nextElement(); 338 if (param.startsWith("page-")) 339 { 340 return Integer.parseInt(param.substring("page-".length())); 341 } 342 } 343 return 1; 344 } 345}