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.search.misc;
017
018import java.util.Arrays;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.context.Context;
024import org.apache.avalon.framework.context.ContextException;
025import org.apache.avalon.framework.context.Contextualizable;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.cocoon.components.ContextHelper;
030import org.apache.cocoon.environment.Request;
031import org.apache.commons.lang3.StringUtils;
032
033import org.ametys.cms.search.query.AndQuery;
034import org.ametys.cms.search.query.NotQuery;
035import org.ametys.cms.search.query.Query;
036import org.ametys.cms.search.query.Query.Operator;
037import org.ametys.core.util.JSONUtils;
038import org.ametys.runtime.model.exception.BadItemTypeException;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.search.query.SiteQuery;
041
042/**
043 * Helper for getting a {@link SiteQuery} from a user-submitted value.
044 */
045public class SiteQueryHelper implements Component, Serviceable, Contextualizable
046{
047    /** Avalon Role */
048    public static final String ROLE = SiteQueryHelper.class.getName();
049    
050    private JSONUtils _jsonUtils;
051    private Context _context;
052    
053    @Override
054    public void contextualize(Context context) throws ContextException
055    {
056        _context = context;
057    }
058    
059    @Override
060    public void service(ServiceManager manager) throws ServiceException
061    {
062        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
063    }
064    
065    /**
066     * Gets a {@link Query} testing {@link Site} from a user-submitted value
067     * @param value a user-submitted value
068     * @param operator The operator
069     * @param criterionId The criterion id (for logging purposes only)
070     * @param contextualParameters  the search contextual parameters.
071     * @return The {@link Query}
072     */
073    @SuppressWarnings("unchecked")
074    public Query getQuery(Object value, Operator operator, String criterionId, Map<String, Object> contextualParameters)
075    {
076        // Handles a list of given site names (SITES_LIST) as well as "CURRENT_SITE", "OTHER_SITES" and "SITES" (all sites).
077        Map<String, Object> valueMap = _parseSite(value, criterionId);
078        String context = (String) valueMap.get("context");
079        
080        Request request = ContextHelper.getRequest(_context);
081        String currentSite = (String) request.getAttribute("siteName");
082        if (StringUtils.isBlank(currentSite))
083        {
084            currentSite = (String) contextualParameters.get("siteName");
085        }
086        
087        if ("CURRENT_SITE".equals(context))
088        {
089            return new SiteQuery(operator, currentSite);
090        }
091        else if ("OTHER_SITES".equals(context))
092        {
093            // other sites than current one
094            // so 'normal case' (i.e. operator = EQ) is testing <NE current_site>
095            // and 'NE case' is testing the opposite (site is not equal to other sites), <EQ current_site>
096            return new AndQuery(
097                    new SiteQuery(), 
098                    new SiteQuery(Operator.NE == operator
099                            ? Operator.EQ
100                            : Operator.NE
101                        , currentSite));
102        }
103        else if ("SITES".equals(context))
104        {
105            return Operator.NE == operator
106                    ? new NotQuery(new SiteQuery())
107                    : new SiteQuery();
108        }
109        
110        // "SITES_LIST" context: get the site list from the value map.
111        List<String> names = (List<String>) valueMap.get("sites");
112        
113        // Standard context: site list.
114        return new SiteQuery(operator, names);
115    }
116    
117    /**
118     * Converts the given site value to have {@link Map} containing site context and value
119     * @param value the value to convert
120     * @param criterionId The criterion id (for logging purposes only)
121     * @return the value, converted to a {@link Map}
122     * @throws BadItemTypeException if the given value can not be converted
123     */
124    private Map<String, Object> _parseSite(Object value, String criterionId) throws BadItemTypeException
125    {
126        if (value == null)
127        {
128            return null;
129        }
130        else if (value instanceof String)
131        {
132            String strValue = (String) value;
133            if (strValue.startsWith("{"))
134            {
135                // Usually when coming from BO site widget
136                return _jsonUtils.convertJsonToMap(strValue);
137            }
138            else if ("CURRENT_SITE".equals(strValue) || "OTHER_SITES".equals(strValue) || "SITES".equals(strValue))
139            {
140                return Map.of("context", strValue);
141            }
142            else
143            {
144                // Consider it as the list of sites itself
145                return Map.of(
146                        "context", "SITES_LIST", 
147                        "sites", Arrays.asList(strValue.split(","))
148                );
149            }
150        }
151        else if (value instanceof String[] arrayValue)
152        {
153            // Consider it as the list of sites itself
154            // Coming from all options of search service criterion
155            return Map.of(
156                    "context", "SITES_LIST", 
157                    "sites", Arrays.asList(arrayValue)
158            );
159        }
160        else
161        {
162            throw new BadItemTypeException("The value " + value + " for criterion " + criterionId + " is not a valid Site value.");
163        }
164    }
165}
166