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        boolean showDisableValues = parameters.getParameterAsBoolean("showDisableValues", false);
114
115        View view = _getView(content, isEdition);
116        Locale locale = content.getLanguage() != null ? new Locale(content.getLanguage()) : defaultLocale;
117        
118        // FIXME CMS-3057
119        Request request = ObjectModelHelper.getRequest(objectModel);
120        boolean displayWorkflow = !"true".equals(request.getParameter("ignore-workflow"));
121        
122        _contentSaxer.saxContent(content, contentHandler, locale, view, null, displayWorkflow, displayWorkflow, true, "metadata", isEdition, showDisableValues);
123        
124        if (isEdition)
125        {
126            XMLUtils.startElement(contentHandler, "comments");
127            _saxAttributesComments(content, view);
128            XMLUtils.endElement(contentHandler, "comments");
129        }
130        
131        String[] cTypes = (String[]) ArrayUtils.addAll(content.getTypes(), content.getMixinTypes());
132        for (String cTypeId : cTypes)
133        {
134            ContentType cType = _contentTypeExtensionPoint.getExtension(cTypeId);
135            cType.saxContentTypeAdditionalData(contentHandler, content);
136        }
137        
138        _saxOtherData(content, defaultLocale);
139        
140        XMLUtils.endElement(contentHandler, "content");
141    }
142    
143    /**
144     * SAX content attributes comments.
145     * @param content the content.
146     * @param view the view containing the attributes.
147     * @throws SAXException if an error occurs while SAXing.
148     */
149    protected void _saxAttributesComments(Content content, View view) throws SAXException
150    {
151        content.commentsToSAX(contentHandler, view);
152    }
153    
154    /**
155     * SAX any other data needed by the view.<p>
156     * Default implementation does nothing.
157     * @param content the content.
158     * @throws SAXException if an error occurs while SAXing.
159     * @throws ProcessingException if an error occurs.
160     * @deprecated Use and/or override {@link #_saxOtherData(Content, Locale)} instead
161     */
162    @Deprecated
163    protected void _saxOtherData(Content content) throws SAXException, ProcessingException
164    {
165        // No other data to SAX
166    }
167    
168    /**
169     * SAX any other data needed by the view.<p>
170     * Default implementation does nothing.
171     * @param content the content.
172     * @param defaultLocale The default locale
173     * @throws SAXException if an error occurs while SAXing.
174     * @throws ProcessingException if an error occurs.
175     * @throws IOException if an error occurs.
176     */
177    protected void _saxOtherData(Content content, Locale defaultLocale) throws SAXException, ProcessingException, IOException
178    {
179        // For legacy purposes
180        _saxOtherData(content);
181    }
182    
183    /**
184     * Retrieves the view to be used when SAX'ing attributes and attribute comments.
185     * @param content The content to consider. Cannot be null.
186     * @param isEdition <code>true</code> if the view is to use for edition, <code>false</code> otherwise
187     * @return The retrieved view
188     * @throws ProcessingException If the view could not be retrieved
189     */
190    protected View _getView(Content content, boolean isEdition) throws ProcessingException
191    {
192        String fallbackViewName = parameters.getParameter("fallbackViewName", StringUtils.EMPTY);
193        String viewName = parameters.getParameter("viewName", StringUtils.EMPTY);
194        
195        View view = _cTypesHelper.getViewWithFallback(viewName, fallbackViewName, content);
196        if (view == null)
197        {
198            String errorMsg = String.format("Unknown view '%s' nor fallback view '%s' for content '%s'", viewName, fallbackViewName, content.getId());
199            getLogger().error(errorMsg);
200            throw new IllegalArgumentException(errorMsg);
201        }
202        
203        return isEdition ? ViewHelper.getTruncatedView(view) : view; 
204    }
205}