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