001/* 002 * Copyright 2010 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.newsletter.category; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Set; 023 024import javax.jcr.RepositoryException; 025 026import org.apache.avalon.framework.configuration.Configurable; 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.configuration.ConfigurationException; 029import org.apache.avalon.framework.logger.LogEnabled; 030import org.apache.avalon.framework.logger.Logger; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034import org.apache.commons.lang.StringUtils; 035 036import org.ametys.cms.repository.Content; 037import org.ametys.cms.repository.ContentTypeExpression; 038import org.ametys.plugins.repository.AmetysObject; 039import org.ametys.plugins.repository.AmetysObjectIterable; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.plugins.repository.AmetysRepositoryException; 042import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 043import org.ametys.plugins.repository.TraversableAmetysObject; 044import org.ametys.plugins.repository.UnknownAmetysObjectException; 045import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject; 046import org.ametys.plugins.repository.jcr.JCRAmetysObject; 047import org.ametys.plugins.repository.query.expression.AndExpression; 048import org.ametys.plugins.repository.query.expression.Expression; 049import org.ametys.plugins.repository.query.expression.Expression.Operator; 050import org.ametys.plugins.repository.query.expression.StringExpression; 051import org.ametys.runtime.i18n.I18nizableText; 052import org.ametys.runtime.plugin.component.PluginAware; 053import org.ametys.web.repository.site.SiteManager; 054 055/** 056 * Class representing a JCR categories provider 057 */ 058public class JCRCategoryProvider implements LogEnabled, CategoryProvider, Serviceable, Configurable, PluginAware 059{ 060 /** The id */ 061 protected String _id; 062 /** The label */ 063 protected I18nizableText _label; 064 /** The description */ 065 protected I18nizableText _description; 066 /** The plugin name */ 067 protected String _pluginName; 068 /** The feature name */ 069 protected String _featureName; 070 /** The Logger */ 071 protected Logger _logger; 072 073 private AmetysObjectResolver _resolver; 074 private SiteManager _siteManager; 075 076 @Override 077 public void service(ServiceManager serviceManager) throws ServiceException 078 { 079 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 080 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 081 } 082 083 public void configure(Configuration configuration) throws ConfigurationException 084 { 085 _label = configureLabel(configuration); 086 _description = configureDescription(configuration); 087 } 088 089 public void enableLogging(Logger logger) 090 { 091 _logger = logger; 092 } 093 094 @Override 095 public boolean isWritable() 096 { 097 return true; 098 } 099 100 @Override 101 public List<Category> getCategories(String siteName, String lang) 102 { 103 if (StringUtils.isEmpty(siteName)) 104 { 105 return null; 106 } 107 108 List<Category> categories = new ArrayList<>(); 109 110 try 111 { 112 DefaultTraversableAmetysObject rootNode = (DefaultTraversableAmetysObject) _getRootNode(siteName, lang); 113 114 AmetysObjectIterable<AmetysObject> it = rootNode.getChildren(); 115 for (AmetysObject object : it) 116 { 117 if (object instanceof JCRCategory) 118 { 119 JCRCategory jcrCategory = (JCRCategory) object; 120 Category category = new Category(jcrCategory.getId(), jcrCategory.getName(), rootNode.getId(), new I18nizableText(jcrCategory.getTitle()), new I18nizableText(jcrCategory.getDescription()), jcrCategory.getTemplate(), siteName, lang); 121 122 categories.add(category); 123 } 124 } 125 } 126 catch (RepositoryException e) 127 { 128 _logger.error("Unable to list the newsletter categories for site " + siteName + " and language " + lang, e); 129 } 130 131 return categories; 132 } 133 134 @Override 135 public Collection<Category> getAllCategories(String siteName, String lang) 136 { 137 if (StringUtils.isEmpty(siteName)) 138 { 139 return null; 140 } 141 142 Set<Category> categories = new HashSet<>(); 143 144 try 145 { 146 TraversableAmetysObject rootNode = _getRootNode(siteName, lang); 147 148 // Recursively add categories from the root node. 149 addCategories(categories, rootNode, siteName, lang); 150 } 151 catch (RepositoryException e) 152 { 153 _logger.error("Unable to list the newsletter categories for site " + siteName + " and language " + lang, e); 154 } 155 156 return categories; 157 } 158 159 /** 160 * Recursively find {@link JCRCategory} objects in the given TraversableAmetysObject. 161 * @param categories the Set to fill with categories. 162 * @param parentObject the parent {@link TraversableAmetysObject}. 163 * @param siteName the site name. 164 * @param lang the language. 165 */ 166 protected void addCategories(Set<Category> categories, TraversableAmetysObject parentObject, String siteName, String lang) 167 { 168 AmetysObjectIterable<AmetysObject> it = parentObject.getChildren(); 169 for (AmetysObject object : it) 170 { 171 if (object instanceof JCRCategory) 172 { 173 JCRCategory jcrCategory = (JCRCategory) object; 174 Category category = new Category(jcrCategory.getId(), jcrCategory.getName(), parentObject.getId(), new I18nizableText(jcrCategory.getTitle()), new I18nizableText(jcrCategory.getDescription()), jcrCategory.getTemplate(), siteName, lang); 175 176 categories.add(category); 177 178 // 179 addCategories(categories, jcrCategory, siteName, lang); 180 } 181 } 182 } 183 184 @Override 185 public Category getCategory(String categoryID) 186 { 187 try 188 { 189 JCRCategory category = _resolver.resolveById(categoryID); 190 191 AmetysObject parent = category.getParent(); 192 String parentId = parent.getId(); 193 if (!(parent instanceof JCRCategory)) 194 { 195 parentId = "provider_" + getId(); 196 } 197 198 return new Category(category.getId(), category.getName(), parentId, new I18nizableText(category.getTitle()), new I18nizableText(category.getDescription()), category.getTemplate(), category.getSiteName(), category.getLang()); 199 } 200 catch (UnknownAmetysObjectException e) 201 { 202 _logger.warn("Unable to retrieve newsletter category of id " + categoryID, e); 203 return null; 204 } 205 } 206 207 @Override 208 public void setTemplate(Category category, String templateName) 209 { 210 JCRCategory jcrCategory = _resolver.resolveById(category.getId()); 211 jcrCategory.setTemplate(templateName); 212 jcrCategory.saveChanges(); 213 } 214 215 @Override 216 public Collection<String> getAutomaticIds(String categoryId) 217 { 218 JCRCategory jcrCategory = _resolver.resolveById(categoryId); 219 return jcrCategory.getAutomaticIds(); 220 } 221 222 @Override 223 public void setAutomatic(String categoryId, Collection<String> automaticNewsletterIds) 224 { 225 JCRCategory jcrCategory = _resolver.resolveById(categoryId); 226 jcrCategory.setAutomaticIds(automaticNewsletterIds); 227 jcrCategory.saveChanges(); 228 } 229 230 @Override 231 public boolean hasCategory(String categoryID) 232 { 233 try 234 { 235 AmetysObject object = _resolver.resolveById(categoryID); 236 return object instanceof JCRCategory; 237 } 238 catch (UnknownAmetysObjectException e) 239 { 240 _logger.debug("Unable to retrieve newsletter category of id " + categoryID, e); 241 return false; 242 } 243 catch (AmetysRepositoryException e) 244 { 245 _logger.debug("Unable to retrieve newsletter category of id " + categoryID, e); 246 return false; 247 } 248 } 249 250 @Override 251 public List<Category> getCategories(String categoryID) 252 { 253 List<Category> categories = new ArrayList<>(); 254 255 try 256 { 257 JCRCategory category = _resolver.resolveById(categoryID); 258 AmetysObjectIterable<AmetysObject> it = category.getChildren(); 259 for (AmetysObject object : it) 260 { 261 if (object instanceof JCRCategory) 262 { 263 JCRCategory jcrCategory = (JCRCategory) object; 264 Category child = new Category(jcrCategory.getId(), jcrCategory.getName(), category.getId(), new I18nizableText(jcrCategory.getTitle()), new I18nizableText(jcrCategory.getDescription()), jcrCategory.getTemplate(), jcrCategory.getSiteName(), jcrCategory.getLang()); 265 266 categories.add(child); 267 } 268 } 269 } 270 catch (AmetysRepositoryException e) 271 { 272 _logger.error("Unable to list the newsletter categories for category of id " + categoryID, e); 273 } 274 275 276 return categories; 277 } 278 279 280 @Override 281 public boolean hasChildren(String categoryID) 282 { 283 try 284 { 285 JCRCategory category = _resolver.resolveById(categoryID); 286 return category.getChildren().iterator().hasNext(); 287 } 288 catch (AmetysRepositoryException e) 289 { 290 _logger.error("Unable to retrieve newsletter category of id " + categoryID, e); 291 return false; 292 } 293 294 } 295 296 @Override 297 public AmetysObjectIterable<Content> getNewsletters(String categoryID, String siteName, String lang) 298 { 299 Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, "org.ametys.plugins.newsletter.Content.newsletter"); 300 Expression siteExpr = new StringExpression("site", Operator.EQ, siteName); 301 Expression catExpr = new StringExpression("category", Operator.EQ, categoryID); 302 Expression expr = new AndExpression(cTypeExpr, siteExpr, catExpr); 303 304 String xpathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr); 305 return _resolver.query(xpathQuery); 306 } 307 308 @Override 309 public boolean hasNewsletters(String categoryID, String siteName, String lang) 310 { 311 Expression expr = new ContentTypeExpression(Operator.EQ, "org.ametys.plugins.newsletter.Content.newsletter"); 312 Expression siteExpr = new StringExpression("site", Operator.EQ, siteName); 313 expr = new AndExpression(expr, siteExpr); 314 Expression catExpr = new StringExpression("category", Operator.EQ, categoryID); 315 expr = new AndExpression(expr, catExpr); 316 317 String xpathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr); 318 319 return _resolver.query(xpathQuery).iterator().hasNext(); 320 } 321 322 @Override 323 public I18nizableText getLabel() 324 { 325 return _label; 326 } 327 328 @Override 329 public I18nizableText getDescription() 330 { 331 return _description; 332 } 333 334 @Override 335 public String getId() 336 { 337 return _id; 338 } 339 340 /** 341 * Configure label from the passed configuration 342 * @param configuration The configuration 343 * @return The label 344 * @throws ConfigurationException If an error occurred 345 */ 346 protected I18nizableText configureLabel (Configuration configuration) throws ConfigurationException 347 { 348 Configuration labelConfiguration = configuration.getChild("label"); 349 350 if (labelConfiguration.getAttributeAsBoolean("i18n", false)) 351 { 352 return new I18nizableText("plugin." + _pluginName, labelConfiguration.getValue("")); 353 } 354 else 355 { 356 return new I18nizableText(labelConfiguration.getValue("")); 357 } 358 } 359 360 /** 361 * Configure description from the passed configuration 362 * @param configuration The configuration 363 * @return The description 364 * @throws ConfigurationException If an error occurred 365 */ 366 protected I18nizableText configureDescription (Configuration configuration) throws ConfigurationException 367 { 368 Configuration descConfiguration = configuration.getChild("description"); 369 370 if (descConfiguration.getAttributeAsBoolean("i18n", false)) 371 { 372 return new I18nizableText("plugin." + _pluginName, descConfiguration.getValue("")); 373 } 374 else 375 { 376 return new I18nizableText(descConfiguration.getValue("")); 377 } 378 } 379 380 @Override 381 public void setPluginInfo(String pluginName, String featureName, String id) 382 { 383 _pluginName = pluginName; 384 _featureName = featureName; 385 _id = id; 386 } 387 388 /** 389 * Get the plugin name 390 * @return the plugin name 391 */ 392 public String getPluginName() 393 { 394 return _pluginName; 395 } 396 397 @Override 398 public String getRootId(String siteName, String lang) 399 { 400 try 401 { 402 return _getRootNode(siteName, lang).getId(); 403 } 404 catch (RepositoryException e) 405 { 406 _logger.error("Unable to retrieve the root node of newsletter categories for site " + siteName + " and language " + lang, e); 407 return null; 408 } 409 } 410 411 412 private TraversableAmetysObject _getRootNode (String sitename, String lang) throws RepositoryException 413 { 414 ModifiableTraversableAmetysObject pluginsNode = _siteManager.getSite(sitename).getRootPlugins(); 415 416 ModifiableTraversableAmetysObject categoriesNode = null; 417 if (!pluginsNode.hasChild("newsletter")) 418 { 419 categoriesNode = ((ModifiableTraversableAmetysObject) pluginsNode.createChild("newsletter", "ametys:unstructured")).createChild("ametys:categories", "ametys:unstructured"); 420 } 421 else 422 { 423 categoriesNode = pluginsNode.getChild("newsletter/ametys:categories"); 424 } 425 426 if (!categoriesNode.hasChild(lang)) 427 { 428 categoriesNode.createChild(lang, "ametys:unstructured"); 429 ((JCRAmetysObject) pluginsNode).getNode().getSession().save(); 430 } 431 432 return pluginsNode.getChild("newsletter/ametys:categories/" + lang); 433 } 434 435}