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}