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.requesttime.impl;
017
018import java.io.IOException;
019import java.util.Collection;
020import java.util.Optional;
021
022import org.apache.avalon.framework.configuration.Configurable;
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.xml.AttributesImpl;
027import org.apache.cocoon.xml.XMLUtils;
028import org.apache.commons.lang.StringUtils;
029import org.xml.sax.ContentHandler;
030import org.xml.sax.SAXException;
031
032import org.ametys.cms.data.type.impl.PasswordRepositoryElementType;
033import org.ametys.web.WebConstants;
034import org.ametys.web.frontoffice.search.instance.SearchServiceInstance;
035import org.ametys.web.frontoffice.search.instance.model.Link;
036import org.ametys.web.frontoffice.search.instance.model.ResultDisplay;
037import org.ametys.web.frontoffice.search.instance.model.ResultDisplayType;
038import org.ametys.web.frontoffice.search.metamodel.AdditionalParameterValueMap;
039import org.ametys.web.frontoffice.search.metamodel.AdditionalSearchServiceParameter;
040import org.ametys.web.frontoffice.search.requesttime.SearchComponent;
041import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments;
042import org.ametys.web.repository.page.Page;
043import org.ametys.web.repository.page.SitemapElement;
044import org.ametys.web.repository.page.ZoneItem;
045import org.ametys.web.service.ServiceParameter;
046
047/**
048 * {@link SearchComponent} executing in two parts: opening 'search' tag and SAXing general data, then closing 'search' tag.
049 */
050public class SaxGeneralDataSearchComponent implements SearchComponent, Configurable
051{
052    private static final String __IS_AJAX_PARAMETER_NAME = "ajax";
053    
054    private int _part;
055    
056    @Override
057    public void configure(Configuration configuration) throws ConfigurationException
058    {
059        _part = configuration.getChild("part").getValueAsInteger();
060    }
061
062    @Override
063    public int priority()
064    {
065        return _part == 1 ? MAX_PRIORITY + 1000 : MIN_PRIORITY - 1000;
066    }
067
068    @Override
069    public boolean supports(SearchComponentArguments args)
070    {
071        return !args.generatorParameters().getParameterAsBoolean(DISABLE_DEFAULT_SAX_PARAMETER_NAME, false);
072    }
073
074    @Override
075    public void execute(SearchComponentArguments args) throws Exception
076    {
077        ContentHandler contentHandler = args.contentHandler();
078        if (_part == 1)
079        {
080            String currentSiteName = args.currentSite().getName();
081            String lang = args.currentLang();
082            SitemapElement page = args.currentPage();
083            
084            AttributesImpl attrs = new AttributesImpl();
085            attrs.addCDATAAttribute("site", currentSiteName);
086            attrs.addCDATAAttribute("lang", lang);
087            attrs.addCDATAAttribute("ajax", args.generatorParameters().getParameter(__IS_AJAX_PARAMETER_NAME, "false"));
088
089            XMLUtils.startElement(contentHandler, "search", attrs);
090
091            ZoneItem zoneItem = (ZoneItem) args.request().getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM);
092            saxServiceIdentifiers(args, zoneItem);
093            saxCacheable(args, zoneItem);
094            saxAdditionalParameters(args);
095            saxAdditionalInfos(args);
096
097            // The search url
098            String url = lang + "/" + page.getPathInSitemap() + ".html";
099            if (args.isDebug())
100            {
101                url = DebugSearchComponent.appendDebugRequestParameters(url, args);
102            }
103            XMLUtils.createElement(contentHandler, "url", url);
104
105            SearchServiceInstance serviceInstance = args.serviceInstance();
106            ResultDisplay resultDisplay = serviceInstance.getResultDisplay();
107            // Display the form and results on same page?
108            ResultDisplayType resultDisplayType = resultDisplay.getType();
109            XMLUtils.createElement(contentHandler, "result-display-type", resultDisplayType.name());
110            
111            XMLUtils.createElement(contentHandler, "header", serviceInstance.getTitle());
112            XMLUtils.createElement(contentHandler, "result-page", resultDisplayType == ResultDisplayType.ON_PAGE ? resultDisplay.resultPage().get().getId() : "");
113            XMLUtils.createElement(contentHandler, "compute-counts", Boolean.toString(serviceInstance.computeCriteriaCounts()));
114            XMLUtils.createElement(contentHandler, "launch-search-at-startup", Boolean.toString(resultDisplay.launchSearchAtStartup().orElse(false)));
115            saxRSSFeedURL(contentHandler, serviceInstance, zoneItem);
116            Link link = serviceInstance.getLink();
117            Optional<Page> linkTarget = link.getTarget();
118            if (linkTarget.isPresent())
119            {
120                XMLUtils.startElement(contentHandler, "link");
121                XMLUtils.createElement(contentHandler, "page", linkTarget.get().getId());
122                XMLUtils.createElement(contentHandler, "title", link.getTitle());
123                XMLUtils.endElement(contentHandler, "link");
124            }
125        }
126        else
127        {
128            XMLUtils.endElement(contentHandler, "search");
129        }
130    }
131    
132    /**
133     * Generate the service identifiers: service group ID, ZoneItem ID, ...
134     * @param args The arguments
135     * @param zoneItem The zone item
136     * @throws SAXException if an error occurs SAXing data.
137     * @throws IOException if an error occurs SAXing data.
138     * @throws ProcessingException if a processing error occurs.
139     */
140    protected void saxServiceIdentifiers(SearchComponentArguments args, ZoneItem zoneItem) throws SAXException, IOException, ProcessingException
141    {
142        ContentHandler contentHandler = args.contentHandler();
143        
144        String serviceGroupId = args.serviceInstance().getResultDisplay().serviceGroupId();
145        
146        // The service group ID.
147        if (StringUtils.isNotEmpty(serviceGroupId))
148        {
149            XMLUtils.createElement(contentHandler, "group-id", serviceGroupId);
150        }
151        
152        // Generate the ZoneItem ID if it exists.
153        if (zoneItem != null)
154        {
155            AttributesImpl atts = new AttributesImpl();
156            atts.addCDATAAttribute("id", zoneItem.getId());
157            XMLUtils.createElement(contentHandler, "zone-item", atts);
158        }
159    }
160    
161    /**
162     * Generate the "cacheable" status of the service instance
163     * @param args The arguments
164     * @param zoneItem The zone item
165     * @throws SAXException if an error occurs SAXing data.
166     */
167    protected void saxCacheable(SearchComponentArguments args, ZoneItem zoneItem) throws SAXException
168    {
169        boolean cacheable = zoneItem == null ? false : args.service().isCacheable(args.currentPage(), zoneItem);
170        XMLUtils.createElement(args.contentHandler(), "cacheable", Boolean.toString(cacheable));
171    }
172    
173    /**
174     * Generate the value of the {@link AdditionalSearchServiceParameter}s
175     * @param args The arguments
176     * @throws SAXException if an error occurs SAXing data.
177     */
178    protected void saxAdditionalParameters(SearchComponentArguments args) throws SAXException
179    {
180        ContentHandler contentHandler = args.contentHandler();
181        SearchServiceInstance serviceInstance = args.serviceInstance();
182        Collection<AdditionalSearchServiceParameter> additionalParameters = serviceInstance.getAdditionalParameters();
183        AdditionalParameterValueMap additionalParameterValues = serviceInstance.getAdditionalParameterValues();
184        
185        XMLUtils.startElement(contentHandler, "additionalParameters");
186        for (AdditionalSearchServiceParameter additionalParameter : additionalParameters)
187        {
188            ServiceParameter serviceParameter = additionalParameter.getParameter();
189            String name = serviceParameter.getName();
190            if (PasswordRepositoryElementType.TYPE_ID.equals(serviceParameter.getType().getId()))
191            {
192                args.logger().info("Additional parameter value for '{}' will not be saxed because it is a password and it is probably not meant for the view.", name);
193            }
194            else
195            {
196                Object value = additionalParameterValues.getValue(name);
197                XMLUtils.createElement(contentHandler, name, String.valueOf(value));
198            }
199        }
200        XMLUtils.endElement(contentHandler, "additionalParameters");
201    }
202    
203    /**
204     * Generate any additional information.
205     * @param args The arguments
206     * @throws SAXException if an error occurs SAXing data.
207     * @throws IOException if an error occurs SAXing data.
208     * @throws ProcessingException if a processing error occurs.
209     */
210    protected void saxAdditionalInfos(SearchComponentArguments args) throws SAXException, IOException, ProcessingException
211    {
212        // Nothing to do here.
213    }
214    
215    /**
216     * Generate the URL for RSS feed
217     * @param contentHandler The content handler
218     * @param serviceInstance The service instance
219     * @param zoneItem The zone item
220     * @throws SAXException if an error occurs SAXing data.
221     */
222    protected void saxRSSFeedURL(ContentHandler contentHandler, SearchServiceInstance serviceInstance, ZoneItem zoneItem) throws SAXException
223    {
224        if (serviceInstance.handleRss() && zoneItem != null)
225        {
226            // Split protocol and id
227            String[] zoneItemId = zoneItem.getId().split("://");
228            String url = "_plugins/web/" + zoneItemId[0] + "/" + zoneItemId[1] + "/search-rss.xml";
229            
230            XMLUtils.createElement(contentHandler, "RSSFeedURL", url);
231        }
232    }
233}