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