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