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.cms.tag.jcr; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import javax.jcr.RepositoryException; 026import javax.jcr.Session; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.logger.AbstractLogEnabled; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.cocoon.ProcessingException; 034 035import org.ametys.cms.ObservationConstants; 036import org.ametys.cms.tag.Tag; 037import org.ametys.cms.tag.TagHelper; 038import org.ametys.cms.tag.TagProvider; 039import org.ametys.core.observation.Event; 040import org.ametys.core.observation.ObservationManager; 041import org.ametys.core.ui.Callable; 042import org.ametys.core.user.CurrentUserProvider; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 045import org.ametys.plugins.repository.UnknownAmetysObjectException; 046import org.ametys.plugins.repository.jcr.JCRAmetysObject; 047import org.ametys.plugins.repository.jcr.SimpleAmetysObject; 048 049/** 050 * Component for operations on JCR tags 051 */ 052public abstract class AbstractJCRTagsDAO extends AbstractLogEnabled implements Serviceable, Component 053{ 054 /** The ametys object resolver */ 055 protected AmetysObjectResolver _resolver; 056 057 /** Observer manager. */ 058 protected ObservationManager _observationManager; 059 060 /** The current user provider */ 061 protected CurrentUserProvider _currentUserProvider; 062 063 @Override 064 public void service(ServiceManager manager) throws ServiceException 065 { 066 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 067 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 068 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 069 } 070 071 /** 072 * Create a new tag from client-side. User rights are checked. 073 * @param parentId The id of parent tag 074 * @param originalName The original name 075 * @param title The tag's title 076 * @param description The tag's description 077 * @param otherParameters the other parameters 078 * @param contextualParameters Contextual parameters transmitted by the environment. 079 * @return The result map 080 */ 081 @Callable 082 public Map<String, Object> createTag (String parentId, String originalName, String title, String description, Map<String, Object> otherParameters, Map<String, Object> contextualParameters) 083 { 084 _checkUserRight(); 085 086 Map<String, Object> result = new HashMap<>(); 087 088 try 089 { 090 JCRTag jcrTag = addTag(parentId, originalName, title, description, otherParameters, contextualParameters); 091 092 result.put("tagId", jcrTag.getId()); 093 result.put("tagName", jcrTag.getName()); 094 result.put("tagParentId", parentId); 095 } 096 catch (RepositoryException e) 097 { 098 getLogger().error("Unable to get JCR tag root node", e); 099 result.put("message", "unknown-parent-tag"); 100 } 101 catch (UnknownAmetysObjectException e) 102 { 103 getLogger().error("The tag '" + parentId + "' does not exist anymore", e); 104 result.put("message", "unknown-parent-tag"); 105 } 106 107 return result; 108 } 109 110 /** 111 * Create a new tag regardless of user rights. 112 * @param parentId The id of parent tag 113 * @param originalName The original name 114 * @param title The tag's title 115 * @param description The tag's description 116 * @param otherParameters the other parameters 117 * @param contextualParameters Contextual parameters transmitted by the environment. 118 * @return The created JCR tag 119 * @throws UnknownAmetysObjectException if the parent does not exist 120 * @throws RepositoryException if failed to create tag 121 */ 122 public JCRTag addTag(String parentId, String originalName, String title, String description, Map<String, Object> otherParameters, Map<String, Object> contextualParameters) throws UnknownAmetysObjectException, RepositoryException 123 { 124 // Find an unique name 125 String name = _findUniqueName (originalName, contextualParameters); 126 127 JCRTag jcrTag = _createJCRTag(parentId, name, title, description, otherParameters, contextualParameters); 128 129 // Notify observers that the tag has been added. 130 Map<String, Object> eventParams = new HashMap<>(); 131 eventParams.put(ObservationConstants.ARGS_TAG_ID, jcrTag.getId()); 132 eventParams.put(ObservationConstants.ARGS_TAG_NAME, jcrTag.getName()); 133 _observationManager.notify(new Event(ObservationConstants.EVENT_TAG_ADDED, _currentUserProvider.getUser(), eventParams)); 134 135 return jcrTag; 136 } 137 138 /** 139 * Deletes a JCR tag 140 * @param tagId The tag's id 141 * @param contextualParameters Contextual parameters transmitted by the environment. 142 * @return the result map 143 */ 144 @Callable 145 public Map<String, Object> deleteTag (String tagId, Map<String, Object> contextualParameters) 146 { 147 _checkUserRight(); 148 149 Map<String, Object> result = new HashMap<>(); 150 151 try 152 { 153 JCRTag jcrTag = _resolver.resolveById(tagId); 154 SimpleAmetysObject<?> parent = jcrTag.getParent(); 155 156 Map<String, Object> eventParams = new HashMap<>(); 157 eventParams.put(ObservationConstants.ARGS_TAG_ID, jcrTag.getId()); 158 eventParams.put(ObservationConstants.ARGS_TAG_NAME, jcrTag.getName()); 159 160 // descendant names for the event arguments 161 Tag tag = _getTagFromName(jcrTag.getName(), contextualParameters); 162 Collection<String> descendantNames = TagHelper.getDescendantNames(tag, true); 163 eventParams.put("tag.descendantnames", descendantNames); 164 165 result.put("id", jcrTag.getId()); 166 result.put("name", jcrTag.getName()); 167 result.put("title", jcrTag.getTitle()); 168 169 jcrTag.remove(); 170 parent.saveChanges(); 171 172 // Notify observers that the tag has been deleted. 173 _observationManager.notify(new Event(ObservationConstants.EVENT_TAG_DELETED, _currentUserProvider.getUser(), eventParams)); 174 } 175 catch (UnknownAmetysObjectException e) 176 { 177 getLogger().error("Unable to delete tag : the tag '" + tagId + "' does not exist anymore", e); 178 result.put("message", "unknown-tag"); 179 } 180 181 return result; 182 } 183 184 /** 185 * Updates a JCR tag 186 * @param tagId The tag's id 187 * @param title The tag's title 188 * @param description The tag's description 189 * @param otherParameters the other parameters 190 * @return The result map 191 */ 192 @Callable 193 public Map<String, Object> updateTag (String tagId, String title, String description, Map<String, Object> otherParameters) 194 { 195 _checkUserRight(); 196 197 Map<String, Object> result = new HashMap<>(); 198 199 try 200 { 201 JCRTag jcrTag = _updateJCRTag(tagId, title, description, otherParameters); 202 203 result.put("id", jcrTag.getId()); 204 result.put("name", jcrTag.getName()); 205 result.put("title", title); 206 207 // Notify observers that the tag has been modified. 208 209 Map<String, Object> eventParams = new HashMap<>(); 210 eventParams.put(ObservationConstants.ARGS_TAG_ID, jcrTag.getId()); 211 eventParams.put(ObservationConstants.ARGS_TAG_NAME, jcrTag.getName()); 212 _observationManager.notify(new Event(ObservationConstants.EVENT_TAG_UPDATED, _currentUserProvider.getUser(), eventParams)); 213 } 214 catch (UnknownAmetysObjectException e) 215 { 216 getLogger().error("Unable to update tag : the tag '" + tagId + "' does not exist anymore", e); 217 result.put("message", "unknown-tag"); 218 } 219 220 return result; 221 } 222 223 /** 224 * Move a JCR tag 225 * @param targetId The tag where to move to 226 * @param ids The ids of tag to move 227 * @return the result map 228 * @throws ProcessingException If an error occurred 229 */ 230 @Callable 231 public Map<String, Object> moveTags(String targetId, List<String> ids) throws ProcessingException 232 { 233 _checkUserRight(); 234 235 Map<String, Object> result = new HashMap<>(); 236 List<Map<String, Object>> movedTags = new ArrayList<>(); 237 238 JCRAmetysObject target = _resolver.resolveById(targetId); 239 String targetPath = ""; 240 try 241 { 242 targetPath = target.getNode().getPath(); 243 244 for (int i = 0; i < ids.size(); i++) 245 { 246 JCRAmetysObject tag = (JCRAmetysObject) _resolver.resolveById(ids.get(i)); 247 if (!tag.getNode().getPath().equals(targetPath + "/" + tag.getNode().getName())) 248 { 249 Map<String, Object> eventParams = new HashMap<>(); 250 eventParams.put("id", tag.getId()); 251 eventParams.put("oldPath", tag.getNode().getPath()); 252 eventParams.put("path", targetPath + "/" + tag.getNode().getName()); 253 254 Session session = tag.getNode().getSession(); 255 session.move(tag.getNode().getPath(), targetPath + "/" + tag.getNode().getName()); 256 257 session.save(); 258 259 Map<String, Object> tagProperties = new HashMap<>(); 260 tagProperties.put("id", tag.getId()); 261 tagProperties.put("name", tag.getName()); 262 movedTags.add(tagProperties); 263 264 _observationManager.notify(new Event(ObservationConstants.EVENT_TAG_MOVED, _currentUserProvider.getUser(), eventParams)); 265 } 266 } 267 } 268 catch (RepositoryException e) 269 { 270 getLogger().error("Unable to get JCR tag node path", e); 271 throw new ProcessingException("Unable to get JCR tag node path", e); 272 } 273 274 if (movedTags.size() > 0) 275 { 276 result.put("movedTags", movedTags); 277 } 278 279 result.put("target", targetId); 280 281 return result; 282 } 283 284 /** 285 * Get the root node for tags 286 * @param tagProviderId The tag provider id 287 * @param contextualParameters Contextual parameters transmitted by the environment. 288 * @return The root node in key "id" 289 * @throws ProcessingException If an error occurred in the repository 290 */ 291 @Callable 292 public Map<String, Object> getTagRootNode (String tagProviderId, Map<String, Object> contextualParameters) throws ProcessingException 293 { 294 Map<String, Object> result = new HashMap<>(); 295 296 try 297 { 298 result.put("id", _getTagRootObject(tagProviderId, contextualParameters).getId()); 299 } 300 catch (RepositoryException e) 301 { 302 getLogger().error("Unable to get JCR tag root node", e); 303 throw new ProcessingException("Unable to get JCR tag root node", e); 304 } 305 306 return result; 307 } 308 309 /** 310 * Find a JCR tag and return its data 311 * @param tagId The tag's id 312 * @return The result map 313 */ 314 @Callable 315 public Map<String, Object> getTag(String tagId) 316 { 317 Map<String, Object> result = new HashMap<>(); 318 319 try 320 { 321 JCRTag jcrTag = _resolver.resolveById(tagId); 322 result = jcrTag.toJSON(); 323 324 if (jcrTag.getChildren().iterator().hasNext()) 325 { 326 result.put("leaf", true); 327 } 328 } 329 catch (UnknownAmetysObjectException e) 330 { 331 getLogger().error("Unable to get tag : the tag '" + tagId + "' does not exist anymore", e); 332 result.put("message", "unknown-tag"); 333 } 334 335 return result; 336 } 337 338 /** 339 * Get the tag root node object 340 * @param tagProviderId The tag provider id 341 * @param contextualParameters Contextual parameters transmitted by the environment. 342 * @return The tag root node object 343 * @throws RepositoryException If an error occurred in the repository 344 */ 345 public abstract ModifiableTraversableAmetysObject _getTagRootObject (String tagProviderId, Map<String, Object> contextualParameters) throws RepositoryException; 346 347 /** 348 * Check if the user right to access the feature 349 * @throws IllegalStateException if the user has no right 350 */ 351 protected abstract void _checkUserRight () throws IllegalStateException; 352 353 /** 354 * Get the tag from the name 355 * @param name the name 356 * @param contextualParameters the contextual parameters 357 * @return the tag 358 */ 359 protected abstract Tag _getTagFromName(String name, Map<String, Object> contextualParameters); 360 361 /** 362 * Get all tag's providers 363 * @return the providers 364 */ 365 protected abstract Set<TagProvider<? extends Tag>> _getTagProviders (); 366 367 /** 368 * Create a JCR tag under his parent 369 * @param parentId the parent id 370 * @param name the name 371 * @param title the title 372 * @param description the description 373 * @param otherParameters the other parameters 374 * @param contextualParameters Contextual parameters transmitted by the environment. 375 * @return the created JCR tag 376 * @throws RepositoryException if an error occurred 377 */ 378 protected abstract JCRTag _createJCRTag(String parentId, String name, String title, String description, Map<String, Object> otherParameters, Map<String, Object> contextualParameters) throws RepositoryException; 379 380 /** 381 * Update a JCR tag 382 * @param tagId the tag id to update 383 * @param title the title 384 * @param description the description 385 * @param otherParameters the other parameters 386 * @return return the updated JCR tag 387 * @throws UnknownAmetysObjectException if an error occurred 388 */ 389 protected abstract JCRTag _updateJCRTag(String tagId, String title, String description, Map<String, Object> otherParameters) throws UnknownAmetysObjectException; 390 391 392 /** 393 * Find a unique name for the tag 394 * @param originalName The requested name 395 * @param contextualParameters Contextual parameters transmitted by the environment. 396 * @return A unique name 397 */ 398 protected String _findUniqueName(String originalName, Map<String, Object> contextualParameters) 399 { 400 Set<TagProvider<? extends Tag>> providers = _getTagProviders(); 401 402 // Find an unique name 403 int index = 2; 404 String name = originalName; 405 while (_hasTag(providers, name, contextualParameters)) 406 { 407 name = originalName + "_" + (index++); 408 } 409 410 return name; 411 } 412 413 /** 414 * Determines if a tag with given name already exists 415 * @param providers The tag providers 416 * @param name The name of the tag 417 * @param contextualParameters Contextual parameters transmitted by the environment. 418 * @return true if the tag exists 419 */ 420 protected boolean _hasTag(Set<TagProvider<? extends Tag>> providers, String name, Map<String, Object> contextualParameters) 421 { 422 for (TagProvider<? extends Tag> provider : providers) 423 { 424 if (provider.hasTag(name, contextualParameters)) 425 { 426 return true; 427 } 428 } 429 return false; 430 } 431}