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