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 */
016
017package org.ametys.cms.content;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.environment.ObjectModelHelper;
029import org.apache.cocoon.generation.ServiceableGenerator;
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.lang3.StringUtils;
033import org.xml.sax.SAXException;
034
035import org.ametys.cms.content.references.OutgoingReferences;
036import org.ametys.cms.contenttype.ContentTypesHelper;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.transformation.ConsistencyChecker;
039import org.ametys.cms.transformation.ConsistencyChecker.CHECK;
040import org.ametys.cms.transformation.ConsistencyChecker.CheckReport;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.plugins.repository.UnknownAmetysObjectException;
043import org.ametys.plugins.repository.data.type.ModelItemTypeConstants;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.runtime.model.ModelHelper;
046import org.ametys.runtime.model.ModelItem;
047import org.ametys.runtime.model.exception.UndefinedItemPathException;
048
049/**
050 * Generates the consistency report for the given content
051 */
052public class ConsistencyGenerator extends ServiceableGenerator
053{
054    /** Repository content */
055    protected AmetysObjectResolver _resolver;
056    /** The consistency checker */
057    protected ConsistencyChecker _consistencyChecker;
058    /** The content types helper */
059    protected ContentTypesHelper _contentTypesHelper;
060    /** The content helper */
061    protected ContentHelper _contentHelper;
062    
063    @Override
064    public void service(ServiceManager smanager) throws ServiceException
065    {
066        super.service(smanager);
067        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
068        _consistencyChecker = (ConsistencyChecker) smanager.lookup(ConsistencyChecker.ROLE);
069        _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
070        _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE);
071    }
072
073    @Override
074    public void generate() throws IOException, SAXException, ProcessingException
075    {
076        contentHandler.startDocument();
077        XMLUtils.startElement(contentHandler, "contents");
078        
079        @SuppressWarnings("unchecked")
080        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
081        @SuppressWarnings("unchecked")
082        List<String> contentsId = (List<String>) jsParameters.get("contentsId");
083        for (String contentId : contentsId)
084        {
085            try
086            {
087                Content content = _resolver.resolveById(contentId);
088                
089                AttributesImpl attrs = new AttributesImpl();
090                attrs.addCDATAAttribute("id", content.getId());
091                attrs.addCDATAAttribute("title", _contentHelper.getTitle(content));
092                attrs.addCDATAAttribute("name", content.getName());
093                attrs.addCDATAAttribute("path", content.getPath());
094                attrs.addCDATAAttribute("type", StringUtils.join(content.getTypes(), ','));
095                if (content.getLanguage() != null)
096                {
097                    attrs.addCDATAAttribute("lang", content.getLanguage());
098                }
099
100                XMLUtils.startElement(contentHandler, "content", attrs);
101                
102                List<ModelItem> modelItemsToSax = new ArrayList<>();
103                Map<String, OutgoingReferences> referencesByPath = content.getOutgoingReferences();
104                
105                for (String dataPath : referencesByPath.keySet())
106                {
107                    try
108                    {
109                        List<ModelItem> modelItemPath = _contentTypesHelper.getModelItemPath(dataPath, content);
110                        
111                        // Add these model items to the set of model items to SAX.
112                        modelItemsToSax.addAll(modelItemPath);
113                        
114                        saxReferencesConsistency(content, referencesByPath.get(dataPath), dataPath);
115                    }
116                    catch (UndefinedItemPathException e)
117                    {
118                        getLogger().warn("Trying to check consistency for a item path that doesn't exist in the model. Reference will be ignored", e);
119                    }
120                }
121                
122                // SAX model items used for this content
123                for (ModelItem modelItem : modelItemsToSax)
124                {
125                    attrs.clear();
126                    attrs.addCDATAAttribute("path", modelItem.getPath());
127                    attrs.addCDATAAttribute("is-repeater", Boolean.toString(ModelItemTypeConstants.REPEATER_TYPE_ID.equals(modelItem.getType().getId())));
128                    XMLUtils.startElement(contentHandler, "metadata-definition", attrs);
129                    modelItem.getLabel().toSAX(contentHandler);
130                    XMLUtils.endElement(contentHandler, "metadata-definition");
131                }
132                
133                XMLUtils.endElement(contentHandler, "content");
134            }
135            catch (UnknownAmetysObjectException e)
136            {
137                getLogger().warn("Can not check consistency of non existing content '" + contentId + "'");
138            }
139        }
140        
141        XMLUtils.endElement(contentHandler, "contents");
142        contentHandler.endDocument();
143    }
144
145    /**
146     * Check consistency for each outgoing references and generate SAX event for the result.
147     * @param content the content containing the references
148     * @param references the outgoing references
149     * @param dataPath the path of the data containing the outgoing references
150     * @throws SAXException if an error occurred
151     */
152    protected void saxReferencesConsistency(Content content, OutgoingReferences references, String dataPath) throws SAXException
153    {
154        // SAX'ing consistency info
155        String definitionPath = ModelHelper.getDefinitionPathFromDataPath(dataPath);
156        for (String referenceType : references.keySet())
157        {
158            for (String referenceValue : references.get(referenceType))
159            {
160                AttributesImpl attrs = new AttributesImpl();
161                attrs.addCDATAAttribute("type", referenceType);
162                attrs.addCDATAAttribute("element", referenceValue);
163                attrs.addCDATAAttribute("path", definitionPath);
164                
165                CheckReport report;
166                try
167                {
168                    report = _consistencyChecker.checkConsistency(referenceType, referenceValue, content.getId(), dataPath, false);
169                }
170                catch (Exception e)
171                {
172                    // Consider it a failure.
173                    report = new CheckReport(CHECK.SERVER_ERROR, Optional.ofNullable(e.getMessage()));
174                }
175                
176                String elementName;
177                switch (report.status())
178                {
179                    case SUCCESS:
180                        elementName = "success";
181                        break;
182                    case UNKNOWN:
183                        elementName = "unknown";
184                        break;
185                    case UNAUTHORIZED:
186                        elementName = "unauthorized";
187                        break;
188                    case NOT_FOUND:
189                        elementName = "not-found";
190                        break;
191                    case SERVER_ERROR:
192                    default:
193                        elementName = "server-error";
194                        break;
195                }
196                XMLUtils.startElement(contentHandler, elementName, attrs);
197                I18nizableText label = _consistencyChecker.getLabel(referenceType, referenceValue, content.getId(), dataPath);
198                label.toSAX(contentHandler, "label");
199                if (report.message().isPresent())
200                {
201                    XMLUtils.createElement(contentHandler, "message", report.message().get());
202                }
203                XMLUtils.endElement(contentHandler, elementName);
204            }
205        }
206    }
207}