001/*
002 *  Copyright 2022 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.calendar.search;
017
018import java.util.Collection;
019import java.util.Collections;
020import java.util.HashSet;
021import java.util.LinkedHashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Objects;
025import java.util.Set;
026import java.util.stream.Collectors;
027
028import org.apache.avalon.framework.parameters.Parameters;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.xml.AttributesImpl;
032import org.apache.cocoon.xml.XMLUtils;
033import org.xml.sax.ContentHandler;
034import org.xml.sax.SAXException;
035
036import org.ametys.cms.search.advanced.AbstractTreeNode;
037import org.ametys.cms.search.advanced.TreeLeaf;
038import org.ametys.cms.tag.CMSTag;
039import org.ametys.cms.tag.Tag;
040import org.ametys.cms.tag.TagProviderExtensionPoint;
041import org.ametys.plugins.calendar.icsreader.IcsEventHelper;
042import org.ametys.web.frontoffice.search.instance.SearchServiceInstance;
043import org.ametys.web.frontoffice.search.instance.model.FOSearchCriterion;
044import org.ametys.web.frontoffice.search.requesttime.SearchComponent;
045import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments;
046import org.ametys.web.frontoffice.search.requesttime.impl.SaxFormSearchComponent;
047import org.ametys.web.frontoffice.search.requesttime.input.SearchUserInputs;
048import org.ametys.web.repository.page.ZoneItem;
049
050/**
051 * {@link SearchComponent} for saxing form for calendar search service
052 * Reactivate default {@link SaxFormSearchComponent} (disabled by "disableDefaultSax" parameter)
053 */
054public class CalendarSaxFormSearchComponent extends SaxFormSearchComponent
055{
056    private IcsEventHelper _icsEventHelper;
057    private TagProviderExtensionPoint _tagProviderEP;
058    
059    @Override
060    public void service(ServiceManager smanager) throws ServiceException
061    {
062        super.service(smanager);
063        _icsEventHelper = (IcsEventHelper) smanager.lookup(IcsEventHelper.ROLE);
064        _tagProviderEP = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE);
065    }
066    
067    @Override
068    public boolean supports(SearchComponentArguments args)
069    {
070        return CalendarSearchService.isActive(args);
071    }
072    
073    @Override
074    public void execute(SearchComponentArguments args) throws Exception
075    {
076        ContentHandler contentHandler = args.contentHandler();
077        SearchServiceInstance serviceInstance = args.serviceInstance();
078        List<FOSearchCriterion> nonStaticCriteria = serviceInstance.getCriterionTree()
079                .map(AbstractTreeNode::getFlatLeaves)
080                .orElseGet(Collections::emptyList)
081                .stream()
082                .map(TreeLeaf::getValue)
083                .filter(c -> !c.getMode().isStatic())
084                .collect(Collectors.toList());
085        Parameters parameters = args.generatorParameters();
086        
087        XMLUtils.startElement(contentHandler, "form");
088        
089        XMLUtils.startElement(contentHandler, "fields");
090        saxFormFields(contentHandler, parameters, nonStaticCriteria, args.currentLang(), args.currentSite(), serviceInstance.computeCriteriaCounts());
091        XMLUtils.endElement(contentHandler, "fields");
092        
093        SearchUserInputs userInputs = args.userInputs();
094        
095        Map<String, Object> userCriteria = userInputs.criteria();
096        XMLUtils.startElement(contentHandler, "values");
097        saxFormValues(contentHandler, parameters, nonStaticCriteria, userCriteria);
098        XMLUtils.endElement(contentHandler, "values");
099        
100        String zoneItemId = args.serviceInstance().getId();
101        ZoneItem zoneItem = (ZoneItem) _resolver.resolveById(zoneItemId);
102        
103        if (CalendarSearchService.hasTagCategories(args.serviceInstance()))
104        {
105            List<String> categoryNames = CalendarSearchService.getTagCategories(serviceInstance);
106            Set<Tag> tagCategories = getTagCategories(categoryNames, args);
107            tagCategories.addAll(_icsEventHelper.getIcsTags(zoneItem, args.currentSite().getName()));
108            saxTagFacet(contentHandler, tagCategories, args);
109        }
110        
111        XMLUtils.endElement(contentHandler, "form");
112    }
113    
114    /**
115     * Get the configured tag categories
116     * @param categoryNames The parent tag category
117     * @param args the search arguments
118     * @return the tags
119     */
120    protected Set<Tag> getTagCategories(List<String> categoryNames, SearchComponentArguments args)
121    {
122        Set<Tag> tags = new HashSet<>();
123        
124        if (categoryNames != null && !categoryNames.isEmpty())
125        {
126            Map<String, Object> params = Map.of("siteName", args.currentSite().getName());
127            
128            Set<CMSTag> tagCategories = categoryNames.stream()
129                    .map(c -> _tagProviderEP.getTag(c, params))
130                    .filter(Objects::nonNull)
131                    .collect(Collectors.toSet());
132            
133            for (CMSTag cmsTag : tagCategories)
134            {
135                tags.addAll(_getChildTags(cmsTag));
136            }
137        }
138        
139        return tags;
140    }
141    
142    private Set<Tag> _getChildTags(Tag tag)
143    {
144        Set<Tag> allTags = new LinkedHashSet<>();
145
146        Map<String, ? extends Tag> childTagsMap = tag.getTags();
147
148        if (childTagsMap != null)
149        {
150            Collection<? extends Tag> childTags = childTagsMap.values();
151            allTags.addAll(childTags);
152            
153            for (Tag child : childTags)
154            {
155                allTags.addAll(_getChildTags(child));
156            }
157        }
158
159        return allTags;
160    }
161    
162    /**
163     * Generate the list of tags
164     * @param handler the content handler
165     * @param tags the list of tags
166     * @param args the search arguments
167     * @throws SAXException if an error occurs while saxing
168     */
169    protected void saxTagFacet(ContentHandler handler, Collection<Tag> tags, SearchComponentArguments args) throws SAXException
170    {
171        List<String> selectedTags = CalendarSearchService.getSelectedTags(args);
172        
173        XMLUtils.startElement(handler, "tag-facet");
174        for (Tag tag : tags)
175        {
176            AttributesImpl attrs = new AttributesImpl();
177            attrs.addCDATAAttribute("selected",  String.valueOf(selectedTags.contains(tag.getName())));
178            CalendarSearchService.saxTag(handler, attrs, tag);
179        }
180        XMLUtils.endElement(handler, "tag-facet");
181    }
182    
183}