001/*
002 *  Copyright 2014 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.cms.tag;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import javax.jcr.RepositoryException;
027
028import org.apache.avalon.framework.parameters.Parameters;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.ProcessingException;
032import org.apache.cocoon.acting.ServiceableAction;
033import org.apache.cocoon.environment.ObjectModelHelper;
034import org.apache.cocoon.environment.Redirector;
035import org.apache.cocoon.environment.Request;
036import org.apache.cocoon.environment.SourceResolver;
037import org.apache.commons.lang.StringUtils;
038import org.xml.sax.SAXException;
039
040import org.ametys.cms.tag.jcr.AbstractJCRTagProvider;
041import org.ametys.core.cocoon.JSonReader;
042import org.ametys.plugins.repository.AmetysObjectResolver;
043import org.ametys.plugins.repository.AmetysRepositoryException;
044
045/**
046 * SAX events for tags
047 *
048 */
049public abstract class AbstractGetTagsAction extends ServiceableAction
050{
051    /** The tag provider extension point */
052    protected AbstractTagProviderExtensionPoint<? extends Tag> _tagProviderExtPt;
053
054    /** The Ametys object resolver */
055    protected AmetysObjectResolver _resolver;
056    
057    @SuppressWarnings("unchecked")
058    @Override
059    public void service(ServiceManager serviceManager) throws ServiceException
060    {
061        super.service(serviceManager);
062        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
063        _tagProviderExtPt = (AbstractTagProviderExtensionPoint< ? extends Tag>) serviceManager.lookup(getTagProviderEPRole());
064    }
065
066    /**
067     * Get the tag provider extension point role
068     * @return the tag provider extension point role
069     */
070    public abstract String getTagProviderEPRole();
071    
072    @SuppressWarnings("unchecked")
073    @Override
074    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws IOException, SAXException, ProcessingException
075    {
076        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
077        
078        String parentId = (String) jsParameters.get("node");
079        String nodeName = (String) jsParameters.get("nodeName");
080        boolean onlyCustomTags = (Boolean) jsParameters.get("onlyCustomTags");
081        
082        List<String> objectTargetIds = (List<String>) jsParameters.get("objectTargetIds");
083        
084        Map<String, Object> contextualParameters = (Map<String, Object>) jsParameters.get("contextualParameters");
085        
086        List<Map<String, Object>> tags = new ArrayList<>();
087
088        if (StringUtils.isEmpty(parentId) || parentId.equals("tag-root"))
089        {
090            Set<String> extensionsIds = getProvidersIds(onlyCustomTags);
091
092            for (String id : extensionsIds)
093            {
094                TagProvider<? extends Tag> tagProvider = _tagProviderExtPt.getExtension(id);
095                tags.add(providerToJSON(tagProvider, contextualParameters, jsParameters));
096            }
097        }
098        else if (nodeName.startsWith("provider_"))
099        {
100            String providerId = nodeName.substring("provider_".length());
101            TagProvider<? extends Tag> tagProvider = _tagProviderExtPt.getExtension(providerId);
102            
103            if (tagProvider != null)
104            {
105                for (Tag tag : _getFilteredTags(tagProvider.getTags(contextualParameters), jsParameters).values())
106                {
107                    tags.add(tagToJSON(tag, tagProvider, objectTargetIds, jsParameters));
108                }
109            }
110        }
111        else
112        {
113            Set<String> extensionsIds = getProvidersIds(onlyCustomTags);
114            for (String id : extensionsIds)
115            {
116                TagProvider<? extends Tag> tagProvider = _tagProviderExtPt.getExtension(id);
117                if (tagProvider.hasTag(nodeName, contextualParameters))
118                {
119                    Tag parent = tagProvider.getTag(nodeName, contextualParameters);
120                    for (Tag tag : _getFilteredTags(parent.getTags(), jsParameters).values())
121                    {
122                        tags.add(tagToJSON(tag, tagProvider, objectTargetIds, jsParameters));
123                    }
124                }
125            }
126        }
127
128        Map<String, Object> result = new HashMap<>();
129        result.put("children", tags);
130
131        Request request = ObjectModelHelper.getRequest(objectModel);
132        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
133
134        return EMPTY_MAP;
135    }
136
137    /**
138     * Get filtered tags
139     * @param tags the tags to filter
140     * @param jsParameters the js parameters
141     * @return the map of tags
142     */
143    protected Map<String, ? extends Tag> _getFilteredTags(Map<String, ? extends Tag> tags, Map<String, Object> jsParameters)
144    {
145        return tags;
146    }
147    
148    /**
149     * Get all providers IDs
150     * @param onlyCustomTags If true, return only JCR providers IDs.
151     * @return a set of providers IDs
152     */
153    protected Set<String> getProvidersIds(boolean onlyCustomTags)
154    {
155        if (!onlyCustomTags)
156        {
157            return _tagProviderExtPt.getExtensionsIds();
158        }
159        
160        return new HashSet<>();
161    }
162    
163    /**
164     * Returns the tag providers' id
165     * @return The id of tag providers
166     */
167    protected Set<String> getProvidersId()
168    {
169        return getProvidersIds(false);
170    }
171
172    /**
173     * Get tag's properties to JSON format
174     * @param tag The tag
175     * @param tagProvider the tag provider
176     * @param objectTargetIds the list of Ametys object ids. Can be empty
177     * @param jsParameters the js parameters
178     * @return The tag properties
179     */
180    protected Map<String, Object> tagToJSON(Tag tag, TagProvider<? extends Tag> tagProvider, List<String> objectTargetIds, Map<String, Object> jsParameters)
181    {
182        Map<String, Object> infos = new HashMap<>();
183
184        infos.put("id", tag.getId());
185        infos.put("name", tag.getName());
186        infos.put("title", tag.getTitle());
187        infos.put("description", tag.getDescription());
188        
189        boolean authorized = isUserAuthorized(tag, objectTargetIds, jsParameters);
190        infos.put("authorized", authorized);
191        infos.put("creationAllowed", isCreationAllowed(tagProvider, tag));
192        
193        if (tag.getTags().size() == 0)
194        {
195            infos.put("leaf", true);
196        }
197        
198        return infos;
199    }
200
201    /**
202     * Get tag provider's properties to JSON format
203     * @param tagProvider the tag provider
204     * @param contextualParameters The contextual parameters
205     * @param jsParameters The js parameters
206     * @return The tag providers properties
207     * @throws AmetysRepositoryException If an error occurred
208     */
209    protected Map<String, Object> providerToJSON(TagProvider<? extends Tag> tagProvider, Map<String, Object> contextualParameters, Map<String, Object> jsParameters)
210    {
211        Map<String, Object> infos = new HashMap<>();
212
213        if (tagProvider instanceof AbstractJCRTagProvider)
214        {
215            AbstractJCRTagProvider<? extends Tag> provider = (AbstractJCRTagProvider<? extends Tag>) tagProvider;
216            try
217            {
218                infos.put("id", provider.getRootNode(contextualParameters).getId());
219            }
220            catch (AmetysRepositoryException e)
221            {
222                getLogger().error("Unable to get JCR tag provider root node", e);
223            }
224            catch (RepositoryException e)
225            {
226                getLogger().error("Unable to get JCR tag provider root node", e);
227            }
228        }
229        else
230        {
231            infos.put("id", tagProvider.getId());
232        }
233        
234        infos.put("type", "provider");
235        infos.put("name", "provider_" + tagProvider.getId());
236        infos.put("title", tagProvider.getLabel());
237        infos.put("description", tagProvider.getDescription());
238        infos.put("authorized", true); 
239        infos.put("creationAllowed", isCreationAllowed(tagProvider, null));
240        infos.put("iconSmall", "/plugins/cms/resources/img/tag/tags_16.png");
241        infos.put("iconMedium", "/plugins/cms/resources/img/tag/tags_32.png");
242        infos.put("iconLarge", "/plugins/cms/resources/img/tag/tags_50.png");
243
244        if (_getFilteredTags(tagProvider.getTags(contextualParameters), jsParameters).size() == 0)
245        {
246            infos.put("leaf", true);
247        }
248        return infos;
249    }
250    
251    /**
252     * Test if a tag is visible to the current user.
253     * @param tag the Tag object.
254     * @param objectTargetIds the ids of current Ametys objects
255     * @param jsParameters The js parameters
256     * @return true if the user has access to the tag, false otherwise.
257     */
258    protected boolean isUserAuthorized(Tag tag, List<String> objectTargetIds, Map<String, Object> jsParameters)
259    {
260        return true;
261    }
262    
263    /**
264     * Determines if the current user can created tag
265     * @param tagProvider The tag provider
266     * @param tag The tag. Can be null.
267     * @return true if creation is allowed
268     */
269    protected boolean isCreationAllowed(TagProvider<? extends Tag> tagProvider, Tag tag)
270    {
271        return true;
272    }
273}