001/* 002 * Copyright 2017 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.ArrayList; 020import java.util.List; 021import java.util.Locale; 022import java.util.Map; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.cocoon.ProcessingException; 029import org.apache.cocoon.components.ContextHelper; 030import org.apache.cocoon.components.source.SourceUtil; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.XMLUtils; 034import org.apache.commons.lang3.StringUtils; 035import org.apache.excalibur.source.Source; 036import org.xml.sax.SAXException; 037 038import org.ametys.cms.contenttype.ContentType; 039import org.ametys.cms.repository.Content; 040import org.ametys.cms.search.GroupSearchContentHelper; 041import org.ametys.cms.search.SearchResults; 042import org.ametys.cms.search.model.SearchModel; 043import org.ametys.cms.search.ui.model.ColumnHelper.Column; 044import org.ametys.cms.search.ui.model.SearchUIModel; 045import org.ametys.core.util.IgnoreRootHandler; 046import org.ametys.plugins.repository.AmetysRepositoryException; 047import org.ametys.runtime.model.ViewItemContainer; 048 049/** 050 * Generate contents returned by the {@link SearchAction}, groups by selected fields. 051 */ 052public class DocSearchGenerator extends SearchGenerator 053{ 054 private GroupSearchContentHelper _groupSearchContentHelper; 055 056 @Override 057 public void service(ServiceManager serviceManager) throws ServiceException 058 { 059 super.service(serviceManager); 060 _groupSearchContentHelper = (GroupSearchContentHelper) serviceManager.lookup(GroupSearchContentHelper.ROLE); 061 } 062 063 @Override 064 protected void saxContents(SearchModel model, SearchResults<Content> results, String versionLabel, Map<String, Object> jsParameters, Locale locale, Map<String, Object> contextualParameters) throws SAXException, AmetysRepositoryException, IOException, ProcessingException 065 { 066 String modelId = (String) jsParameters.get("model"); 067 SearchUIModel uiModel = _searchModelManager.getExtension(modelId); 068 069 Set<String> cTypeIds = getCommonContentTypeIds(jsParameters, uiModel, contextualParameters); 070 071 List<String> groupingFields = _getGroupingFields(jsParameters); 072 073 // Columns 074 List<Column> columns = getColumnsFromParameters(jsParameters, cTypeIds); 075 List<String> columnIds = columns.stream() 076 .map(Column::getId) 077 .collect(Collectors.toList()); 078 079 ViewItemContainer resultItems = model.getResultItems(contextualParameters); 080 ViewItemContainer filteredResultItems = (ViewItemContainer) SearchGeneratorHelper.copyAndFilterViewItemAccessor(resultItems, columnIds); 081 SearchGeneratorHelper.removeResultItems(filteredResultItems, groupingFields); 082 083 ViewItemContainer uiResultItems = _columnHelper.createViewFromColumns(cTypeIds, columns, false); 084 085 Set<ContentType> contentTypes = cTypeIds.stream() 086 .map(_contentTypeExtensionPoint::getExtension) 087 .collect(Collectors.toSet()); 088 GroupSearchContent groupSearchContent = _groupSearchContentHelper.organizeContentsInGroups(results.getObjects(), groupingFields, contentTypes, locale); 089 090 // Set the request attributes from the contextual parameters 091 Request request = ContextHelper.getRequest(_context); 092 for (String name : contextualParameters.keySet()) 093 { 094 request.setAttribute(name, contextualParameters.get(name)); 095 } 096 097 XMLUtils.startElement(contentHandler, "contents"); 098 _saxGroup(groupSearchContent, 0, filteredResultItems, uiResultItems, locale); 099 XMLUtils.endElement(contentHandler, "contents"); 100 } 101 102 private List<String> _getGroupingFields(Map<String, Object> jsParameters) 103 { 104 @SuppressWarnings("unchecked") 105 List<String> groupingFields = (List<String>) jsParameters.get("groupingFields"); 106 if (groupingFields == null || groupingFields.size() == 1 && StringUtils.isEmpty(groupingFields.get(0))) 107 { 108 groupingFields = new ArrayList<>(); 109 } 110 111 return groupingFields; 112 } 113 114 /** 115 * Sax a group of content recursively. 116 * First level (level=0) will not create a xml group (usually, 1st group is just here to handle a list) 117 * @param group group to sax 118 * @param level current level (start with zero) 119 * @param resultItems result items to sax 120 * @param uiResultItems The columns to be exported 121 * @param locale The locale for search. Can be null. 122 * @throws SAXException if a error occurred during sax 123 * @throws AmetysRepositoryException if a error occurred 124 * @throws IOException if a error occurred 125 * @throws ProcessingException if a error occurred 126 */ 127 private void _saxGroup(GroupSearchContent group, int level, ViewItemContainer resultItems, ViewItemContainer uiResultItems, Locale locale) throws SAXException, AmetysRepositoryException, IOException, ProcessingException 128 { 129 if (level > 0) 130 { 131 AttributesImpl attrs = new AttributesImpl(); 132 attrs.addCDATAAttribute("level", String.valueOf(level)); 133 attrs.addCDATAAttribute("fullModelPath", group.getGroupFieldPath()); 134 attrs.addCDATAAttribute("value", group.getGroupName()); 135 XMLUtils.startElement(contentHandler, "group", attrs); 136 } 137 138 for (GroupSearchContent groupSearchContent : group.getSubList()) 139 { 140 _saxGroup(groupSearchContent, level + 1, resultItems, uiResultItems, locale); 141 } 142 143 for (Content content : group.getContents()) 144 { 145 _saxContentForDoc(content, resultItems, uiResultItems, locale); 146 } 147 148 if (level > 0) 149 { 150 XMLUtils.endElement(contentHandler, "group"); 151 } 152 } 153 /** 154 * SAX the result content 155 * @param content the result 156 * @param resultItems the result items 157 * @param uiResultItems The columns to be exported 158 * @param locale The locale for search. Can be null. 159 * @throws SAXException if a error occurred during sax 160 * @throws AmetysRepositoryException if a error occurred 161 * @throws IOException if a error occurred 162 * @throws ProcessingException if a error occurred 163 */ 164 private void _saxContentForDoc(Content content, ViewItemContainer resultItems, ViewItemContainer uiResultItems, Locale locale) throws SAXException, AmetysRepositoryException, IOException, ProcessingException 165 { 166 Request request = ContextHelper.getRequest(_context); 167 168 try 169 { 170 request.setAttribute(Content.class.getName(), content); 171 request.setAttribute("resultItems", resultItems); 172 request.setAttribute("viewName", "export-word"); 173 request.setAttribute("forceRemoteUrl", true); 174 175 request.setAttribute("uiResultItems", uiResultItems); 176 177 AttributesImpl attrs = new AttributesImpl(); 178 attrs.addCDATAAttribute("id", content.getId()); 179 attrs.addCDATAAttribute("name", content.getName()); 180 attrs.addCDATAAttribute("title", content.getTitle(locale)); 181 if (content.getLanguage() != null) 182 { 183 attrs.addCDATAAttribute("language", content.getLanguage()); 184 } 185 186 XMLUtils.startElement(contentHandler, "content", attrs); 187 Source contentSource = resolver.resolveURI("cocoon:/export/content.doc" + (locale != null ? "?lang=" + locale.getLanguage() : "")); 188 SourceUtil.toSAX(contentSource, new IgnoreRootHandler(contentHandler)); 189 190 XMLUtils.endElement(contentHandler, "content"); 191 } 192 finally 193 { 194 request.setAttribute(Content.class.getName(), null); 195 request.removeAttribute("forceRemoteUrl"); 196 } 197 } 198 199}