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}