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.plugins.queriesdirectory;
017
018import java.util.Optional;
019import java.util.Set;
020
021import org.ametys.core.group.GroupIdentity;
022import org.ametys.core.user.UserIdentity;
023import org.ametys.plugins.repository.AmetysObjectResolver;
024import org.ametys.plugins.repository.AmetysRepositoryException;
025import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
026import org.ametys.plugins.repository.RepositoryConstants;
027
028/**
029 * Helper for manipulating {@link Query}
030 *
031 */
032public final class QueryHelper
033{
034    private static final String __PLUGIN_NODE_NAME = "queriesdirectory";
035    private static final String __ROOT_QUERY_NODETYPE = RepositoryConstants.NAMESPACE_PREFIX + ":queries";
036    
037    private QueryHelper()
038    {
039        // Private
040    }
041    
042    /**
043     * Get the root plugin storage object.
044     * @param resolver The Ametys object resolver
045     * @return the root plugin storage object.
046     * @throws AmetysRepositoryException if a repository error occurs.
047     */
048    public static ModifiableTraversableAmetysObject getQueriesRootNode(AmetysObjectResolver resolver) throws AmetysRepositoryException
049    {
050        try
051        {
052            ModifiableTraversableAmetysObject pluginsNode = resolver.resolveByPath("/ametys:plugins");
053            
054            ModifiableTraversableAmetysObject pluginNode = getOrCreateNode(pluginsNode, __PLUGIN_NODE_NAME, "ametys:unstructured");
055            
056            return getOrCreateNode(pluginNode, "ametys:queries", __ROOT_QUERY_NODETYPE);
057        }
058        catch (AmetysRepositoryException e)
059        {
060            throw new AmetysRepositoryException("Unable to get the queries root node", e);
061        }
062    }
063    
064    private static ModifiableTraversableAmetysObject getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException
065    {
066        ModifiableTraversableAmetysObject definitionsNode;
067        if (parentNode.hasChild(nodeName))
068        {
069            definitionsNode = parentNode.getChild(nodeName);
070        }
071        else
072        {
073            definitionsNode = parentNode.createChild(nodeName, nodeType);
074            parentNode.saveChanges();
075        }
076        return definitionsNode;
077    }
078    
079    /**
080     * Creates the XPath query to get all queries for administrator
081     * @param type The query type
082     * @return The XPath query
083     */
084    public static String getXPathQueryForAdministrator(Optional<String> type)
085    {
086        return _getXPathQuery(null, false/*admin*/, null, type, null);
087    }
088    
089    /**
090     * Creates the XPath query to get all queries in READ access
091     * @param user The user
092     * @param groups The user's groups
093     * @param type The query type
094     * @return The XPath query
095     */
096    public static String getXPathQueryForReadAccess(UserIdentity user, Set<GroupIdentity> groups, Optional<String> type)
097    {
098        return _getXPathQuery(user, true, groups, type, true);
099    }
100    
101    /**
102     * Creates the XPath query to get all queries in WRITE access
103     * @param user The user
104     * @param groups The user's groups
105     * @param type The query type
106     * @return The XPath query
107     */
108    public static String getXPathQueryForWriteAccess(UserIdentity user, Set<GroupIdentity> groups, Optional<String> type)
109    {
110        return _getXPathQuery(user, true, groups, type, false);
111    }
112    
113    private static String _getXPathQuery(UserIdentity user, boolean checkVisibility, Set<GroupIdentity> groups, Optional<String> type, Boolean readAccess)
114    {
115        StringBuilder query = new StringBuilder("//element(*, ")
116                .append(QueryFactory.QUERY_NODETYPE)
117                .append(")");
118        if (checkVisibility || type.isPresent())
119        {
120            query.append('[');
121        }
122        
123        type.ifPresent(t -> query.append("@").append(Query.TYPE).append("='").append(t).append("'"));
124        
125        if (checkVisibility)
126        {
127            type.ifPresent(t -> query.append(" and ("));
128            
129            _appendPublicPredicate(query);
130            
131            String login = user.getLogin();
132            String populationId = user.getPopulationId();
133            _appendPrivatePredicate(query, login, populationId);
134            
135            _appendSharedPredicate(query, login, populationId, groups, readAccess);
136            
137            type.ifPresent(t -> query.append(')'));
138        }
139        
140        
141        if (checkVisibility || type.isPresent())
142        {
143            query.append(']');
144        }
145        
146        return query.toString();
147    }
148    
149    private static void _appendPublicPredicate(StringBuilder query)
150    {
151        query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.PUBLIC.name())
152             .append("') or ");
153    }
154    
155    private static void _appendPrivatePredicate(StringBuilder query, String login, String populationId)
156    {
157        query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.PRIVATE.name())
158             .append("' and ");
159        _appendUserCondition(query, login, populationId);
160        query.append(") or ");
161    }
162    
163    private static void _appendSharedPredicate(StringBuilder query, String login, String populationId, Set<GroupIdentity> groups, boolean readAccess)
164    {
165        query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.SHARED.name())
166             .append("' and (");
167        query.append("(");
168        _appendUserCondition(query, login, populationId);
169        query.append(") or ");
170        
171        if (readAccess)
172        {
173            _appendUserAccessCondition(query, login, populationId, Query.PROFILE_READ_ACCESS);
174            query.append(" or ");
175        }
176        _appendUserAccessCondition(query, login, populationId, Query.PROFILE_WRITE_ACCESS);
177        
178        _appendGroupsCondition(query, groups, readAccess);
179        query.append("))");
180    }
181    
182    private static void _appendUserCondition(StringBuilder query, String login, String populationId)
183    {
184        query.append('@').append(Query.AUTHOR).append("/ametys:login='").append(login)
185             .append("' and @").append(Query.AUTHOR).append("/ametys:population='").append(populationId)
186             .append('\'');
187    }
188    
189    private static void _appendGroupsCondition(StringBuilder query, Set<GroupIdentity> groups, boolean readAccess)
190    {
191        for (GroupIdentity group : groups)
192        {
193            String groupId = group.getId();
194            String groupDirectory = group.getDirectoryId();
195            
196            if (readAccess)
197            {
198                query.append(" or ");
199                _appendGroupAccessCondition(query, groupId, groupDirectory, Query.PROFILE_READ_ACCESS);
200            }
201            query.append(" or ");
202            _appendGroupAccessCondition(query, groupId, groupDirectory, Query.PROFILE_WRITE_ACCESS);
203        }
204    }
205    
206    private static void _appendUserAccessCondition(StringBuilder query, String login, String populationId, String accessNodeName)
207    {
208        query.append("(").append(accessNodeName).append("/").append(Query.USERS).append("/ametys:login='").append(login)
209             .append("' and ").append(accessNodeName).append("/").append(Query.USERS).append("/ametys:population='").append(populationId)
210             .append("')");
211    }
212    
213    private static void _appendGroupAccessCondition(StringBuilder query, String groupId, String groupDirectory, String accessNodeName)
214    {
215        query.append("(").append(accessNodeName).append("/").append(Query.GROUPS).append("/ametys:groupId='").append(groupId)
216             .append("' and ").append(accessNodeName).append("/").append(Query.GROUPS).append("/ametys:groupDirectory='").append(groupDirectory)
217             .append("')");
218    }
219}