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