001/* 002 * Copyright 2013 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.cms.search.cocoon; 017 018import java.io.IOException; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Date; 022import java.util.Map; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.ProcessingException; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Request; 029import org.apache.cocoon.generation.ServiceableGenerator; 030import org.apache.cocoon.xml.AttributesImpl; 031import org.apache.cocoon.xml.XMLUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.xml.sax.SAXException; 034 035import org.ametys.cms.contenttype.ContentTypesHelper; 036import org.ametys.cms.contenttype.MetadataType; 037import org.ametys.cms.repository.Content; 038import org.ametys.cms.search.SearchResult; 039import org.ametys.cms.search.SearchResults; 040import org.ametys.cms.search.content.ContentValuesExtractorFactory; 041import org.ametys.cms.search.content.ContentValuesExtractorFactory.SearchModelContentValuesExtractor; 042import org.ametys.cms.search.model.ResultField; 043import org.ametys.cms.search.ui.model.SearchUIColumn; 044import org.ametys.cms.search.ui.model.SearchUIModel; 045import org.ametys.core.user.User; 046import org.ametys.core.user.UserManager; 047import org.ametys.core.util.ServerCommHelper; 048import org.ametys.plugins.repository.AmetysObjectResolver; 049import org.ametys.plugins.repository.AmetysRepositoryException; 050import org.ametys.runtime.i18n.I18nizableText; 051import org.ametys.runtime.parameter.Enumerator; 052import org.ametys.runtime.parameter.ParameterHelper; 053 054/** 055 * Generate contents returned by the {@link SearchAction}. 056 */ 057public class SearchGenerator extends ServiceableGenerator 058{ 059 060 /** The {@link AmetysObjectResolver} */ 061 protected AmetysObjectResolver _resolver; 062 /** The user manager. */ 063 protected UserManager _userManager; 064 /** Helper for content types */ 065 protected ContentTypesHelper _contentTypesHelper; 066 /** The server comm helper */ 067 protected ServerCommHelper _serverCommHelper; 068 /** The content values extractor factory. */ 069 protected ContentValuesExtractorFactory _valuesExtractorFactory; 070 071 @Override 072 public void service(ServiceManager smanager) throws ServiceException 073 { 074 super.service(smanager); 075 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 076 _userManager = (UserManager) smanager.lookup(UserManager.ROLE); 077 _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 078 _serverCommHelper = (ServerCommHelper) smanager.lookup(ServerCommHelper.ROLE); 079 _valuesExtractorFactory = (ContentValuesExtractorFactory) smanager.lookup(ContentValuesExtractorFactory.ROLE); 080 } 081 082 @SuppressWarnings("unchecked") 083 @Override 084 public void generate() throws IOException, SAXException, ProcessingException 085 { 086 Request request = ObjectModelHelper.getRequest(objectModel); 087 088 SearchResults<Content> results = (SearchResults<Content>) request.getAttribute(SearchAction.SEARCH_RESULTS); 089 SearchUIModel model = (SearchUIModel) request.getAttribute(SearchAction.SEARCH_MODEL); 090 091 Map<String, Object> jsParameters = _serverCommHelper.getJsParameters(); 092 093 Map<String, Object> contextualParameters = (Map<String, Object>) jsParameters.get("contextualParameters"); 094 if (contextualParameters == null) 095 { 096 contextualParameters = Collections.emptyMap(); 097 } 098 099 boolean generateLabels = parameters.getParameterAsBoolean("generateLabels", false); 100 String language = parameters.getParameter("language", (String) request.getAttribute("sitemapLanguage")); 101 102 boolean fullValues = parameters.getParameterAsBoolean("fullValues", "true".equals(contextualParameters.get("fullValues"))); 103 104 SearchModelContentValuesExtractor extractor = _valuesExtractorFactory.create(model).setFullValues(fullValues); 105 106 Collection<SearchUIColumn> resultFields = model.getResultFields(contextualParameters).values(); 107 108 contentHandler.startDocument(); 109 XMLUtils.startElement(contentHandler, "contents"); 110 111 Iterable<SearchResult<Content>> contents = results.getResults(); 112 for (SearchResult<Content> result : contents) 113 { 114 Content content = result.getObject(); 115 Map<String, Object> values = extractor.getValues(content); 116 saxContent(content, resultFields, values, generateLabels, language); 117 } 118 119 XMLUtils.endElement(contentHandler, "contents"); 120 contentHandler.endDocument(); 121 } 122 123 /** 124 * Generate the content's values. 125 * @param content The content. 126 * @param fields The model result fields. 127 * @param values The content values, extracted from the fields. 128 * @param generateLabels true to generate value labels, false to generate raw values. 129 * @param language The language. 130 * @throws SAXException If an error occurs generating the XML. 131 */ 132 protected void saxContent(Content content, Collection<SearchUIColumn> fields, Map<String, Object> values, boolean generateLabels, String language) throws SAXException 133 { 134 AttributesImpl attrs = new AttributesImpl(); 135 136 attrs.addCDATAAttribute("id", content.getId()); 137 attrs.addCDATAAttribute("name", content.getName()); 138 attrs.addCDATAAttribute("title", content.getTitle()); 139 attrs.addCDATAAttribute("language", content.getLanguage()); 140 String iconGlyph = _contentTypesHelper.getIconGlyph(content); 141 if (iconGlyph != null) 142 { 143 attrs.addCDATAAttribute("iconGlyph", iconGlyph); 144 } 145 String iconDecorator = _contentTypesHelper.getIconDecorator(content); 146 if (iconDecorator != null) 147 { 148 attrs.addCDATAAttribute("iconDecorator", iconDecorator); 149 } 150 attrs.addCDATAAttribute("smallIcon", _contentTypesHelper.getSmallIcon(content)); 151 attrs.addCDATAAttribute("mediumIcon", _contentTypesHelper.getMediumIcon(content)); 152 attrs.addCDATAAttribute("largeIcon", _contentTypesHelper.getLargeIcon(content)); 153 154 XMLUtils.startElement(contentHandler, "content", attrs); 155 156 XMLUtils.startElement(contentHandler, "values"); 157 158 for (ResultField resultField : fields) 159 { 160 Object value = values.get(resultField.getId()); 161 162 if (generateLabels) 163 { 164 saxLabel(resultField, value, language); 165 } 166 else 167 { 168 saxValue(resultField, value); 169 } 170 } 171 XMLUtils.endElement(contentHandler, "values"); 172 173 XMLUtils.endElement(contentHandler, "content"); 174 } 175 176 /** 177 * Generate the value of a result field for a content. 178 * @param resultField The result field. 179 * @param value The content value. 180 * @throws SAXException If an error occurs generating the XML. 181 */ 182 protected void saxValue(ResultField resultField, Object value) throws SAXException 183 { 184 String fieldName = resultField.getId(); 185 186 if (value != null) 187 { 188 if (value instanceof I18nizableText) 189 { 190 ((I18nizableText) value).toSAX(contentHandler, fieldName); 191 } 192 else if (value instanceof Date) 193 { 194 XMLUtils.createElement(contentHandler, fieldName, ParameterHelper.valueToString(value)); 195 } 196 else if (value instanceof Map) 197 { 198 AttributesImpl valueAttrs = new AttributesImpl(); 199 200 Map<?, ?> valueMap = (Map<?, ?>) value; 201 for (Map.Entry<?, ?> entry : valueMap.entrySet()) 202 { 203 valueAttrs.addCDATAAttribute(entry.getKey().toString(), entry.getValue().toString()); 204 } 205 206 XMLUtils.createElement(contentHandler, fieldName, valueAttrs); 207 } 208 else if (value instanceof Collection) 209 { 210 XMLUtils.startElement(contentHandler, fieldName); 211 Collection<?> values = (Collection<?>) value; 212 for (Object valueObj : values) 213 { 214 XMLUtils.createElement(contentHandler, "value", valueObj.toString()); 215 } 216 XMLUtils.endElement(contentHandler, fieldName); 217 } 218 else 219 { 220 XMLUtils.createElement(contentHandler, fieldName, value.toString()); 221 } 222 } 223 } 224 225 /** 226 * Generate a human-readable representation of a result field for a content. 227 * @param resultField The result field. 228 * @param value The content value. 229 * @param language The language. 230 * @throws SAXException If an error occurs generating the XML. 231 */ 232 protected void saxLabel(ResultField resultField, Object value, String language) throws SAXException 233 { 234 String fieldName = resultField.getId(); 235 MetadataType type = resultField.getType(); 236 Enumerator enumerator = resultField.getEnumerator(); 237 238 if (value != null) 239 { 240 if (type == MetadataType.USER) 241 { 242 generateUserLabel(resultField, value); 243 } 244 else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) 245 { 246 generateContentLabel(resultField, value); 247 } 248 else if (enumerator != null) 249 { 250 generateEnumeratedLabel(resultField, value); 251 } 252 else if (value instanceof I18nizableText) 253 { 254 ((I18nizableText) value).toSAX(contentHandler, fieldName); 255 } 256 else if (value instanceof Date) 257 { 258 XMLUtils.createElement(contentHandler, fieldName, ParameterHelper.valueToString(value)); 259 } 260 else if (value instanceof Map) 261 { 262 Map<?, ?> valueMap = (Map<?, ?>) value; 263 XMLUtils.createElement(contentHandler, fieldName, StringUtils.join(valueMap.values(), ", ")); 264 } 265 else if (value instanceof Collection) 266 { 267 Collection<?> values = (Collection<?>) value; 268 XMLUtils.createElement(contentHandler, fieldName, StringUtils.join(values, ", ")); 269 } 270 else 271 { 272 XMLUtils.createElement(contentHandler, fieldName, value.toString()); 273 } 274 } 275 } 276 277 /** 278 * Generate the label of an user value. 279 * @param resultField The result field. 280 * @param value The content value. 281 * @throws SAXException If an error occurs generating the XML. 282 */ 283 @SuppressWarnings("unchecked") 284 protected void generateUserLabel(ResultField resultField, Object value) throws SAXException 285 { 286 String fieldName = resultField.getId(); 287 288 String login; 289 String populationId; 290 if (value instanceof String) 291 { 292 String[] parts = ((String) value).split("#"); 293 login = parts[0]; 294 populationId = parts[1]; 295 } 296 else 297 { 298 login = ((Map<String, String>) value).get("login"); 299 populationId = ((Map<String, String>) value).get("populationId"); 300 } 301 302 User user = _userManager.getUser(populationId, login); 303 if (user != null) 304 { 305 XMLUtils.createElement(contentHandler, fieldName, user.getFullName()); 306 } 307 } 308 309 /** 310 * Generate the label of an content value. 311 * @param resultField The result field. 312 * @param value The content value. 313 * @throws SAXException If an error occurs generating the XML. 314 */ 315 protected void generateContentLabel(ResultField resultField, Object value) throws SAXException 316 { 317 try 318 { 319 String fieldName = resultField.getId(); 320 321 Content content = _resolver.resolveById(value.toString()); 322 XMLUtils.createElement(contentHandler, fieldName, content.getTitle()); 323 } 324 catch (AmetysRepositoryException e) 325 { 326 // Ignore 327 } 328 } 329 330 /** 331 * Generate the label of an enumerated value. 332 * @param resultField The result field. 333 * @param value The content value. 334 * @throws SAXException If an error occurs generating the XML. 335 */ 336 protected void generateEnumeratedLabel(ResultField resultField, Object value) throws SAXException 337 { 338 String fieldName = resultField.getId(); 339 Enumerator enumerator = resultField.getEnumerator(); 340 341 if (value instanceof Collection) 342 { 343 Collection<?> values = (Collection<?>) value; 344 XMLUtils.startElement(contentHandler, fieldName); 345 int i = 0; 346 for (Object valueObj : values) 347 { 348 try 349 { 350 I18nizableText entry = enumerator.getEntry(valueObj.toString()); 351 if (entry != null) 352 { 353 if (i > 0) 354 { 355 XMLUtils.data(contentHandler, ", "); 356 } 357 entry.toSAX(contentHandler); 358 i++; 359 } 360 } 361 catch (Exception e) 362 { 363 // Ignore. 364 } 365 } 366 XMLUtils.endElement(contentHandler, fieldName); 367 } 368 else 369 { 370 try 371 { 372 I18nizableText entry = enumerator.getEntry(value.toString()); 373 if (entry != null) 374 { 375 entry.toSAX(contentHandler, fieldName); 376 } 377 } 378 catch (Exception e) 379 { 380 // Ignore. 381 } 382 } 383 } 384 385}