001/*
002 *  Copyright 2019 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.service.search;
017
018import java.util.List;
019import java.util.Locale;
020import java.util.Optional;
021
022import org.apache.cocoon.components.ContextHelper;
023import org.apache.cocoon.environment.Request;
024import org.apache.cocoon.xml.AttributesImpl;
025import org.apache.cocoon.xml.XMLUtils;
026import org.apache.commons.lang3.StringUtils;
027import org.slf4j.Logger;
028import org.xml.sax.ContentHandler;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.contenttype.ContentType;
032import org.ametys.odf.program.Program;
033import org.ametys.odf.program.ProgramPart;
034import org.ametys.odf.program.SubProgram;
035import org.ametys.odf.program.SubProgramFactory;
036import org.ametys.plugins.odfweb.repository.ProgramPage;
037import org.ametys.plugins.odfweb.service.search.ProgramReturnable.DisplaySubprogramMode;
038import org.ametys.plugins.repository.AmetysObject;
039import org.ametys.runtime.model.View;
040import org.ametys.web.frontoffice.search.metamodel.ReturnableSaxer;
041import org.ametys.web.frontoffice.search.metamodel.impl.PageReturnable;
042import org.ametys.web.frontoffice.search.metamodel.impl.PageSaxer;
043import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments;
044import org.ametys.web.repository.page.Page;
045import org.ametys.web.repository.site.Site;
046
047/**
048 * {@link ReturnableSaxer} for {@link ProgramReturnable}
049 */
050public class ProgramSaxer extends PageSaxer
051{
052    private ProgramReturnable _programReturnable;
053    private DisplaySubprogramMode _displaySubprogramMode;
054
055    /**
056     * Constructor
057     * @param pageReturnable The page returnable (needed for superclass)
058     * @param programReturnable The associated returnable on programs
059     * @param displaySubprogramMode The mode for displaying (or not) subprograms
060     */
061    public ProgramSaxer(PageReturnable pageReturnable, ProgramReturnable programReturnable, DisplaySubprogramMode displaySubprogramMode)
062    {
063        super(pageReturnable);
064        _programReturnable = programReturnable;
065        _displaySubprogramMode = displaySubprogramMode;
066    }
067    
068    @Override
069    public boolean canSax(AmetysObject hit, Logger logger, SearchComponentArguments args)
070    {
071        return hit instanceof Program;
072    }
073    
074    @Override
075    public void sax(ContentHandler contentHandler, AmetysObject hit, Logger logger, SearchComponentArguments args) throws SAXException
076    {
077        Request request = ContextHelper.getRequest(_programReturnable._avalonContext);
078        Program program = (Program) hit;
079        Optional<ProgramPage> programPage = _resolveProgramPage(program, args.currentSite());
080        if (programPage.isPresent())
081        {
082            _saxProgramPage(contentHandler, programPage.get(), logger, args);
083        }
084        else
085        {
086            logger.warn("The program {} was returned from the search but its ProgramPage could not be resolved", program);
087            // sax minimal data for the program
088            String sitemapName = args.currentPage().getSitemapName();
089            _saxProgramWithNoPage(contentHandler, program, sitemapName, logger);
090        }
091        saxSubPrograms(contentHandler, program, programPage, logger, request);
092    }
093    
094    private Optional<ProgramPage> _resolveProgramPage(Program program, Site currentSite)
095    {
096        return Optional.of(_programReturnable._getOdfPageResolver())
097                .map(res -> res.getProgramPage(program, currentSite.getName()));
098    }
099    
100    private Optional<Page> _resolveSubProgramPage(Program program, Optional<ProgramPage> programPage, SubProgram subProgram)
101    {
102        return programPage
103                .map(ProgramPage::getSiteName)
104                .map(siteName -> _programReturnable._getOdfPageResolver().getSubProgramPage(subProgram, program, siteName));
105    }
106    
107    private String _getSubProgramPagePath(Page subProgramPage, String programPath)
108    {
109        return StringUtils.substringAfterLast(subProgramPage.getPathInSitemap(), programPath);
110    }
111    
112    private void _saxProgramWithNoPage(ContentHandler contentHandler, Program program, String sitemapName, Logger logger) throws SAXException
113    {
114        Locale locale = new Locale(sitemapName);
115        XMLUtils.createElement(contentHandler, "title", program.getTitle());
116        saxContent(program, "index", locale, contentHandler, logger);
117    }
118    
119    private void _saxProgramPage(ContentHandler contentHandler, ProgramPage programPage, Logger logger, SearchComponentArguments args) throws SAXException
120    {
121        super.sax(contentHandler, programPage, logger, args);
122    }
123    
124    /**
125     * SAX the subprograms of the program
126     * @param contentHandler The content handler
127     * @param program The program
128     * @param programPage The program page
129     * @param logger A logger
130     * @param request The request
131     * @throws SAXException if a SAX error occured
132     */
133    protected void saxSubPrograms(ContentHandler contentHandler, Program program, Optional<ProgramPage> programPage, Logger logger, Request request) throws SAXException
134    {
135        if (_displaySubprogramMode == DisplaySubprogramMode.NONE)
136        {
137            return;
138        }
139
140        List<String> matchingSubProgramIds = MatchingSubprogramSearchComponent._getMatchingSubProgramIds(request);
141        
142        ContentType subProgramCType = _programReturnable._getContentTypeEP().getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE);
143        View view = subProgramCType.getView("index");
144        Optional<String> programPath = programPage
145                .map(ProgramPage::getPathInSitemap);
146        
147        for (ProgramPart childProgramPart : program.getProgramPartChildren())
148        {
149            if (childProgramPart instanceof SubProgram)
150            {
151                SubProgram subProgram = (SubProgram) childProgramPart;
152                boolean matchSearch = matchingSubProgramIds.contains(subProgram.getId());
153                saxSubProgram(contentHandler, subProgram, program, programPage, matchSearch, programPath, logger, view);
154            }
155        }
156    }
157    
158    /**
159     * SAX the subprogram
160     * @param contentHandler The content handler
161     * @param subProgram The subProgram
162     * @param program The program
163     * @param programPage The program page
164     * @param matchSearch <code>true</code> if the subprogram matches the search
165     * @param programPath The program path
166     * @param logger A logger
167     * @param view The view
168     * @throws SAXException if a SAX error occured
169     */
170    protected void saxSubProgram(
171            ContentHandler contentHandler, 
172            SubProgram subProgram, 
173            Program program, 
174            Optional<ProgramPage> programPage, 
175            boolean matchSearch, 
176            Optional<String> programPath, 
177            Logger logger, 
178            View view) throws SAXException
179    {
180        if (_displaySubprogramMode != DisplaySubprogramMode.MATCHING_SEARCH_ONLY || matchSearch)
181        {
182            AttributesImpl attrs = new AttributesImpl();
183            Optional<Page> subProgramPage = _resolveSubProgramPage(program, programPage, subProgram);
184            if (subProgramPage.isPresent() && programPath.isPresent())
185            {
186                attrs.addCDATAAttribute("path", _getSubProgramPagePath(subProgramPage.get(), programPath.get()));
187            }
188            attrs.addCDATAAttribute("id", subProgram.getId());
189            attrs.addCDATAAttribute("name", subProgram.getName());
190            attrs.addCDATAAttribute("title", subProgram.getTitle());
191            if (_displaySubprogramMode == DisplaySubprogramMode.ALL_WITH_HIGHLIGHT)
192            {
193                attrs.addCDATAAttribute("highlight", String.valueOf(matchSearch));
194            }
195            XMLUtils.startElement(contentHandler, "subprogram", attrs);
196
197            try
198            {
199                subProgram.dataToSAX(contentHandler, view);
200            }
201            catch (Exception e)
202            {
203                logger.error("An error occurred during saxing subprogram '" + subProgram.getId() + "' metadata", e);
204            }
205            
206            XMLUtils.endElement(contentHandler, "subprogram");
207        }
208    }
209}