001/* 002 * Copyright 2022 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.calendar.search; 017 018import java.io.IOException; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.Locale; 023import java.util.Map; 024import java.util.Optional; 025import java.util.Set; 026 027import org.apache.cocoon.components.source.impl.SitemapSource; 028import org.apache.cocoon.environment.Request; 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.commons.lang3.LocaleUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.slf4j.Logger; 034import org.xml.sax.ContentHandler; 035import org.xml.sax.SAXException; 036 037import org.ametys.cms.contenttype.ContentTypesHelper; 038import org.ametys.cms.repository.Content; 039import org.ametys.cms.tag.Tag; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.core.util.DateUtils; 042import org.ametys.core.util.IgnoreRootHandler; 043import org.ametys.core.util.LambdaUtils; 044import org.ametys.plugins.repository.AmetysObject; 045import org.ametys.runtime.model.View; 046import org.ametys.runtime.model.type.DataContext; 047import org.ametys.web.WebConstants; 048import org.ametys.web.content.GetSiteAction; 049import org.ametys.web.contenttype.SkinContentViewHelper; 050import org.ametys.web.contenttype.SkinContentViewHelper.SkinContentView; 051import org.ametys.web.frontoffice.search.metamodel.ReturnableSaxer; 052import org.ametys.web.frontoffice.search.metamodel.impl.AbstractContentBasedReturnable; 053import org.ametys.web.frontoffice.search.metamodel.impl.ContentSaxer; 054import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments; 055import org.ametys.web.repository.content.WebContent; 056import org.ametys.web.repository.site.Site; 057 058/** 059 * {@link ReturnableSaxer} for {@link CalendarContentReturnable} 060 */ 061public class CalendarContentSaxer extends ContentSaxer 062{ 063 private SkinContentViewHelper _skinContentViewHelper; 064 065 /** 066 * Constructor 067 * @param contentReturnable The associated returnable on contents 068 * @param view The view for SAXing contents 069 * @param cTypesHelper the {@link ContentTypesHelper} 070 * @param skinContentViewHelper The skin content veiw helper 071 * @param contentTypes the allowed content types 072 */ 073 public CalendarContentSaxer(AbstractContentBasedReturnable contentReturnable, String view, ContentTypesHelper cTypesHelper, SkinContentViewHelper skinContentViewHelper, Collection<String> contentTypes) 074 { 075 super(contentReturnable, view, cTypesHelper, contentTypes); 076 _skinContentViewHelper = skinContentViewHelper; 077 } 078 079 @Override 080 public void sax(ContentHandler contentHandler, AmetysObject hit, Logger logger, SearchComponentArguments args) throws SAXException 081 { 082 Content content = (Content) hit; 083 084 String modelViewName = _view; 085 SkinContentView skinContentView = _skinContentViewHelper.getContentViewFromSkin(_view, content); 086 if (skinContentView != null) 087 { 088 // the requested view is a skin view, get the associated model view to check model 089 modelViewName = skinContentView.modelViewName(); 090 } 091 092 View view = _cTypesHelper.getView(modelViewName, content); 093 094 AttributesImpl attrs = new AttributesImpl(); 095 attrs.addCDATAAttribute("id", content.getId()); 096 attrs.addCDATAAttribute("name", content.getName()); 097 attrs.addCDATAAttribute("title", content.getTitle(null)); 098 attrs.addCDATAAttribute("createdAt", DateUtils.zonedDateTimeToString(content.getCreationDate())); 099 attrs.addCDATAAttribute("creator", UserIdentity.userIdentityToString(content.getCreator())); 100 attrs.addCDATAAttribute("lastModifiedAt", DateUtils.zonedDateTimeToString(content.getLastModified())); 101 102 Optional.ofNullable(content.getLanguage()).ifPresent(lang -> attrs.addCDATAAttribute("language", lang)); 103 104 XMLUtils.startElement(contentHandler, "content", attrs); 105 106 String[] contentTypes = content.getTypes(); 107 XMLUtils.startElement(contentHandler, "contentTypes"); 108 Arrays.asList(contentTypes).forEach(LambdaUtils.wrapConsumer(cType -> XMLUtils.createElement(contentHandler, "contentType", cType))); 109 XMLUtils.endElement(contentHandler, "contentTypes"); 110 111 XMLUtils.startElement(contentHandler, "attributes"); 112 Locale defaultLocale = getDefaultLocale(args.request(), args); 113 content.dataToSAX(contentHandler, view, DataContext.newInstance().withLocale(defaultLocale).withEmptyValues(false)); 114 XMLUtils.endElement(contentHandler, "attributes"); 115 116 saxHtmlView(contentHandler, content, args, logger); 117 118 saxTags(content, contentHandler); 119 120 XMLUtils.endElement(contentHandler, "content"); 121 } 122 123 /** 124 * Get the default locale 125 * @param request the request 126 * @param args the search arguments 127 * @return the default locale 128 */ 129 protected Locale getDefaultLocale(Request request, SearchComponentArguments args) 130 { 131 String lang = args.generatorParameters().getParameter("lang", request.getParameter("lang")); 132 return StringUtils.isNotEmpty(lang) ? LocaleUtils.toLocale(lang) : Locale.getDefault(); 133 } 134 135 /** 136 * SAX content html view 137 * @param contentHandler the content handler 138 * @param content the content 139 * @param args the search arguments 140 * @param logger the logger 141 * @throws SAXException if an error occurred while saxing 142 */ 143 protected void saxHtmlView(ContentHandler contentHandler, Content content, SearchComponentArguments args, Logger logger) throws SAXException 144 { 145 146 Request request = args.request(); 147 String currentSiteName = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SITE_NAME); 148 Site currentSite = (Site) request.getAttribute(WebConstants.REQUEST_ATTR_SITE); 149 String currentSkinName = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SKIN_ID); 150 151 try 152 { 153 // Content of other sites should be rendered with the current skin 154 request.setAttribute(GetSiteAction.OVERRIDE_SITE_REQUEST_ATTR, currentSiteName); 155 request.setAttribute(GetSiteAction.OVERRIDE_SKIN_REQUEST_ATTR, currentSkinName); 156 157 XMLUtils.startElement(contentHandler, "view"); 158 159 if (_contentReturnable instanceof CalendarContentReturnable) 160 { 161 162 163 String uri = _contentReturnable.getContentHelper().getContentHtmlViewUrl(content, _view); 164 165 SitemapSource src = null; 166 167 try 168 { 169 src = (SitemapSource) ((CalendarContentReturnable) _contentReturnable)._srcResolver.resolveURI(uri); 170 src.toSAX(new IgnoreRootHandler(contentHandler)); 171 } 172 finally 173 { 174 ((CalendarContentReturnable) _contentReturnable)._srcResolver.release(src); 175 } 176 } 177 178 XMLUtils.endElement(contentHandler, "view"); 179 } 180 catch (IOException e) 181 { 182 logger.error("Unable to sax HTML view for content '{}'", content.getId(), e); 183 } 184 finally 185 { 186 // Need to do this as contentFilterHelper redirects to a Cocoon URL that gets through 'GetSiteAction' which changes some request attributes 187 request.removeAttribute(GetSiteAction.OVERRIDE_SITE_REQUEST_ATTR); 188 request.removeAttribute(GetSiteAction.OVERRIDE_SKIN_REQUEST_ATTR); 189 request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, currentSiteName); 190 request.setAttribute(WebConstants.REQUEST_ATTR_SITE, currentSite); 191 request.setAttribute("siteName", currentSiteName); 192 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, currentSkinName); 193 } 194 } 195 196 /** 197 * Generates SAX events for tags. 198 * @param content the {@link WebContent}. 199 * @param contentHandler the ContentHandler receving SAX events. 200 * @throws SAXException if an error occurs during the SAX events generation. 201 */ 202 protected void saxTags(Content content, ContentHandler contentHandler) throws SAXException 203 { 204 if (_contentReturnable instanceof CalendarContentReturnable) 205 { 206 XMLUtils.startElement(contentHandler, "tags"); 207 208 Set<String> tags = content.getTags(); 209 for (String tagName : tags) 210 { 211 Map<String, Object> contextParameters = new HashMap<>(); 212 if (content instanceof WebContent) 213 { 214 contextParameters.put("siteName", ((WebContent) content).getSiteName()); 215 } 216 217 Tag tag = ((CalendarContentReturnable) _contentReturnable).getTagProviderExtensionPoint().getTag(tagName, contextParameters); 218 219 if (tag != null) 220 { 221 AttributesImpl attrs = new AttributesImpl(); 222 if (tag.getParentName() != null) 223 { 224 attrs.addCDATAAttribute("parent", tag.getParentName()); 225 } 226 227 XMLUtils.startElement(contentHandler, tagName, attrs); 228 tag.getTitle().toSAX(contentHandler); 229 XMLUtils.endElement(contentHandler, tagName); 230 } 231 } 232 233 XMLUtils.endElement(contentHandler, "tags"); 234 } 235 236 } 237}