001/*
002 *  Copyright 2019 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.web.frontoffice.search.metamodel.impl;
017
018import java.util.Collections;
019import java.util.Comparator;
020import java.util.Locale;
021import java.util.Map;
022import java.util.Optional;
023import java.util.stream.Stream;
024
025import org.apache.commons.lang3.tuple.Pair;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import org.ametys.cms.contenttype.ContentType;
030import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
031import org.ametys.cms.contenttype.MetadataType;
032import org.ametys.cms.repository.Content;
033import org.ametys.cms.repository.ContentTypeExpression;
034import org.ametys.cms.repository.LanguageExpression;
035import org.ametys.cms.search.ui.model.SearchUICriterion;
036import org.ametys.core.util.LambdaUtils;
037import org.ametys.plugins.repository.AmetysObjectIterable;
038import org.ametys.plugins.repository.AmetysObjectResolver;
039import org.ametys.plugins.repository.RepositoryConstants;
040import org.ametys.plugins.repository.query.QueryHelper;
041import org.ametys.plugins.repository.query.expression.AndExpression;
042import org.ametys.plugins.repository.query.expression.Expression;
043import org.ametys.plugins.repository.query.expression.Expression.Operator;
044import org.ametys.plugins.repository.query.expression.OrExpression;
045import org.ametys.runtime.i18n.I18nizableText;
046import org.ametys.runtime.parameter.Validator;
047import org.ametys.web.frontoffice.search.metamodel.FrontEnumerableSearchCriterionDefinition;
048import org.ametys.web.frontoffice.search.metamodel.Searchable;
049
050/**
051 * {@link ContentSearchCriterionDefinition} for a {@link MetadataType#CONTENT} attribute.
052 */
053public class ContentAttributeContentSearchCriterionDefinition extends ContentSearchCriterionDefinition implements FrontEnumerableSearchCriterionDefinition
054{
055    private static final Logger __LOGGER = LoggerFactory.getLogger(ContentAttributeContentSearchCriterionDefinition.class);
056    
057    /** The resolver */
058    protected AmetysObjectResolver _resolver;
059    /** The extension point for content types */
060    protected ContentTypeExtensionPoint _cTypeEP;
061    
062    /**
063     * Default constructor
064     * @param id The id
065     * @param pluginName The plugin name
066     * @param searchable the {@link Searchable}
067     * @param criterion The linked {@link SearchUICriterion}
068     * @param contentType The content type on which this criterion definition applies. Can be empty if it applies to all types of contents.
069     * @param validator the validator
070     * @param resolver The resolver
071     * @param contentTypeEP The extension point for content types
072     */
073    public ContentAttributeContentSearchCriterionDefinition(
074            String id,
075            String pluginName,
076            Optional<Searchable> searchable,
077            SearchUICriterion criterion,
078            Optional<ContentType> contentType,
079            Optional<Validator> validator,
080            AmetysObjectResolver resolver,
081            ContentTypeExtensionPoint contentTypeEP)
082    {
083        super(id, pluginName, searchable, criterion, contentType, validator);
084        _resolver = resolver;
085        _cTypeEP = contentTypeEP;
086    }
087    
088    @Override
089    public Map<Object, I18nizableText> getEntries(String language)
090    {
091        String cTypeId = _searchUICriterion.getContentTypeId();
092        if (cTypeId != null)
093        {
094            try
095            {
096                boolean multilingual = _cTypeEP.getExtension(cTypeId).isMultilingual();
097                Expression expr = new AndExpression(
098                        getContentTypeExpression(cTypeId),
099                        multilingual ? null : new LanguageExpression(Operator.EQ, language));
100                AmetysObjectIterable<Content> contents = _resolver.query(QueryHelper.getXPathQuery(null, RepositoryConstants.NAMESPACE_PREFIX + ":content", expr));
101                
102                return contents.stream()
103                        .map(content -> Pair.of(content.getId(), content.getTitle(new Locale(language))))
104                        .sorted(Comparator.comparing(Pair::getRight)) // sort by title
105                        .collect(LambdaUtils.Collectors.toLinkedHashMap(Pair::getLeft, pair -> new I18nizableText(pair.getRight())));
106            }
107            catch (Exception e)
108            {
109                __LOGGER.error("Failed to get content enumeration for content type {}", cTypeId, e);
110                return Collections.EMPTY_MAP;
111            }
112        }
113        
114        return Collections.EMPTY_MAP;
115    }
116    
117    /**
118     * Gets the content type expression for retrieving enumeration of contents
119     * @param parentCTypeId The parent content type id
120     * @return The {@link Expression}
121     */
122    protected Expression getContentTypeExpression(String parentCTypeId)
123    {
124        Stream<String> subCTypesIds = _cTypeEP.getSubTypes(parentCTypeId).stream();
125        Expression[] exprs = Stream.concat(Stream.of(parentCTypeId), subCTypesIds)
126                .map(cTypeId -> new ContentTypeExpression(Operator.EQ, cTypeId))
127                .toArray(Expression[]::new);
128        return new OrExpression(exprs);
129    }
130}
131