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.cms.content;
017
018import java.io.IOException;
019import java.text.DateFormat;
020import java.text.SimpleDateFormat;
021import java.util.Locale;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.generation.Generator;
029import org.apache.cocoon.generation.ServiceableGenerator;
030import org.apache.cocoon.i18n.I18nUtils;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.lang.ArrayUtils;
033import org.apache.commons.lang3.StringUtils;
034import org.xml.sax.SAXException;
035
036import org.ametys.cms.contenttype.ContentType;
037import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
038import org.ametys.cms.contenttype.ContentTypesHelper;
039import org.ametys.cms.repository.Content;
040import org.ametys.runtime.model.View;
041import org.ametys.runtime.model.ViewHelper;
042
043/**
044 * {@link Generator} for rendering raw content data.
045 */
046public class ContentGenerator extends ServiceableGenerator
047{
048    /** The display date format. */
049    protected static final DateFormat _DC_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
050    
051    /** Content type extension point. */
052    protected ContentTypeExtensionPoint _contentTypeExtensionPoint;
053    /** Helper for content types */
054    protected ContentTypesHelper _cTypesHelper; 
055    /** The content saxer */
056    protected ContentSaxer _contentSaxer;
057    
058    @Override
059    public void service(ServiceManager serviceManager) throws ServiceException
060    {
061        super.service(serviceManager);
062        _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
063        _cTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE);
064        _contentSaxer = (ContentSaxer) serviceManager.lookup(ContentSaxer.CMS_CONTENT_SAXER_ROLE);
065    }
066    
067    public void generate() throws IOException, SAXException, ProcessingException
068    {
069        contentHandler.startDocument();
070        _generateContent();
071        contentHandler.endDocument();
072    }
073    
074    /**
075     * Generate the content (with the start/end document)
076     * @throws SAXException if an error occurs while SAXing
077     * @throws IOException if an error occurs
078     * @throws ProcessingException if an error occurs
079     */
080    protected void _generateContent() throws SAXException, IOException, ProcessingException
081    {
082        Request request = ObjectModelHelper.getRequest(objectModel);
083        Content content = (Content) request.getAttribute(Content.class.getName());
084        
085        // SAX the content
086        _saxContent(content, getDefaultLocale(request));
087    }
088    
089    /**
090     * Get the default locale to use to sax localized values
091     * @param request the request
092     * @return the default locale
093     */
094    protected Locale getDefaultLocale(Request request)
095    {
096        String lang = parameters.getParameter("lang", request.getParameter("lang"));
097        return StringUtils.isNotEmpty(lang) ? new Locale(lang) : I18nUtils.findLocale(objectModel, "locale", null, Locale.getDefault(), true);
098    }
099    
100    /**
101     * SAX the content 
102     * @param content The content to SAX
103     * @param defaultLocale The default locale to use to sax localized values if the content's language is null.
104     * @throws SAXException if an error occurs while SAXing
105     * @throws IOException if an error occurs
106     * @throws ProcessingException if an error occurs
107     */
108    protected void _saxContent (Content content, Locale defaultLocale) throws SAXException, IOException, ProcessingException
109    {
110        _contentSaxer.saxRootTag(content, contentHandler, defaultLocale, "content");
111        
112        boolean isEdition = parameters.getParameterAsBoolean("isEdition", false);
113
114        View view = _getView(content, isEdition);
115        Locale locale = content.getLanguage() != null ? new Locale(content.getLanguage()) : defaultLocale;
116        
117        // FIXME CMS-3057
118        Request request = ObjectModelHelper.getRequest(objectModel);
119        boolean displayWorkflow = !"true".equals(request.getParameter("ignore-workflow"));
120        
121        _contentSaxer.saxContent(content, contentHandler, locale, view, null, displayWorkflow, displayWorkflow, true, "metadata", isEdition);
122        
123        if (isEdition)
124        {
125            XMLUtils.startElement(contentHandler, "comments");
126            _saxAttributesComments(content, view);
127            XMLUtils.endElement(contentHandler, "comments");
128        }
129        
130        String[] cTypes = (String[]) ArrayUtils.addAll(content.getTypes(), content.getMixinTypes());
131        for (String cTypeId : cTypes)
132        {
133            ContentType cType = _contentTypeExtensionPoint.getExtension(cTypeId);
134            cType.saxContentTypeAdditionalData(contentHandler, content);
135        }
136        
137        _saxOtherData(content, defaultLocale);
138        
139        XMLUtils.endElement(contentHandler, "content");
140    }
141    
142    /**
143     * SAX content attributes comments.
144     * @param content the content.
145     * @param view the view containing the attributes.
146     * @throws SAXException if an error occurs while SAXing.
147     */
148    protected void _saxAttributesComments(Content content, View view) throws SAXException
149    {
150        content.commentsToSAX(contentHandler, view);
151    }
152    
153    /**
154     * SAX any other data needed by the view.<p>
155     * Default implementation does nothing.
156     * @param content the content.
157     * @throws SAXException if an error occurs while SAXing.
158     * @throws ProcessingException if an error occurs.
159     * @deprecated Use and/or override {@link #_saxOtherData(Content, Locale)} instead
160     */
161    @Deprecated
162    protected void _saxOtherData(Content content) throws SAXException, ProcessingException
163    {
164        // No other data to SAX
165    }
166    
167    /**
168     * SAX any other data needed by the view.<p>
169     * Default implementation does nothing.
170     * @param content the content.
171     * @param defaultLocale The default locale
172     * @throws SAXException if an error occurs while SAXing.
173     * @throws ProcessingException if an error occurs.
174     * @throws IOException if an error occurs.
175     */
176    protected void _saxOtherData(Content content, Locale defaultLocale) throws SAXException, ProcessingException, IOException
177    {
178        // For legacy purposes
179        _saxOtherData(content);
180    }
181    
182    /**
183     * Retrieves the view to be used when SAX'ing attributes and attribute comments.
184     * @param content The content to consider. Cannot be null.
185     * @param isEdition <code>true</code> if the view is to use for edition, <code>false</code> otherwise
186     * @return The retrieved view
187     * @throws ProcessingException If the view could not be retrieved
188     */
189    protected View _getView(Content content, boolean isEdition) throws ProcessingException
190    {
191        String fallbackViewName = parameters.getParameter("fallbackViewName", StringUtils.EMPTY);
192        String viewName = parameters.getParameter("viewName", StringUtils.EMPTY);
193        
194        View view = _cTypesHelper.getViewWithFallback(viewName, fallbackViewName, content);
195        if (view == null)
196        {
197            String errorMsg = String.format("Unknown view '%s' nor fallback view '%s' for content '%s'", viewName, fallbackViewName, content.getId());
198            getLogger().error(errorMsg);
199            throw new IllegalArgumentException(errorMsg);
200        }
201        
202        return isEdition ? ViewHelper.getTruncatedView(view) : ViewHelper.mergeDuplicatedItems(view); 
203    }
204}