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.plugins.cart.generators;
017
018import java.io.IOException;
019import java.util.Collections;
020import java.util.Comparator;
021import java.util.Date;
022import java.util.List;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.ProcessingException;
027import org.apache.cocoon.environment.ObjectModelHelper;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.generation.ServiceableGenerator;
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.lang.ArrayUtils;
033import org.apache.commons.lang.StringUtils;
034import org.xml.sax.SAXException;
035
036import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.repository.WorkflowAwareContent;
039import org.ametys.core.user.CurrentUserProvider;
040import org.ametys.core.user.User;
041import org.ametys.core.user.UserIdentity;
042import org.ametys.core.user.UserManager;
043import org.ametys.core.util.DateUtils;
044import org.ametys.plugins.cart.Cart;
045import org.ametys.plugins.cart.CartElement;
046import org.ametys.plugins.cart.CartsDAO;
047import org.ametys.plugins.cart.ContentElement;
048import org.ametys.plugins.cart.QueryFromDirectoryElement;
049import org.ametys.plugins.repository.AmetysObjectResolver;
050import org.ametys.plugins.repository.AmetysRepositoryException;
051import org.ametys.plugins.repository.version.VersionAwareAmetysObject;
052import org.ametys.plugins.workflow.store.AmetysStep;
053import org.ametys.plugins.workflow.support.WorkflowHelper;
054import org.ametys.plugins.workflow.support.WorkflowProvider;
055import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
056import org.ametys.runtime.i18n.I18nizableText;
057
058import com.opensymphony.workflow.WorkflowException;
059import com.opensymphony.workflow.spi.Step;
060
061/**
062 * SAX a {@link Cart}
063 *
064 */
065public class CartElementsGenerator extends ServiceableGenerator
066{
067    /** The Ametys object resolver */
068    protected AmetysObjectResolver _resolver;
069    /** The workflow provider */
070    protected WorkflowProvider _workflowProvider;
071    /** The workflow helper */
072    protected WorkflowHelper _workflowHelper;
073    /** The current user provider */
074    protected CurrentUserProvider _userProvider;
075    /** The user manager */
076    protected UserManager _userManager;
077    /** The carts DAO */
078    protected CartsDAO _cartsDAO;
079    /** The content type extension point */
080    protected ContentTypeExtensionPoint _cTypeEP;
081    
082    @Override
083    public void service(ServiceManager smanager) throws ServiceException
084    {
085        super.service(smanager);
086        _cTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
087        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
088        _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE);
089        _workflowHelper = (WorkflowHelper) smanager.lookup(WorkflowHelper.ROLE);
090        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
091        _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
092        _cartsDAO = (CartsDAO) smanager.lookup(CartsDAO.ROLE);
093    }
094    
095    @Override
096    public void generate() throws IOException, SAXException, ProcessingException
097    {
098        Request request = ObjectModelHelper.getRequest(objectModel);
099        String id = request.getParameter("cartId");
100        
101        Cart cart = _resolver.resolveById(id);
102        
103        contentHandler.startDocument();
104        
105        UserIdentity user = _userProvider.getUser();
106        if (_cartsDAO.canRead(user, cart))
107        {
108            AttributesImpl attrs = new AttributesImpl();
109            attrs.addCDATAAttribute("id", cart.getId());
110            
111            XMLUtils.startElement(contentHandler, "cart", attrs);
112            
113            XMLUtils.createElement(contentHandler, "label", cart.getTitle());
114            XMLUtils.createElement(contentHandler, "description", cart.getDescription());
115            
116            List<CartElement> elements = cart.getElements();
117            for (CartElement elmt : elements)
118            {
119                _saxElement(elmt);
120            }
121            
122            XMLUtils.createElement(contentHandler, "total", String.valueOf(elements.size()));
123            
124            XMLUtils.endElement(contentHandler, "cart");
125        }
126        else
127        {
128            XMLUtils.createElement(contentHandler, "not-allowed");
129        }
130        
131        contentHandler.endDocument();
132    }
133    
134    /**
135     * SAX an element of the cart
136     * @param elmt The element to SAX
137     * @throws SAXException if something goes wrong when saxing the element
138     */
139    protected void _saxElement (CartElement elmt) throws SAXException
140    {
141        List<I18nizableText> groups = elmt.getGroups();
142        
143        for (I18nizableText group : groups)
144        {
145            AttributesImpl attrs = new AttributesImpl();
146            attrs.addCDATAAttribute("id", elmt.getId());
147            
148            XMLUtils.startElement(contentHandler, "cartElement", attrs);
149                    
150            elmt.getTitle().toSAX(contentHandler, "title");
151            elmt.getDescription().toSAX(contentHandler, "description");
152            group.toSAX(contentHandler, "group");
153            
154            XMLUtils.createElement(contentHandler, "creation", DateUtils.zonedDateTimeToString(elmt.getCreationDate()));
155            
156            UserIdentity creatorIdentity = elmt.getCreator();
157            User creator = _userManager.getUser(creatorIdentity);
158            XMLUtils.createElement(contentHandler, "creator", creator != null ? creator.getFullName() : StringUtils.EMPTY);
159            
160            XMLUtils.createElement(contentHandler, "lastModification", DateUtils.zonedDateTimeToString(elmt.getLastModified()));
161            
162            UserIdentity lastContributorIdentity = elmt.getLastContributor();
163            User lastContributor = _userManager.getUser(lastContributorIdentity);
164            XMLUtils.createElement(contentHandler, "lastContributor", lastContributor != null ? lastContributor.getFullName() : StringUtils.EMPTY);
165            
166            String glyphIcon = elmt.getGlyphIcon();
167            if (glyphIcon != null)
168            {
169                XMLUtils.createElement(contentHandler, "iconGlyph", glyphIcon);
170            }
171            
172            String smallIcon = elmt.getSmallIcon();
173            if (smallIcon != null)
174            {
175                XMLUtils.createElement(contentHandler, "smallIcon", smallIcon);
176            }
177            String mediumIcon = elmt.getSmallIcon();
178            if (mediumIcon != null)
179            {
180                XMLUtils.createElement(contentHandler, "mediumIcon", mediumIcon);
181            }
182            
183            XMLUtils.createElement(contentHandler, "type", elmt.getType());
184            
185            if (elmt instanceof ContentElement)
186            {
187                Content content = _resolver.resolveById(elmt.getId());
188                _saxWorkflowStep(content);
189            }
190            else if (elmt instanceof QueryFromDirectoryElement queryElement)
191            {
192                XMLUtils.createElement(contentHandler, "documentation", queryElement.getQuery().getDocumentation());
193                XMLUtils.createElement(contentHandler, "queryType", queryElement.getQuery().getType());
194            }
195            
196            XMLUtils.endElement(contentHandler, "cartElement");
197        }
198    }
199
200    /**
201     * SAX the workflow step if the content is a <code>WorkflowAwareContent</code>
202     * @param content The content
203     * @throws SAXException if an error occurs while SAXing.
204     */
205    protected void _saxWorkflowStep (Content content) throws SAXException
206    {
207        if (content instanceof WorkflowAwareContent)
208        {
209            WorkflowAwareContent waContent = (WorkflowAwareContent) content;
210            
211            try
212            {
213                AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent);
214                long workflowId = waContent.getWorkflowId();
215                String workflowName = workflow.getWorkflowName(workflowId);
216                
217                Step currentStep = _getCurrentStep(waContent, workflow);
218                
219                int currentStepId = currentStep.getStepId();
220                
221                I18nizableText workflowStepName = new I18nizableText("application",  _workflowHelper.getStepName(workflowName, currentStepId));
222                
223                AttributesImpl atts = new AttributesImpl();
224                atts.addAttribute("", "id", "id", "CDATA", String.valueOf(currentStepId));
225                if ("application".equals(workflowStepName.getCatalogue()))
226                {
227                    atts.addAttribute("", "icon-small", "icon-small", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-small.png");
228                    atts.addAttribute("", "icon-medium", "icon-medium", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-medium.png");
229                    atts.addAttribute("", "icon-large", "icon-large", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-large.png");
230                }
231                else
232                {
233                    String pluginName = workflowStepName.getCatalogue().substring("plugin.".length());
234                    atts.addAttribute("", "icon-small", "icon-small", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-small.png");
235                    atts.addAttribute("", "icon-medium", "icon-medium", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-medium.png");
236                    atts.addAttribute("", "icon-large", "icon-large", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-large.png");
237                }
238                
239                XMLUtils.startElement(contentHandler, "workflow-step", atts);
240                workflowStepName.toSAX(contentHandler);
241                XMLUtils.endElement(contentHandler, "workflow-step");
242            }
243            catch (AmetysRepositoryException e)
244            {
245                // Current step id was not positioned
246            }
247            catch (WorkflowException e)
248            {
249                // Ignore, just don't SAX the workflow step.
250            }
251        }
252    }
253    
254    /**
255     * Get a content's step, wherever it works on the base version or not.
256     * @param content the content.
257     * @param workflow the workflow instance bound to this content
258     * @return the content's workflow step.
259     * @throws WorkflowException if an error occurs.
260     */
261    protected Step _getCurrentStep(WorkflowAwareContent content, AmetysObjectWorkflow workflow) throws WorkflowException
262    {
263        long workflowId = content.getWorkflowId();
264        Step currentStep = (Step) workflow.getCurrentSteps(workflowId).get(0);
265        
266        if (content instanceof VersionAwareAmetysObject)
267        {
268            VersionAwareAmetysObject vaContent = (VersionAwareAmetysObject) content;
269            String currentRevision = vaContent.getRevision();
270            
271            if (currentRevision != null)
272            {
273                
274                String[] allRevisions = vaContent.getAllRevisions();
275                int currentRevIndex = ArrayUtils.indexOf(allRevisions, currentRevision);
276                
277                if (currentRevIndex > -1 && currentRevIndex < (allRevisions.length - 1))
278                {
279                    String nextRevision = allRevisions[currentRevIndex + 1];
280                    
281                    Date currentRevTimestamp = vaContent.getRevisionTimestamp();
282                    Date nextRevTimestamp = vaContent.getRevisionTimestamp(nextRevision);
283                    
284                    // Get all steps between the two revisions.
285                    List<Step> steps = _workflowHelper.getStepsBetween(workflow, workflowId, currentRevTimestamp, nextRevTimestamp);
286                    
287                    // In the old workflow structure
288                    // We take the second, which is current revision's last step.
289                    if (steps.size() > 0 && steps.get(0) instanceof AmetysStep)
290                    {
291                        AmetysStep amStep = (AmetysStep) steps.get(0);
292                        if (amStep.getProperty("actionFinishDate") != null)
293                        {
294                            // New workflow structure detected: cut the first workflow step
295                            // in the list, as it belongs to the next version.
296                            steps = steps.subList(1, steps.size());
297                        }
298                    }
299                    
300                    // Order by step descendant.
301                    Collections.sort(steps, new Comparator<Step>()
302                    {
303                        public int compare(Step step1, Step step2)
304                        {
305                            return -Long.valueOf(step1.getId()).compareTo(step2.getId());
306                        }
307                    });
308                    
309                    // The first step in the list is the current version's last workflow step.
310                    if (steps.size() > 0)
311                    {
312                        currentStep = steps.get(0);
313                    }
314                }
315            }
316        }
317        return currentStep;
318    }
319}