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}