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