001/*
002 *  Copyright 2015 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.serverdirectory;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.avalon.framework.parameters.Parameters;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.acting.ServiceableAction;
031import org.apache.cocoon.environment.ObjectModelHelper;
032import org.apache.cocoon.environment.Redirector;
033import org.apache.cocoon.environment.Request;
034import org.apache.commons.lang.StringUtils;
035import org.apache.excalibur.source.Source;
036import org.apache.excalibur.source.SourceResolver;
037import org.apache.excalibur.source.TraversableSource;
038
039import org.ametys.core.cocoon.JSonReader;
040
041/**
042 * Generate a subDirectories list from a query and a list of root paths from the configuration
043 */
044public class ServerDirectoryPathAction extends ServiceableAction
045{
046    /** Excalibur's source resolver */
047    private SourceResolver _sourceResolver;
048    
049    @Override
050    public void service(ServiceManager smanager) throws ServiceException
051    {
052        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
053    }
054    
055    public Map act(Redirector redirector, org.apache.cocoon.environment.SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
056    {
057        Request request = ObjectModelHelper.getRequest(objectModel);
058        Map<String, Object> result = new HashMap<> ();
059        
060        List<Map<String, Object>> items = new ArrayList<> ();
061        boolean enableDynamicPaths = "true".equals(request.getParameter("enableDynamicPaths"));
062
063        Set<Source> sources = new HashSet<>();
064        
065        String queryPath = ServerDirectoryHelper.normalize(parameters.getParameter("query", ""));
066        
067        Set<Source> rootSources = ServerDirectoryHelper.getRootServerSources(_sourceResolver);
068        for (Source rootSource : rootSources)
069        {
070            if (StringUtils.isEmpty(queryPath))
071            {
072                sources.add(rootSource);
073            }
074            else
075            {
076                String rootURI = rootSource.getURI();
077                String rootPath = StringUtils.substringAfter(rootURI, "file:/");
078                
079                if (queryPath.startsWith(rootPath) || queryPath.startsWith(rootURI))
080                {
081                    if (!enableDynamicPaths)
082                    {
083                        sources.addAll(_getValidServerDirectories(queryPath));
084                    }
085                }
086                else if (rootPath.startsWith(queryPath) || rootURI.startsWith(queryPath))
087                {
088                    sources.add(rootSource);
089                }
090            }
091        }
092        
093        for (Source pathResult : sources)
094        {
095            Map<String, Object> item = new HashMap<> ();
096            item.put("displayText", StringUtils.substringAfter(pathResult.getURI(), pathResult.getScheme() + ":/"));
097            item.put("path", pathResult.getURI());
098            
099            items.add(item);
100        }
101        
102        result.put("items", items);
103        
104        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
105        return EMPTY_MAP;
106    }
107    
108    /**
109     * Look for valid directories inside of a path.
110     * @param path The path. It can be incomplete and a partial path will be used to match multiple folders.
111     * @return a set of matching directories paths
112     * @throws ProcessingException if an error occurs when retrieving the list of valid server directories
113     */
114    private Set<Source> _getValidServerDirectories(String path) throws ProcessingException
115    {
116        Set<Source> validSources = new HashSet<>();
117        
118        String folderStartsWith = "";
119        String rootPath = path.replace("\\", "/");
120        
121        if (!rootPath.endsWith("/"))
122        {
123            if (rootPath.contains("/"))
124            {
125                folderStartsWith = rootPath.substring(rootPath.lastIndexOf("/") + 1);
126                rootPath = rootPath.substring(0, rootPath.lastIndexOf("/"));
127            }
128            else
129            {
130                folderStartsWith = rootPath;
131                rootPath = "";
132            }
133        }
134        
135        Source root = null;
136        try
137        {
138            root = _sourceResolver.resolveURI(rootPath.trim(), "file://", null);
139            if (root.exists() && root instanceof TraversableSource && ((TraversableSource) root).isCollection())
140            {
141                if (folderStartsWith.isEmpty())
142                {
143                    validSources.add(root);
144                }
145                
146                TraversableSource tSource = (TraversableSource) root;
147                Collection<Source> childrenSources = tSource.getChildren();
148                for (Source childSource : childrenSources)
149                {
150                    if (childSource instanceof TraversableSource && ((TraversableSource) childSource).isCollection())
151                    { 
152                        String[] childPathSplit = childSource.getURI().split("/");
153                        if (childPathSplit.length > 0 && childPathSplit[childPathSplit.length - 1].startsWith(folderStartsWith))
154                        {
155                            validSources.add(childSource);
156                        }
157                    }
158                    
159                }
160            }
161        }
162        catch (Exception e)
163        {
164            throw new ProcessingException("Unable to retrieve server directory to location: <" + rootPath + ">", e);
165        }
166        finally
167        {
168            _sourceResolver.release(root);
169        }
170        
171        return validSources;
172    }
173
174    
175}