001/*
002 *  Copyright 2010 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.odfweb.program;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Optional;
027import java.util.stream.Collectors;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.ProcessingException;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Request;
034import org.apache.cocoon.generation.ServiceableGenerator;
035import org.apache.cocoon.xml.AttributesImpl;
036import org.apache.cocoon.xml.XMLUtils;
037import org.xml.sax.SAXException;
038
039import org.ametys.cms.data.ContentValue;
040import org.ametys.odf.orgunit.OrgUnit;
041import org.ametys.odf.program.Program;
042import org.ametys.odf.program.ProgramFactory;
043import org.ametys.odf.program.ProgramPart;
044import org.ametys.odf.program.SubProgram;
045import org.ametys.plugins.odfweb.repository.OdfPageHandler;
046import org.ametys.plugins.repository.AmetysObjectIterable;
047import org.ametys.plugins.repository.AmetysObjectResolver;
048import org.ametys.plugins.repository.UnknownAmetysObjectException;
049import org.ametys.runtime.model.ModelItem;
050import org.ametys.web.repository.page.Page;
051import org.ametys.web.repository.page.ZoneItem;
052
053/**
054 * Generates the exhaustive list of programs.
055 */
056public class ProgramListGenerator extends ServiceableGenerator
057{
058    /** The ametys object resolver. */
059    protected AmetysObjectResolver _ametysResolver;
060    
061    /** Handler for root */
062    protected OdfPageHandler _odfPageHandler;
063
064    @Override
065    public void service(ServiceManager serviceManager) throws ServiceException
066    {
067        super.service(serviceManager);
068        _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
069        _odfPageHandler = (OdfPageHandler) serviceManager.lookup(OdfPageHandler.ROLE);
070    }
071    
072    @Override
073    public void generate() throws IOException, SAXException, ProcessingException
074    {
075        Request request = ObjectModelHelper.getRequest(objectModel);
076        
077        String site = parameters.getParameter("site", (String) request.getAttribute("site"));
078        String lang = parameters.getParameter("lang", (String) request.getAttribute("sitemapLanguage"));
079        String catalog = parameters.getParameter("catalog", (String) request.getAttribute("catalog"));
080        
081        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
082        String[] filteredByOrgUnits = zoneItem.getServiceParameters().getValue("orgunitlist", false, new String[0]);
083
084        Page rootPage = _odfPageHandler.getOdfRootPage(site, lang, catalog);
085        if (rootPage == null)
086        {
087            throw new IllegalArgumentException("There is no ODF root page for catalog '" + catalog + "' and language '" + lang + "'.");
088        }
089        
090        AmetysObjectIterable<Program> programs = _odfPageHandler.getProgramsWithRestrictions(rootPage, null, null, null, null);
091        
092        contentHandler.startDocument();
093        
094        AttributesImpl atts = new AttributesImpl();
095        atts.addCDATAAttribute("root-page-path", rootPage.getPathInSitemap());
096        
097        XMLUtils.startElement(contentHandler, "programs", atts);
098        
099        HashMap<String, List<String>> metadataFilters = new HashMap<>(); 
100       
101        if (filteredByOrgUnits.length > 0)
102        {
103            List<String> filteredByOrgUnitsAsList = Arrays.asList(filteredByOrgUnits);
104            metadataFilters.put("orgUnit", filteredByOrgUnitsAsList);
105            
106            for (Program program : programs)
107            {
108                // Check at least one program's org units belongs to the filtered list 
109                List<String> orgUnits = program.getOrgUnits();
110                
111                boolean found = false;
112                for (String ouId : orgUnits)
113                {
114                    try
115                    {
116                        OrgUnit ou = _ametysResolver.resolveById(ouId);
117                        if (_isEqualOrHasParentInList(ou, filteredByOrgUnitsAsList))
118                        {
119                            found = true;
120                            break;
121                        }
122                    }
123                    catch (UnknownAmetysObjectException e)
124                    {
125                        getLogger().error("The OrgUnit with id '" + ouId + "' does not exist anymore. It will be ignored.", e);
126                    }
127                    
128                }
129                
130                if (found)
131                {
132                    saxProgram(program, metadataFilters);
133                }
134            }
135        }
136        else
137        {
138            for (Program program : programs)
139            {
140                saxProgram(program, metadataFilters);
141            }            
142        }
143        XMLUtils.endElement(contentHandler, "programs");
144        
145        contentHandler.endDocument();
146    }
147    
148    private boolean _isEqualOrHasParentInList (OrgUnit ou, List<String> filteredOrgUnits)
149    {
150        if (filteredOrgUnits.contains(ou.getId()))
151        {
152            return true;
153        }
154        
155        // Check parents
156        OrgUnit parentOrgUnit = ou.getParentOrgUnit();
157        while (parentOrgUnit != null)
158        {
159            if (filteredOrgUnits.contains(parentOrgUnit.getId()))
160            {
161                return true;
162            }
163            parentOrgUnit = parentOrgUnit.getParentOrgUnit();
164        }
165        
166        return false;
167    }
168    
169    /**
170     * SAX a program.
171     * @param program The program to SAX.
172     * @param attributeFilters Filters to apply to the attributes
173     * @throws SAXException If an error occurs while SAXing
174     * @throws IOException If an error occurs while retrieving content.
175     */
176    public void saxProgram(Program program, HashMap<String, List<String>> attributeFilters) throws SAXException, IOException
177    {
178        Map<String, ModelItem> enumeratedAttributes = _odfPageHandler.getEnumeratedAttributes(ProgramFactory.PROGRAM_CONTENT_TYPE, true);
179        
180        AttributesImpl atts = new AttributesImpl();
181        atts.addCDATAAttribute("id", program.getId());
182        atts.addCDATAAttribute("name", program.getName());
183        atts.addCDATAAttribute("title", program.getTitle());
184        
185        XMLUtils.startElement(contentHandler, "program", atts);
186        
187        for (String attributePath : enumeratedAttributes.keySet())
188        {
189            List<String> values = Optional.ofNullable(program.getValue(attributePath))
190                                          .map(this::_valuesAsList)
191                                          .orElse(Collections.emptyList())
192                                          .stream()
193                                          .filter(value -> value instanceof String || value instanceof ContentValue)
194                                          .map(value -> value instanceof ContentValue ? ((ContentValue) value).getContentId() : (String) value)
195                                          .collect(Collectors.toList());
196                    
197            AttributesImpl attrs = new AttributesImpl();
198            attrs.addCDATAAttribute("path", attributePath);
199            
200            XMLUtils.startElement(contentHandler, "metadata", attrs);
201            for (String value : values)
202            {
203                if (_filterAttributes(attributePath, value, attributeFilters))
204                {
205                    XMLUtils.createElement(contentHandler, "value", value);
206                }
207            }
208            XMLUtils.endElement(contentHandler, "metadata");
209        }
210        
211        for (ProgramPart programPart : program.getProgramPartChildren())
212        {
213            if (programPart instanceof SubProgram)
214            {
215                saxSubProgram((SubProgram) programPart);
216            }
217        }
218        
219        XMLUtils.endElement(contentHandler, "program");
220    }
221    
222    private List<Object> _valuesAsList(Object values)
223    {
224        if (values instanceof Collection<?>)
225        {
226            return new ArrayList<>((Collection<?>) values); 
227        }
228        else if (values instanceof Object[])
229        {
230            return Arrays.asList((Object[]) values);
231        }
232        else
233        {
234            return Arrays.asList(values);
235        }
236    }
237    
238    private boolean _filterAttributes(String attributePath, String value, HashMap<String, List<String>> attributeFilters)
239    {
240        if (attributePath.equals("orgUnit") && attributeFilters.containsKey("orgUnit"))
241        {
242            try
243            {
244                OrgUnit ou = _ametysResolver.resolveById(value);
245                return _isEqualOrHasParentInList(ou, attributeFilters.get("orgUnit"));
246            }
247            catch (UnknownAmetysObjectException e)
248            {
249                getLogger().error("The OrgUnit with id '" + value + "' does not exist anymore. It will be ignored.", e);
250                return false;
251            }
252            
253        }
254        return true;
255    }
256    
257    /**
258     * SAX a subprogram.
259     * @param subProgram The subprogram to SAX.
260     * @throws SAXException If an error occurs while SAXing
261     */
262    protected void saxSubProgram (SubProgram subProgram) throws SAXException
263    {
264        AttributesImpl atts = new AttributesImpl();
265        
266        atts.addCDATAAttribute("id", subProgram.getId());
267        atts.addCDATAAttribute("name", subProgram.getName());
268        atts.addCDATAAttribute("title", subProgram.getTitle());
269        
270        XMLUtils.createElement(contentHandler, "subprogram", atts);
271    }
272    
273    /**
274     * SAX a value.
275     * @param name the criterion name.
276     * @param value the criterion value.
277     * @throws SAXException SAX error
278     */
279    protected void saxCriterionValue(String name, String value) throws SAXException
280    {
281        AttributesImpl attrs = new AttributesImpl();
282        attrs.addCDATAAttribute("name", name);
283        attrs.addCDATAAttribute("value", value);
284        XMLUtils.createElement(contentHandler, "criterion", attrs);
285    }
286}