001/*
002 *  Copyright 2025 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.odforientation;
017
018import java.io.IOException;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.List;
022import java.util.Locale;
023import java.util.Optional;
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.lang3.LocaleUtils;
034import org.apache.commons.lang3.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.contenttype.ContentTypesHelper;
038import org.ametys.cms.repository.ContentTypeExpression;
039import org.ametys.cms.search.advanced.AbstractTreeNode;
040import org.ametys.cms.search.advanced.TreeLeaf;
041import org.ametys.core.util.LambdaUtils;
042import org.ametys.odf.orgunit.OrgUnit;
043import org.ametys.odf.orgunit.OrgUnitFactory;
044import org.ametys.odf.orgunit.RootOrgUnitProvider;
045import org.ametys.plugins.repository.AmetysObjectIterable;
046import org.ametys.plugins.repository.AmetysObjectResolver;
047import org.ametys.plugins.repository.query.QueryHelper;
048import org.ametys.plugins.repository.query.expression.Expression;
049import org.ametys.plugins.repository.query.expression.Expression.Operator;
050import org.ametys.runtime.model.View;
051import org.ametys.runtime.model.type.DataContext;
052import org.ametys.web.frontoffice.search.SearchService;
053import org.ametys.web.frontoffice.search.instance.SearchServiceInstance;
054import org.ametys.web.frontoffice.search.instance.SearchServiceInstanceManager;
055import org.ametys.web.repository.page.Page;
056import org.ametys.web.repository.page.Page.PageType;
057import org.ametys.web.repository.page.ZoneItem;
058import org.ametys.web.repository.page.ZoneItem.ZoneType;
059
060/**
061 * Generates the exhaustive list of orgunits
062 */
063public class OrgUnitsGenerator extends ServiceableGenerator
064{
065    private AmetysObjectResolver _resolver;
066    private RootOrgUnitProvider _rootOrgUnitProvider;
067    private ContentTypesHelper _cTypeHelper;
068    private SearchServiceInstanceManager _searchServiceInstanceManager;
069
070    @Override
071    public void service(ServiceManager serviceManager) throws ServiceException
072    {
073        super.service(serviceManager);
074        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
075        _rootOrgUnitProvider = (RootOrgUnitProvider) serviceManager.lookup(RootOrgUnitProvider.ROLE);
076        _cTypeHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE);
077        _searchServiceInstanceManager = (SearchServiceInstanceManager) serviceManager.lookup(SearchServiceInstanceManager.ROLE);
078    }
079    
080    @Override
081    public void generate() throws IOException, SAXException, ProcessingException
082    {
083        Request request = ObjectModelHelper.getRequest(objectModel);
084        
085        String lang = parameters.getParameter("lang", (String) request.getAttribute("sitemapLanguage"));
086        Locale defaultLocale = LocaleUtils.toLocale(lang);
087        
088        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
089        
090        String contentView = zoneItem.getServiceParameters().getValue("contentView");
091        View view = _cTypeHelper.getView(contentView, new String[] {OrgUnitFactory.ORGUNIT_CONTENT_TYPE}, new String[0]);
092        
093        
094        contentHandler.startDocument();
095        
096        XMLUtils.startElement(contentHandler, "orgunits");
097        
098        Expression expr = new ContentTypeExpression(Operator.EQ, OrgUnitFactory.ORGUNIT_CONTENT_TYPE);
099        String xPathQuery = QueryHelper.getXPathQuery(null, OrgUnitFactory.ORGUNIT_NODETYPE, expr);
100        AmetysObjectIterable<OrgUnit> orgUnits = _resolver.query(xPathQuery);
101        
102        List<String> excludedOrgunits = Arrays.asList(zoneItem.getServiceParameters().getValue("excludedOrgunits"));
103        
104        orgUnits.stream()
105            .filter(ou -> !_rootOrgUnitProvider.isRoot(ou) && !excludedOrgunits.contains(ou.getId()))
106            .forEach(LambdaUtils.wrapConsumer(ou -> {
107                AttributesImpl attrs = new AttributesImpl();
108                attrs.addCDATAAttribute("id", ou.getId());
109                
110                XMLUtils.startElement(contentHandler, "orgunit", attrs);
111                ou.dataToSAX(contentHandler, view, DataContext.newInstance().withLocale(defaultLocale).withEmptyValues(false));
112                XMLUtils.endElement(contentHandler, "orgunit");
113            }));
114        
115        _saxSearchForm(zoneItem);
116        
117        XMLUtils.endElement(contentHandler, "orgunits");
118        
119        contentHandler.endDocument();
120    }
121    
122    private void _saxSearchForm(ZoneItem zoneItem) throws SAXException
123    {
124        String criterionInputName = _getOrgUnitCriterionInputName(zoneItem);
125        if (criterionInputName != null)
126        {
127            XMLUtils.startElement(contentHandler, "search-form");
128            XMLUtils.createElement(contentHandler, "criterion", criterionInputName);
129            XMLUtils.endElement(contentHandler, "search-form");
130        }
131    }
132    
133    private String _getOrgUnitCriterionInputName(ZoneItem zoneItem)
134    {
135        String searchPageId = zoneItem.getServiceParameters().getValue("search-page");
136        if (StringUtils.isNotEmpty(searchPageId))
137        {
138            Page page = _resolver.resolveById(searchPageId);
139            if (page.getType() == PageType.CONTAINER) 
140            {
141                Optional< ? extends ZoneItem> searchZoneItem = page.getZones().stream()
142                    .flatMap(z -> z.getZoneItems().stream())
143                    .filter(zi -> zi.getType() == ZoneType.SERVICE)
144                    .filter(zi -> zi.getServiceId().equals(SearchService.ROLE))
145                    .findFirst();
146                    
147                if (searchZoneItem.isPresent())
148                {
149                    SearchServiceInstance serviceInstance = _searchServiceInstanceManager.get(searchZoneItem.get().getId());
150                    return serviceInstance.getCriterionTree()
151                            .map(AbstractTreeNode::getFlatLeaves)
152                            .orElseGet(Collections::emptyList)
153                            .stream()
154                            .map(TreeLeaf::getValue)
155                            .filter(c -> StringUtils.endsWith(c.getCriterionDefinition().getName(), "$org.ametys.plugins.odf.Content.abstractProgram$orgUnit"))
156                            .findFirst()
157                            .map(c -> c.getName())
158                            .orElseGet(null);
159                    
160                }
161            }
162        }
163        
164        return null;
165    }
166    
167}