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