001/*
002 *  Copyright 2020 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.workspaces.search.module;
017
018import java.util.Collection;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.Locale;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.xml.AttributesImpl;
029import org.apache.cocoon.xml.XMLUtils;
030import org.xml.sax.SAXException;
031
032import org.ametys.cms.contenttype.ContentTypesHelper;
033import org.ametys.cms.repository.Content;
034import org.ametys.cms.search.SearchResults;
035import org.ametys.cms.search.Sort.Order;
036import org.ametys.cms.search.content.ContentSearcherFactory;
037import org.ametys.cms.search.content.ContentSearcherFactory.SimpleContentSearcher;
038import org.ametys.cms.search.query.Query;
039import org.ametys.cms.tag.Tag;
040import org.ametys.cms.tag.TagProviderExtensionPoint;
041import org.ametys.core.user.UserIdentity;
042import org.ametys.core.util.DateUtils;
043import org.ametys.plugins.repository.AmetysObject;
044import org.ametys.runtime.model.View;
045import org.ametys.runtime.model.type.DataContext;
046import org.ametys.web.repository.content.WebContent;
047import org.ametys.web.repository.page.Page;
048
049/**
050 * Abstract generator for search module of type content
051 *
052 */
053public abstract class AbstractContentSolrSearchModuleGenerator extends AbstractSolrSearchModuleGenerator
054{
055    /** The content searcher factory */
056    protected ContentSearcherFactory _contentSearcherFactory;
057    /** The content types helper */
058    protected ContentTypesHelper _cTypesHelper;
059    /** The tag provider */
060    protected TagProviderExtensionPoint _tagProviderEP;
061    
062    @Override
063    public void service(ServiceManager smanager) throws ServiceException
064    {
065        super.service(smanager);
066        _contentSearcherFactory = (ContentSearcherFactory) manager.lookup(ContentSearcherFactory.ROLE);
067        _cTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
068        _tagProviderEP = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE);
069    }
070    
071    @Override
072    protected void saxHit(AmetysObject object, String lang) throws Exception
073    {
074        if (object instanceof Content)
075        {
076            Content content = (Content) object;
077            View view = _cTypesHelper.getView("search", content.getTypes(), content.getMixinTypes());
078            if (view == null)
079            {
080                // fallback to main view
081                view = _cTypesHelper.getView("main", content.getTypes(), content.getMixinTypes());
082            }
083            
084            AttributesImpl attrs = new AttributesImpl();
085            attrs.addCDATAAttribute("id", content.getId());
086            attrs.addCDATAAttribute("title", content.getTitle());
087            
088            attrs.addCDATAAttribute("createdAt", DateUtils.dateToString(content.getCreationDate()));
089            attrs.addCDATAAttribute("creator", UserIdentity.userIdentityToString(content.getCreator()));
090            attrs.addCDATAAttribute("lastModifiedAt", DateUtils.dateToString(content.getLastModified()));
091            Date lastValidatedAt = content.getLastValidationDate();
092            if (lastValidatedAt != null)
093            {
094                attrs.addCDATAAttribute("lastValidatedAt", DateUtils.dateToString(lastValidatedAt));
095            }
096            
097            attrs.addCDATAAttribute("lastContributor", UserIdentity.userIdentityToString(content.getLastContributor()));
098            
099            XMLUtils.startElement(contentHandler, "hit", attrs);
100            
101            XMLUtils.startElement(contentHandler, "attributes");
102            content.dataToSAX(contentHandler, view, DataContext.newInstance().withLocale(new Locale(lang)).withEmptyValues(false));
103            XMLUtils.endElement(contentHandler, "attributes");
104            
105            saxAdditionalInformation(content);
106            
107            XMLUtils.endElement(contentHandler, "hit");
108        }
109        else
110        {
111            getLogger().error("Object '" + object.getId() + "' is not a Content.");
112        }
113    }
114    
115    /**
116     * SAX additional information on content
117     * @param content the content
118     * @throws SAXException if an error occurs while saxing
119     */
120    protected void saxAdditionalInformation(Content content) throws SAXException
121    {
122        saxPage(content);
123        saxTags(content);
124    }
125    
126    /**
127     * SAX page content
128     * @param content the content
129     * @throws SAXException  if an error occured while saxing
130     */
131    protected void saxPage(Content content) throws SAXException
132    {
133        if (content instanceof WebContent)
134        {
135            Collection<Page> referencingPages = ((WebContent) content).getReferencingPages();
136            if (!referencingPages.isEmpty())
137            {
138                XMLUtils.createElement(contentHandler, "page", referencingPages.iterator().next().getId());
139            }
140        }
141    }
142    
143    /**
144     * SAX content's tags
145     * @param content the content
146     * @throws SAXException  if an error occured while saxing
147     */
148    protected void saxTags(Content content) throws SAXException
149    {
150        Set<String> tags = content.getTags();
151        if (!tags.isEmpty())
152        {
153            XMLUtils.startElement(contentHandler, "tags");
154            
155            for (String tagName : tags)
156            {
157                Map<String, Object> contextParameters = new HashMap<>();
158                if (content instanceof WebContent)
159                {
160                    contextParameters.put("siteName", ((WebContent) content).getSiteName());
161                }
162                Tag tag = _tagProviderEP.getTag(tagName, contextParameters);
163
164                if (tag != null)
165                {
166                    AttributesImpl attrs = new AttributesImpl();
167                    if (tag.getParentName() != null)
168                    {
169                        attrs.addCDATAAttribute("parent", tag.getParentName());
170                    }
171                    
172                    XMLUtils.startElement(contentHandler, tagName, attrs);
173                    tag.getTitle().toSAX(contentHandler);
174                    XMLUtils.endElement(contentHandler, tagName);
175                }
176            }
177            
178            XMLUtils.endElement(contentHandler, "tags");
179        }
180    }
181    
182    @Override
183    protected SearchResults<Content> getSearchResults(String siteName, String lang, String textfield, Request request, int offset, int limit) throws Exception
184    {
185        SimpleContentSearcher searcher = getSearcher();
186        
187        Query query = getQuery(siteName, lang, textfield, request);
188        
189        return searcher
190                .withLimits(offset, limit)
191                .addSort(getSortFieldName(), getSortOrder())
192                .searchWithFacets(query);
193    }
194    
195    /**
196     * Get the searcher
197     * @return the searcher
198     */
199    protected abstract SimpleContentSearcher getSearcher();
200
201    /**
202     * The sort field name
203     * @return the sort field name
204     */
205    protected abstract String getSortFieldName();
206    
207    /**
208     * The sort order
209     * @return the sort order
210     */
211    protected abstract Order getSortOrder();
212
213    /**
214     * Get the search query
215     * @param siteName the current site name
216     * @param lang the current language
217     * @param textfield the search input
218     * @param request the request
219     * @return the search query
220     */
221    protected abstract Query getQuery(String siteName, String lang, String textfield, Request request);
222}