001/*
002 *  Copyright 2018 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.ugc.page;
017
018import java.util.HashMap;
019import java.util.Locale;
020import java.util.Map;
021import java.util.Set;
022import java.util.stream.Collectors;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028import org.apache.commons.lang.StringUtils;
029
030import org.ametys.cms.contenttype.ContentType;
031import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
032import org.ametys.cms.contenttype.MetadataDefinition;
033import org.ametys.cms.contenttype.MetadataType;
034import org.ametys.cms.repository.Content;
035import org.ametys.cms.repository.ContentTypeExpression;
036import org.ametys.cms.repository.LanguageExpression;
037import org.ametys.core.util.I18nUtils;
038import org.ametys.plugins.repository.AmetysObjectIterable;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.plugins.repository.query.QueryHelper;
042import org.ametys.plugins.repository.query.SortCriteria;
043import org.ametys.plugins.repository.query.expression.AndExpression;
044import org.ametys.plugins.repository.query.expression.Expression;
045import org.ametys.plugins.repository.query.expression.Expression.Operator;
046import org.ametys.plugins.repository.query.expression.StringExpression;
047import org.ametys.plugins.repository.query.expression.VirtualFactoryExpression;
048import org.ametys.runtime.i18n.I18nizableText;
049import org.ametys.runtime.parameter.Enumerator;
050import org.ametys.runtime.plugin.component.AbstractLogEnabled;
051import org.ametys.web.repository.page.Page;
052import org.ametys.web.repository.page.PageQueryHelper;
053
054/**
055 * Component providing methods to retrieve ugc virtual pages, such as the ugc root,
056 * transitional page and ugc content page.
057 */
058public class UGCPageHandler extends AbstractLogEnabled implements Component, Serviceable
059{
060    /** The attribute to get the name of transitional page */
061    public static final String ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE = "metadata_value";
062    
063    /** The attribute to get the title of transitional page */
064    public static final String ATTRIBUTE_TRANSITIONAL_PAGE_TITLE = "title";
065    
066    /** The avalon role. */
067    public static final String ROLE = UGCPageHandler.class.getName();
068    
069    /** The metadata name for the content type of the ugc */
070    public static final String CONTENT_TYPE_METADATA_NAME = "ugc-root-contenttype";
071    
072    /** The metadata name for the classification metadata of the ugc */
073    public static final String CLASSIFICATION_METADATA_METADATA_NAME = "ugc-root-classification-metadata";
074    
075    /** The ametys object resolver */
076    protected AmetysObjectResolver _resolver;
077    
078    /** The content type extension point */
079    protected ContentTypeExtensionPoint _cTypeEP;
080    
081    /** The i18n utils */
082    protected I18nUtils _i18nUtils;
083    
084    @Override
085    public void service(ServiceManager manager) throws ServiceException
086    {
087        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
088        _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
089        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
090    }
091    
092    /**
093     * Gets the name of the classification metadata
094     * @param rootPage The ugc root page
095     * @return the name of the classification metadata
096     */
097    public String getClassificationMetadata(Page rootPage)
098    {
099        return rootPage.getValue(CLASSIFICATION_METADATA_METADATA_NAME);
100    }
101    
102    /**
103     * Gets the content type id
104     * @param rootPage The ugc root page
105     * @return the content type id
106     */
107    public String getContentTypeId(Page rootPage)
108    {
109        return rootPage.getValue(CONTENT_TYPE_METADATA_NAME);
110    }
111    
112    /**
113     * Gets the ugc root pages from the given content type id.
114     * @param siteName the site name
115     * @param sitemapName the sitemap name
116     * @param contentTypeId The content type id
117     * @return the ugc root page.
118     * @throws AmetysRepositoryException  if an error occured.
119     */
120    public Page getUGCRootPage(String siteName, String sitemapName, String contentTypeId) throws AmetysRepositoryException
121    {
122        Expression expression = new VirtualFactoryExpression(VirtualUGCPageFactory.class.getName());
123        Expression contentTypeExp = new StringExpression(CONTENT_TYPE_METADATA_NAME, Operator.EQ, contentTypeId);
124        
125        AndExpression andExp = new AndExpression(expression, contentTypeExp);
126        
127        String query = PageQueryHelper.getPageXPathQuery(siteName, sitemapName, null, andExp, null);
128        
129        AmetysObjectIterable<Page> pages = _resolver.query(query);
130        
131        return pages.iterator().hasNext() ? pages.iterator().next() : null;
132    }
133    
134    /**
135     * Get the ugc root pages
136     * @param siteName the current site.
137     * @param sitemapName the sitemap name.
138     * @return the ugc root pages
139     * @throws AmetysRepositoryException if an error occured.
140     */
141    public Set<Page> getUGCRootPages(String siteName, String sitemapName) throws AmetysRepositoryException
142    {
143        Expression expression = new VirtualFactoryExpression(VirtualUGCPageFactory.class.getName());
144        
145        String query = PageQueryHelper.getPageXPathQuery(siteName, sitemapName, null, expression, null);
146        
147        AmetysObjectIterable<Page> pages = _resolver.query(query);
148        
149        return pages.stream().collect(Collectors.toSet());
150    }
151    
152    /**
153     * Get orgUnit contents from rootPage
154     * @param rootPage the root page
155     * @return the list of orgUnit contents
156     */
157    public AmetysObjectIterable<Content> getContentsForRootPage(Page rootPage)
158    {
159        String lang = rootPage.getSitemapName();
160        String contentType = getContentTypeId(rootPage);
161        
162        ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentType);
163        
164        Expression finalExpr = new AndExpression(contentTypeExp, new LanguageExpression(Operator.EQ, lang));
165        
166        SortCriteria sort = new SortCriteria();
167        sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true);
168        
169        String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort);
170        
171        return _resolver.query(xPathQuery);
172    }
173    
174    /**
175     * Get the map of transitional page (name : (id, title))
176     * @param rootPage the root page
177     * @return The map of transitional page
178     */
179    public Map<String, Map<String, String>> getTransitionalPage(Page rootPage)
180    {
181        Map<String, Map<String, String>> transitionalPage = new HashMap<>();
182        String classificationMetadataPath = getClassificationMetadata(rootPage);
183        if (StringUtils.isBlank(classificationMetadataPath))
184        {
185            // No classification metadata defined, so no transitional page
186            return transitionalPage;
187        }
188        
189        String contentTypeId = getContentTypeId(rootPage);
190        ContentType contentType = _cTypeEP.getExtension(contentTypeId);
191        
192        MetadataDefinition metadataDefinition = contentType.getMetadataDefinitionByPath(classificationMetadataPath);
193        if (metadataDefinition != null)
194        {
195            if (metadataDefinition.getType().equals(MetadataType.CONTENT))
196            {
197                String metadataContentType = metadataDefinition.getContentType();
198                AmetysObjectIterable<Content> allContents = _getAllContents(rootPage, metadataContentType);
199                for (Content content : allContents)
200                {
201                    Map<String, String> attributes = new HashMap<>();
202                    attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE, content.getId());
203                    attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_TITLE, content.getTitle(new Locale(rootPage.getSitemapName())));
204                    
205                    transitionalPage.put(content.getName(), attributes);
206                }
207            }
208            else if (metadataDefinition.getEnumerator() != null)
209            {
210                Enumerator enumerator = metadataDefinition.getEnumerator();
211                try
212                {
213                    Map<Object, I18nizableText> entries = enumerator.getEntries();
214                    for (Object key : entries.keySet())
215                    {
216                        I18nizableText text = entries.get(key);
217    
218                        Map<String, String> attributes = new HashMap<>();
219                        attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE, (String) key);
220                        attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_TITLE, _i18nUtils.translate(text));
221                        
222                        transitionalPage.put((String) key, attributes);
223                    }
224                }
225                catch (Exception e)
226                {
227                    getLogger().error("An error occurred. Can't get enumerator entries for metadata path " + classificationMetadataPath, e);
228                }
229            }
230        }
231        
232        return transitionalPage;
233    }
234    
235    /**
236     * Get contents under transitional page
237     * @param rootPage the root page
238     * @param metadataValue the metadata value (linked to the transitional page)
239     * @return list of contents under transitional page
240     */
241    public AmetysObjectIterable<Content> getContentsForTransitionalPage(Page rootPage, String metadataValue)
242    {
243        String classificationMetadata = getClassificationMetadata(rootPage);
244        
245        String lang = rootPage.getSitemapName();
246        String contentType = getContentTypeId(rootPage);
247        
248        ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentType);
249        StringExpression metadataExpression = new StringExpression(classificationMetadata, Operator.EQ, metadataValue);
250        
251        Expression finalExpr = new AndExpression(contentTypeExp, metadataExpression, new LanguageExpression(Operator.EQ, lang));
252        
253        SortCriteria sort = new SortCriteria();
254        sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true);
255        
256        String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort);
257        
258        return _resolver.query(xPathQuery);
259    }
260    
261    /**
262     * Get all content with the content type id
263     * @param rootPage the root page
264     * @param contentTypeId the content type id
265     * @return the list of contents
266     */
267    protected AmetysObjectIterable<Content> _getAllContents(Page rootPage, String contentTypeId)
268    {
269        String xPathQuery = null;
270        ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentTypeId);
271        
272        ContentType contentType = _cTypeEP.getExtension(contentTypeId);
273        if (contentType.isReferenceTable())
274        {
275            if (contentType.isMultilingual())
276            {
277                xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", contentTypeExp, null);
278            }
279            else
280            {
281                String lang = rootPage.getSitemapName();
282                Expression finalExpr = new AndExpression(contentTypeExp, new LanguageExpression(Operator.EQ, lang));
283
284                xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, null);
285            }
286        }
287        else
288        {
289            SortCriteria sort = new SortCriteria();
290            sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true);
291
292            StringExpression siteEpx = new StringExpression("site", Operator.EQ, rootPage.getSiteName());
293            String lang = rootPage.getSitemapName();
294            Expression finalExpr = new AndExpression(contentTypeExp, siteEpx, new LanguageExpression(Operator.EQ, lang));
295            
296            xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort);
297        }
298        
299        return _resolver.query(xPathQuery);
300    }
301}