001/*
002 *  Copyright 2013 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.workspaces.query;
017
018import java.io.IOException;
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.avalon.framework.parameters.Parameters;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.acting.ServiceableAction;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Redirector;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.environment.SourceResolver;
030import org.apache.commons.lang.ArrayUtils;
031import org.apache.commons.lang3.StringUtils;
032import org.apache.excalibur.source.Source;
033
034import org.ametys.cms.search.cocoon.SearchGenerator;
035import org.ametys.core.authentication.AuthenticateAction;
036import org.ametys.core.user.CurrentUserProvider;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.util.JSONUtils;
039import org.ametys.plugins.queriesdirectory.Query;
040import org.ametys.plugins.queriesdirectory.Query.Visibility;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.runtime.authentication.AccessDeniedException;
043
044/**
045 * Get query parameters from query
046 *
047 */
048public class GetQueryParametersAction extends ServiceableAction
049{
050    
051    private static final String _XSLT_BASE_LOCATION = "context://WEB-INF/stylesheets/export";
052    
053    private AmetysObjectResolver _resolver;
054
055    /** The Json utils */
056    private JSONUtils _jsonUtils;
057
058    private CurrentUserProvider _currentUserProvide;
059
060    @Override
061    public void service(ServiceManager smanager) throws ServiceException
062    {
063        super.service(smanager);
064        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
065        _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE);
066        _currentUserProvide = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
067    }
068
069    @Override
070    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
071    {
072        Map<String, String> result = new HashMap<>();
073        Request request = ObjectModelHelper.getRequest(objectModel);
074
075        request.setAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_INTERNAL_ALLOWED, true);
076        
077        String queryId = request.getParameter("queryId");
078        String xslt = parameters.getParameter("xslt", "");
079        
080        UserIdentity user = _currentUserProvide.getUser();
081
082        Query query = _resolver.resolveById(queryId);
083        
084        if (user == null && !query.getVisibility().equals(Visibility.PUBLIC))
085        {
086            throw new AccessDeniedException("The query of id '" + queryId + "' is not a public query");
087        }
088        else if (!query.canRead(user))
089        {
090            throw new AccessDeniedException("The user " + user + " is not allowed to access the private query of id " + queryId);
091        }
092        
093        String xsltFile = StringUtils.defaultString(getXsltLocation(resolver, xslt));
094        
095        Map<String, Object> content = _jsonUtils.convertJsonToMap(query.getContent());
096        @SuppressWarnings("unchecked")
097        Map<String, Object> params = (Map<String, Object>) content.get("exportParams");
098        
099        String versionLabel = parameters.getParameter("versionLabel");
100        if (StringUtils.isNotBlank(versionLabel))
101        {
102            params.put(SearchGenerator.CONTENT_VERSION_LABEL, versionLabel);
103        }
104        
105        String pluginName = (String) content.get("exportXMLUrlPlugin");
106        String exportUrl = (String) content.get("exportXMLUrl");
107
108        result.put("pluginName", pluginName != null ? pluginName : "cms");
109        result.put("exportUrl", exportUrl != null ? exportUrl : "search/export.xml");
110        
111        result.put("parameters", InternalEncoder.encode(_jsonUtils.convertObjectToJson(params)));
112        result.put("xsltFile", xsltFile);
113        return result;
114    }
115    
116    /**
117     * Get the XSLT location.
118     * @param resolver the source resolver.
119     * @param xslt the requested xslt.
120     * @return the XSLT file location.
121     * @throws IOException if an error occurs resolving the XSLT.
122     */
123    protected String getXsltLocation(SourceResolver resolver, String xslt) throws IOException
124    {
125        Source xsltSource = null;
126        try
127        {
128            if (StringUtils.isNotBlank(xslt))
129            {
130                String location = _XSLT_BASE_LOCATION + "/" + xslt;
131                xsltSource = resolver.resolveURI(location);
132                
133                if (xsltSource.exists())
134                {
135                    return location;
136                }
137            }
138        }
139        finally
140        {
141            if (xsltSource != null)
142            {
143                resolver.release(xsltSource);
144            }
145        }
146        
147        return null;
148    }
149    
150    /**
151     * Helper defining methods used to partially encode the query string of
152     * internal (cocoon) wrapped requests.
153     */
154    private static final class InternalEncoder
155    {
156        private static final char[] NEED_ENCODING = {'%', '&', '=', '?', ' ', '+'};
157
158        private InternalEncoder()
159        {
160            // Do not instantiate
161        }
162        
163        /**
164         * Partially encode a string given the NEED_ENCODING array.
165         * @param str The string to encode
166         * @return encode String.
167         */
168        public static String encode(String str)
169        {
170            boolean changed = false;
171            final char escape = '%';
172            
173            int len = str.length();
174            StringBuilder sb = new StringBuilder(len);
175            
176            for (int i = 0; i < len; i++)
177            {
178                char ch = str.charAt(i);
179                if (ArrayUtils.contains(NEED_ENCODING, ch))
180                {
181                    sb.append(escape);
182                    sb.append(Integer.toHexString(ch));
183                    changed = true;
184                }
185                else
186                {
187                    sb.append(ch);
188                }
189            }
190            
191            return changed ? sb.toString() : str;
192        }
193    }
194}
195