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        if (tag instanceof ColorableTag colorableTag)
190        {
191            infos.put("isColorable", true);
192            infos.put("color", colorableTag.getColor(true));
193            infos.put("class",  colorableTag.getColorCSSClassPrefix());
194        }
195        
196        boolean authorized = isUserAuthorized(tag, objectTargetIds, jsParameters);
197        infos.put("authorized", authorized);
198        infos.put("creationAllowed", isCreationAllowed(tagProvider, tag));
199        
200        if (tag.getTags().size() == 0)
201        {
202            infos.put("leaf", true);
203        }
204        
205        return infos;
206    }
207
208    /**
209     * Get tag provider's properties to JSON format
210     * @param tagProvider the tag provider
211     * @param contextualParameters The contextual parameters
212     * @param jsParameters The js parameters
213     * @return The tag providers properties
214     * @throws AmetysRepositoryException If an error occurred
215     */
216    protected Map<String, Object> providerToJSON(TagProvider<? extends Tag> tagProvider, Map<String, Object> contextualParameters, Map<String, Object> jsParameters)
217    {
218        Map<String, Object> infos = new HashMap<>();
219
220        if (tagProvider instanceof AbstractJCRTagProvider)
221        {
222            AbstractJCRTagProvider<? extends Tag> provider = (AbstractJCRTagProvider<? extends Tag>) tagProvider;
223            try
224            {
225                infos.put("id", provider.getRootNode(contextualParameters).getId());
226            }
227            catch (AmetysRepositoryException e)
228            {
229                getLogger().error("Unable to get JCR tag provider root node", e);
230            }
231            catch (RepositoryException e)
232            {
233                getLogger().error("Unable to get JCR tag provider root node", e);
234            }
235        }
236        else
237        {
238            infos.put("id", tagProvider.getId());
239        }
240        
241        infos.put("type", "provider");
242        infos.put("name", "provider_" + tagProvider.getId());
243        infos.put("title", tagProvider.getLabel());
244        infos.put("description", tagProvider.getDescription());
245        infos.put("authorized", true); 
246        infos.put("creationAllowed", isCreationAllowed(tagProvider, null));
247        infos.put("iconSmall", "/plugins/cms/resources/img/tag/tags_16.png");
248        infos.put("iconMedium", "/plugins/cms/resources/img/tag/tags_32.png");
249        infos.put("iconLarge", "/plugins/cms/resources/img/tag/tags_50.png");
250
251        if (_getFilteredTags(tagProvider.getTags(contextualParameters), jsParameters).size() == 0)
252        {
253            infos.put("leaf", true);
254        }
255        return infos;
256    }
257    
258    /**
259     * Test if a tag is visible to the current user.
260     * @param tag the Tag object.
261     * @param objectTargetIds the ids of current Ametys objects
262     * @param jsParameters The js parameters
263     * @return true if the user has access to the tag, false otherwise.
264     */
265    protected boolean isUserAuthorized(Tag tag, List<String> objectTargetIds, Map<String, Object> jsParameters)
266    {
267        return true;
268    }
269    
270    /**
271     * Determines if the current user can created tag
272     * @param tagProvider The tag provider
273     * @param tag The tag. Can be null.
274     * @return true if creation is allowed
275     */
276    protected boolean isCreationAllowed(TagProvider<? extends Tag> tagProvider, Tag tag)
277    {
278        return true;
279    }
280}