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 try 079 { 080 Program program = (Program) hit; 081 Optional<ProgramPage> programPage = _resolveProgramPage(program, args.currentSite()); 082 if (programPage.isPresent()) 083 { 084 _saxProgramPage(contentHandler, programPage.get(), logger, args); 085 } 086 else 087 { 088 logger.warn("The program {} was returned from the search but its ProgramPage could not be resolved", program); 089 // sax minimal data for the program 090 String sitemapName = args.currentPage().getSitemapName(); 091 _saxProgramWithNoPage(contentHandler, program, sitemapName, logger); 092 } 093 saxSubPrograms(contentHandler, program, programPage, logger, request); 094 } 095 finally 096 { 097 MatchingSubprogramSearchComponent._removeMatchingSubProgramIdsRequestAttribute(request); 098 } 099 } 100 101 private Optional<ProgramPage> _resolveProgramPage(Program program, Site currentSite) 102 { 103 return Optional.of(_programReturnable._getOdfPageResolver()) 104 .map(res -> res.getProgramPage(program, currentSite.getName())); 105 } 106 107 private Optional<Page> _resolveSubProgramPage(Program program, Optional<ProgramPage> programPage, SubProgram subProgram) 108 { 109 return programPage 110 .map(ProgramPage::getSiteName) 111 .map(siteName -> _programReturnable._getOdfPageResolver().getSubProgramPage(subProgram, program, siteName)); 112 } 113 114 private String _getSubProgramPagePath(Page subProgramPage, String programPath) 115 { 116 return StringUtils.substringAfterLast(subProgramPage.getPathInSitemap(), programPath); 117 } 118 119 private void _saxProgramWithNoPage(ContentHandler contentHandler, Program program, String sitemapName, Logger logger) throws SAXException 120 { 121 Locale locale = new Locale(sitemapName); 122 XMLUtils.createElement(contentHandler, "title", program.getTitle()); 123 saxContent(program, "index", locale, contentHandler, logger); 124 } 125 126 private void _saxProgramPage(ContentHandler contentHandler, ProgramPage programPage, Logger logger, SearchComponentArguments args) throws SAXException 127 { 128 super.sax(contentHandler, programPage, logger, args); 129 } 130 131 /** 132 * SAX the subprograms of the program 133 * @param contentHandler The content handler 134 * @param program The program 135 * @param programPage The program page 136 * @param logger A logger 137 * @param request The request 138 * @throws SAXException if a SAX error occured 139 */ 140 protected void saxSubPrograms(ContentHandler contentHandler, Program program, Optional<ProgramPage> programPage, Logger logger, Request request) throws SAXException 141 { 142 if (_displaySubprogramMode == DisplaySubprogramMode.NONE) 143 { 144 return; 145 } 146 147 List<String> matchingSubProgramIds = MatchingSubprogramSearchComponent._getMatchingSubProgramIds(request); 148 149 ContentType subProgramCType = _programReturnable._getContentTypeEP().getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE); 150 View view = subProgramCType.getView("index"); 151 Optional<String> programPath = programPage 152 .map(ProgramPage::getPathInSitemap); 153 154 for (ProgramPart childProgramPart : program.getProgramPartChildren()) 155 { 156 if (childProgramPart instanceof SubProgram) 157 { 158 SubProgram subProgram = (SubProgram) childProgramPart; 159 boolean matchSearch = matchingSubProgramIds.contains(subProgram.getId()); 160 saxSubProgram(contentHandler, subProgram, program, programPage, matchSearch, programPath, logger, view); 161 } 162 } 163 } 164 165 /** 166 * SAX the subprogram 167 * @param contentHandler The content handler 168 * @param subProgram The subProgram 169 * @param program The program 170 * @param programPage The program page 171 * @param matchSearch <code>true</code> if the subprogram matches the search 172 * @param programPath The program path 173 * @param logger A logger 174 * @param view The view 175 * @throws SAXException if a SAX error occured 176 */ 177 protected void saxSubProgram( 178 ContentHandler contentHandler, 179 SubProgram subProgram, 180 Program program, 181 Optional<ProgramPage> programPage, 182 boolean matchSearch, 183 Optional<String> programPath, 184 Logger logger, 185 View view) throws SAXException 186 { 187 if (_displaySubprogramMode != DisplaySubprogramMode.MATCHING_SEARCH_ONLY || matchSearch) 188 { 189 AttributesImpl attrs = new AttributesImpl(); 190 Optional<Page> subProgramPage = _resolveSubProgramPage(program, programPage, subProgram); 191 if (subProgramPage.isPresent() && programPath.isPresent()) 192 { 193 attrs.addCDATAAttribute("path", _getSubProgramPagePath(subProgramPage.get(), programPath.get())); 194 } 195 attrs.addCDATAAttribute("id", subProgram.getId()); 196 attrs.addCDATAAttribute("name", subProgram.getName()); 197 attrs.addCDATAAttribute("title", subProgram.getTitle()); 198 if (_displaySubprogramMode == DisplaySubprogramMode.ALL_WITH_HIGHLIGHT) 199 { 200 attrs.addCDATAAttribute("highlight", String.valueOf(matchSearch)); 201 } 202 XMLUtils.startElement(contentHandler, "subprogram", attrs); 203 204 try 205 { 206 subProgram.dataToSAX(contentHandler, view); 207 } 208 catch (Exception e) 209 { 210 logger.error("An error occurred during saxing subprogram '" + subProgram.getId() + "' metadata", e); 211 } 212 213 XMLUtils.endElement(contentHandler, "subprogram"); 214 } 215 } 216}