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