001/*
002 *  Copyright 2020 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.odf.export.pdf;
017
018import java.io.IOException;
019import java.net.MalformedURLException;
020import java.util.HashMap;
021import java.util.LinkedHashSet;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.components.source.impl.SitemapSource;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.cocoon.environment.Request;
031import org.apache.cocoon.generation.ServiceableGenerator;
032import org.apache.cocoon.xml.AttributesImpl;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.commons.lang.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.repository.Content;
038import org.ametys.core.util.IgnoreRootHandler;
039import org.ametys.odf.NoLiveVersionException;
040import org.ametys.odf.ODFHelper;
041import org.ametys.odf.ProgramItem;
042import org.ametys.odf.course.Course;
043import org.ametys.odf.program.SubProgram;
044import org.ametys.odf.workflow.ValidateODFContentFunction;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.UnknownAmetysObjectException;
047import org.ametys.plugins.repository.jcr.DefaultAmetysObject;
048
049
050/**
051 * Generator producing the SAX of subprogram and its courses for the educational booklet 
052 */ 
053public class EducationalBookletGenerator extends ServiceableGenerator
054{
055    /** The Ametys object resolver */
056    protected AmetysObjectResolver _resolver;
057    
058    /** The ODF helper */
059    protected ODFHelper _odfHelper;
060    
061    @Override
062    public void service(ServiceManager sManager) throws ServiceException
063    {
064        super.service(sManager);
065        _resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
066        _odfHelper = (ODFHelper) sManager.lookup(ODFHelper.ROLE);
067    }
068    
069    public void generate() throws IOException, SAXException, ProcessingException
070    {
071        Request request = ObjectModelHelper.getRequest(objectModel);
072        request.setAttribute(ODFHelper.REQUEST_ATTRIBUTE_VALID_LABEL, true);
073        
074        contentHandler.startDocument();
075
076        Map parentContextParameters = (Map) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
077        if (parentContextParameters == null)
078        {
079            throw new ProcessingException("Missing parent context");
080        }
081
082        AttributesImpl attrs = new AttributesImpl();
083        String archiveDateAsString = (String) parentContextParameters.get("archiveDate");
084        if (StringUtils.isNotBlank(archiveDateAsString))
085        {
086            attrs.addCDATAAttribute("isValidated", "true");
087            attrs.addCDATAAttribute("archiveDate", archiveDateAsString);
088        }
089        else
090        {
091            attrs.addCDATAAttribute("isValidated", "false");
092        }
093
094        XMLUtils.startElement(contentHandler, "booklet", attrs);
095        
096        String subProgramId = (String) parentContextParameters.get("subProgramId");
097        Content content = _resolver.resolveById(subProgramId);
098        if (content instanceof SubProgram)
099        {
100            try
101            {
102                SubProgram subProgram = (SubProgram) content;
103                _odfHelper.switchToLiveVersion(subProgram);
104                _saxSubProgram(subProgram);
105                
106                for (Course course : _getChildCourses(subProgram))
107                {
108                    _saxCourse(course);
109                }
110            }
111            catch (NoLiveVersionException e) 
112            {
113                throw new IllegalArgumentException("The subprogram with id " + subProgramId + " has no live version.");
114            }
115        }
116        
117        XMLUtils.endElement(contentHandler, "booklet");
118        contentHandler.endDocument();
119    }
120    
121    /**
122     * Get all child courses from the program item
123     * @param programItem the program item
124     * @return the set of child courses
125     * @throws MalformedURLException if an error occurred
126     * @throws IOException if an error occurred
127     * @throws SAXException if an error occurred
128     */
129    protected Set<Course> _getChildCourses(ProgramItem programItem) throws MalformedURLException, IOException, SAXException
130    {
131        Set<Course> courses = new LinkedHashSet<>();
132        for (ProgramItem subProgramItem : _odfHelper.getChildProgramItems(programItem))
133        {
134            try
135            {
136                _odfHelper.switchToLiveVersion((DefaultAmetysObject) subProgramItem);
137                if (subProgramItem instanceof Course)
138                {
139                    courses.add((Course) subProgramItem);
140                }
141                courses.addAll(_getChildCourses(subProgramItem));
142            }
143            catch (NoLiveVersionException e) 
144            {
145                getLogger().warn("Cannot add the course to the educational booklet : Live label is required but there is no Live version for content " + subProgramItem.getId());
146            }
147        }
148        
149        return courses;
150    }
151    
152    /**
153     * Sax course as fo
154     * @param course the course
155     * @throws MalformedURLException if an error occurred
156     * @throws IOException if an error occurred
157     * @throws SAXException if an error occurred
158     */
159    protected void _saxCourse(Course course) throws MalformedURLException, IOException, SAXException
160    {
161        AttributesImpl attrs = new AttributesImpl();
162        attrs.addCDATAAttribute("id", course.getId());
163        attrs.addCDATAAttribute("name", course.getName());
164        attrs.addCDATAAttribute("title", course.getTitle());
165        
166        XMLUtils.startElement(contentHandler, "course", attrs);
167        _saxContentAsFo(course);
168        XMLUtils.endElement(contentHandler, "course");
169    }
170
171    /**
172     * Sax sub program as fo
173     * @param subProgram the sub program
174     * @throws MalformedURLException if an error occurred
175     * @throws IOException if an error occurred
176     * @throws SAXException if an error occurred
177     */
178    protected void _saxSubProgram(SubProgram subProgram) throws MalformedURLException, IOException, SAXException
179    {
180        AttributesImpl attrs = new AttributesImpl();
181        attrs.addCDATAAttribute("id", subProgram.getId());
182        attrs.addCDATAAttribute("name", subProgram.getName());
183        attrs.addCDATAAttribute("title", subProgram.getTitle());
184        
185        XMLUtils.startElement(contentHandler, "subProgram", attrs);
186        _saxContentAsFo(subProgram);
187        XMLUtils.endElement(contentHandler, "subProgram");
188    }
189    
190    /**
191     * Sax content as fo
192     * @param content the content
193     * @throws MalformedURLException if an error occurred
194     * @throws IOException if an error occurred
195     * @throws SAXException if an error occurred
196     */
197    protected void _saxContentAsFo(Content content) throws MalformedURLException, IOException, SAXException
198    {
199        XMLUtils.startElement(contentHandler, "fo");
200        SitemapSource src = null;      
201        
202        try
203        {
204            Map<String, Object> pdfParameters = new HashMap<>();
205            pdfParameters.put("versionLabel", ValidateODFContentFunction.VALID_LABEL);
206            
207            String uri = "cocoon://_plugins/odf/_content/" + content.getName() + ".fo";
208            src = (SitemapSource) resolver.resolveURI(uri, null, pdfParameters);
209            src.toSAX(new IgnoreRootHandler(contentHandler));
210        }
211        catch (UnknownAmetysObjectException e)
212        {
213            // The content may be archived
214        }
215        finally
216        {
217            resolver.release(src);
218        }
219        
220        XMLUtils.endElement(contentHandler, "fo");
221    }
222}