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.web.workflow; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Iterator; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.components.ContextHelper; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.XMLUtils; 034import org.apache.commons.lang.StringUtils; 035import org.xml.sax.ContentHandler; 036import org.xml.sax.SAXException; 037 038import org.ametys.cms.repository.Content; 039import org.ametys.cms.repository.ContentLanguageExpression; 040import org.ametys.cms.repository.ContentTypeExpression; 041import org.ametys.cms.repository.DefaultContent; 042import org.ametys.cms.repository.WorkflowStepExpression; 043import org.ametys.core.user.User; 044import org.ametys.plugins.repository.AmetysObjectIterable; 045import org.ametys.plugins.repository.AmetysRepositoryException; 046import org.ametys.plugins.repository.query.SortCriteria; 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.OrExpression; 051import org.ametys.plugins.repository.query.expression.StringExpression; 052import org.ametys.plugins.repository.query.expression.UserExpression; 053import org.ametys.web.repository.content.WebContent; 054import org.ametys.web.repository.page.ContentTypesAssignmentHandler; 055import org.ametys.web.repository.page.Page; 056import org.ametys.web.repository.site.Site; 057import org.ametys.web.repository.site.SiteManager; 058 059/** 060 * Component for saxing tasks specific to an user.<br> 061 * The algorithm is the following : 062 * <ul> 063 * <li>First, we get all granted sites for the user with the right manager. 064 * <li>If there is at least one site allowed, we get all workflows 065 * associated with the granted sites. 066 * <li>Then for each step of each task from the configuration, we get 067 * all workflows where this step is in current steps and where 068 * the workflow is contains in the previous list. 069 * <li>For each workflow matching the previous conditions we 070 * test if the user has all the rights associated with the step (from 071 * the configuration) and then we get the content from this workflow. 072 * <li>Finally, for each content, we sax its first page in order to access 073 * it directly from an URL. 074 * </ul> 075 */ 076public class WorkflowTasksComponent extends org.ametys.cms.workflow.WorkflowTasksComponent implements Contextualizable 077{ 078 079 /** The avalon role. */ 080 @SuppressWarnings("hiding") 081 public static final String ROLE = WorkflowTasksComponent.class.getName(); 082 083 /** The site manager. */ 084 protected SiteManager _siteManager; 085 086 /** The avalon context. */ 087 protected Context _context; 088 089 /** The content types assignment handler */ 090 protected ContentTypesAssignmentHandler _cTypeHandler; 091 092 @Override 093 public void service(ServiceManager serviceManager) throws ServiceException 094 { 095 super.service(serviceManager); 096 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 097 _cTypeHandler = (ContentTypesAssignmentHandler) serviceManager.lookup(ContentTypesAssignmentHandler.ROLE); 098 } 099 100 @Override 101 public void contextualize(Context context) throws ContextException 102 { 103 _context = context; 104 } 105 106 @Override 107 public void toSAX(ContentHandler ch, User user) throws SAXException 108 { 109 XMLUtils.startElement(ch, "tasks"); 110 111 long start = System.currentTimeMillis(); 112 113 Set<String> grantedSites = _siteManager.getGrantedSites(user.getIdentity()); 114 long gsEnd = System.currentTimeMillis(); 115 long wfEnd = 0; 116 117 Request request = ContextHelper.getRequest(_context); 118 for (String siteName : grantedSites) 119 { 120 Site site = _siteManager.getSite(siteName); 121 122 request.setAttribute("siteName", siteName); 123 124 AttributesImpl siteAttr = new AttributesImpl(); 125 siteAttr.addCDATAAttribute("name", siteName); 126 siteAttr.addCDATAAttribute("title", site.getTitle()); 127 siteAttr.addCDATAAttribute("siteContext", "/" + siteName); 128 129 XMLUtils.startElement(ch, "site", siteAttr); 130 131 long wfStart = System.currentTimeMillis(); 132 // Get the workflow corresponding to the configuration. 133 Map<Task, Collection<Content>> workflows = _getCorrespondingWorkflows(user); 134 wfEnd = System.currentTimeMillis(); 135 136 getLogger().info("Contents retrieved in " + (wfEnd - wfStart) + "ms for site '" + siteName + "'"); 137 138 for (Task task : workflows.keySet()) 139 { 140 _saxTask(ch, task, workflows.get(task)); 141 } 142 143 XMLUtils.endElement(ch, "site"); 144 } 145 146 long end = System.currentTimeMillis(); 147 148 // Display performance indicators. 149 AttributesImpl attrs = new AttributesImpl(); 150 151 attrs.addCDATAAttribute("Total", Long.toString(end - start)); 152 attrs.addCDATAAttribute("GS", Long.toString(gsEnd - start)); 153 attrs.addCDATAAttribute("WF", Long.toString(wfEnd - gsEnd)); 154 attrs.addCDATAAttribute("SAX", Long.toString(end - wfEnd)); 155 156 XMLUtils.createElement(ch, "render", attrs); 157 158 XMLUtils.endElement(ch, "tasks"); 159 } 160 161 @Override 162 public void toSAX(ContentHandler ch, User user, String taskId) throws SAXException 163 { 164 XMLUtils.startElement(ch, "tasks"); 165 166 long start = System.currentTimeMillis(); 167 168 Set<String> grantedSites = _siteManager.getGrantedSites(user.getIdentity()); 169 long gsEnd = System.currentTimeMillis(); 170 long wfEnd = 0; 171 172 Request request = ContextHelper.getRequest(_context); 173 for (String siteName : grantedSites) 174 { 175 Site site = _siteManager.getSite(siteName); 176 177 request.setAttribute("siteName", siteName); 178 179 AttributesImpl siteAttr = new AttributesImpl(); 180 siteAttr.addCDATAAttribute("name", siteName); 181 siteAttr.addCDATAAttribute("title", site.getTitle()); 182 siteAttr.addCDATAAttribute("siteContext", "/" + siteName); 183 184 XMLUtils.startElement(ch, "site", siteAttr); 185 186 long wfStart = System.currentTimeMillis(); 187 188 Task task = _tasks.get(taskId); 189 Collection<Content> contents = _getTaskContents(user, task); 190 191 wfEnd = System.currentTimeMillis(); 192 getLogger().info("Contents retrieved in " + (wfEnd - wfStart) + "ms for site '" + siteName + "'"); 193 194 _saxTask(ch, task, contents); 195 196 XMLUtils.endElement(ch, "site"); 197 } 198 199 long end = System.currentTimeMillis(); 200 201 // Display performance indicators. 202 AttributesImpl attrs = new AttributesImpl(); 203 204 attrs.addCDATAAttribute("Total", Long.toString(end - start)); 205 attrs.addCDATAAttribute("GS", Long.toString(gsEnd - start)); 206 attrs.addCDATAAttribute("WF", Long.toString(wfEnd - gsEnd)); 207 attrs.addCDATAAttribute("SAX", Long.toString(end - wfEnd)); 208 209 XMLUtils.createElement(ch, "render", attrs); 210 211 XMLUtils.endElement(ch, "tasks"); 212 } 213 214 /** 215 * SAX the contents for given user, site and task 216 * @param ch the content handler to SAX into 217 * @param user the user 218 * @param siteName the site name 219 * @param language the language. 220 * @param taskId the task id 221 * @throws SAXException if an error occurred while saxing 222 */ 223 public void toSAX(ContentHandler ch, User user, String siteName, String language, String taskId) throws SAXException 224 { 225 XMLUtils.startElement(ch, "tasks"); 226 227 long start = System.currentTimeMillis(); 228 long wfEnd = 0; 229 230 Request request = ContextHelper.getRequest(_context); 231 Site site = _siteManager.getSite(siteName); 232 233 request.setAttribute("siteName", siteName); 234 request.setAttribute("language", language); 235 236 AttributesImpl siteAttr = new AttributesImpl(); 237 siteAttr.addCDATAAttribute("name", siteName); 238 siteAttr.addCDATAAttribute("title", site.getTitle()); 239 siteAttr.addCDATAAttribute("siteContext", "/" + siteName); 240 if (language != null) 241 { 242 siteAttr.addCDATAAttribute("language", language); 243 } 244 245 XMLUtils.startElement(ch, "site", siteAttr); 246 247 long wfStart = System.currentTimeMillis(); 248 249 if (taskId != null) 250 { 251 Task task = _tasks.get(taskId); 252 Collection<Content> contents = _getTaskContents(user, task); 253 254 wfEnd = System.currentTimeMillis(); 255 256 if (getLogger().isInfoEnabled()) 257 { 258 getLogger().info("Contents retrieved in " + (wfEnd - wfStart) + "ms for site '" + siteName + "'"); 259 } 260 261 _saxTask(ch, task, contents); 262 } 263 else 264 { 265 Map<Task, Collection<Content>> workflows = _getCorrespondingWorkflows(user); 266 wfEnd = System.currentTimeMillis(); 267 268 if (getLogger().isInfoEnabled()) 269 { 270 getLogger().info("Contents retrieved in " + (wfEnd - wfStart) + "ms for site '" + siteName + "'"); 271 } 272 273 for (Task task : workflows.keySet()) 274 { 275 _saxTask(ch, task, workflows.get(task)); 276 } 277 } 278 279 XMLUtils.endElement(ch, "site"); 280 281 long end = System.currentTimeMillis(); 282 283 // Display performance indicators. 284 AttributesImpl attrs = new AttributesImpl(); 285 286 attrs.addCDATAAttribute("Total", Long.toString(end - start)); 287 attrs.addCDATAAttribute("WF", Long.toString(wfEnd - start)); 288 attrs.addCDATAAttribute("SAX", Long.toString(end - wfEnd)); 289 290 XMLUtils.createElement(ch, "render", attrs); 291 292 XMLUtils.endElement(ch, "tasks"); 293 } 294 295 /** 296 * Get the list of contents for a given user, task and site. 297 * @param user the user. 298 * @param taskId the task ID. 299 * @param siteName the site name. 300 * @param language the language. 301 * @param limit the maximum number of results, 0 for all. 302 * @return the contents as an iterable collection of contents. 303 * @throws AmetysRepositoryException if an error occurred 304 */ 305 public Collection<Content> getContents(User user, String taskId, String siteName, String language, int limit) throws AmetysRepositoryException 306 { 307 Request request = ContextHelper.getRequest(_context); 308 request.setAttribute("siteName", siteName); 309 request.setAttribute("language", siteName); 310 311 return super.getContents(user, taskId, limit); 312 } 313 314 @Override 315 protected AmetysObjectIterable<Content> _getContents(TaskStep step, User user) 316 { 317 Expression expr = null; 318 Set<Integer> stepIds = step.getStepIds(); 319 for (Integer stepId : stepIds) 320 { 321 Expression stepExpr = new WorkflowStepExpression(Operator.EQ, stepId); 322 expr = expr == null ? stepExpr : new OrExpression(expr, stepExpr); 323 } 324 325 if (step.getUserContentsOnly()) 326 { 327 Expression userExpr = new UserExpression(DefaultContent.METADATA_CONTRIBUTOR, Operator.EQ, user.getIdentity()); 328 expr = new AndExpression(expr, userExpr); 329 } 330 331 Request request = ContextHelper.getRequest(_context); 332 String siteName = (String) request.getAttribute("siteName"); 333 StringExpression siteExpr = new StringExpression("site", Operator.EQ, siteName); 334 expr = new AndExpression(expr, siteExpr); 335 336 Expression cTypeExpr = _getContentTypeExpression (siteName); 337 if (cTypeExpr != null) 338 { 339 expr = new AndExpression(expr, cTypeExpr); 340 } 341 342 String language = (String) request.getAttribute("language"); 343 if (StringUtils.isNotEmpty(language)) 344 { 345 expr = new AndExpression(expr, new ContentLanguageExpression(Operator.EQ, language)); 346 } 347 348 SortCriteria sort = new SortCriteria(); 349 sort.addCriterion(DefaultContent.METADATA_MODIFIED, false, false); 350 String xpathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, sort); 351 352 return _objectResolver.query(xpathQuery); 353 } 354 355 /** 356 * The content type expression 357 * @param siteName the site name 358 * @return The content type expression 359 */ 360 protected Expression _getContentTypeExpression (String siteName) 361 { 362 Site site = _siteManager.getSite(siteName); 363 364 List<Expression> cTypeExprs = new ArrayList<>(); 365 366 Set<String> cTypeIds = _cTypeHandler.getAvailableContentTypes(site, false); 367 for (String cTypeId : cTypeIds) 368 { 369 cTypeExprs.add(new ContentTypeExpression(Operator.EQ, cTypeId)); 370 } 371 372 if (cTypeIds.size() == 0) 373 { 374 return null; 375 } 376 377 return new OrExpression(cTypeExprs.toArray(new Expression[cTypeExprs.size()])); 378 } 379 380 @Override 381 protected void _saxAdditionalAttributes(Content content, AttributesImpl attrs) throws SAXException 382 { 383 if (content instanceof WebContent) 384 { 385 WebContent webContent = (WebContent) content; 386 Iterator<Page> pages = webContent.getReferencingPages().iterator(); 387 if (pages.hasNext()) 388 { 389 Page page = pages.next(); 390 attrs.addCDATAAttribute("pageTitle", page.getTitle()); 391 attrs.addCDATAAttribute("pageUri", page.getSitemapName() + "/" + page.getPathInSitemap()); 392 attrs.addCDATAAttribute("pageId", page.getId()); 393 } 394 } 395 } 396}