001/*
002 *  Copyright 2023 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.consistency;
017
018import java.io.IOException;
019import java.time.ZonedDateTime;
020import java.util.List;
021import java.util.Map;
022import java.util.Objects;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.environment.ObjectModelHelper;
029import org.apache.cocoon.environment.Request;
030import org.apache.cocoon.generation.AbstractGenerator;
031import org.apache.cocoon.xml.AttributesImpl;
032import org.apache.cocoon.xml.XMLUtils;
033import org.apache.commons.lang3.StringUtils;
034import org.xml.sax.SAXException;
035
036import org.ametys.cms.repository.Content;
037import org.ametys.core.util.DateUtils;
038import org.ametys.core.util.ServerCommHelper;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.runtime.i18n.I18nizableText;
041
042/**
043 * Retrieve all content consistency result stored and sax them
044 */
045public class ContentConsistencyResultGenerator extends AbstractGenerator implements Serviceable
046{
047    /** the name of the attribute use to recover the list of result */
048    public static final String RESULTS_REQUEST_ATTRIBUTE_NAME = "contentConsistencyResult";
049    
050    /** The ametys object resolver */
051    protected AmetysObjectResolver _resolver;
052    private ServerCommHelper _serverCommHelper;
053    private ContentConsistencySearchModel _searchModel;
054
055    public void service(ServiceManager manager) throws ServiceException
056    {
057        _searchModel = (ContentConsistencySearchModel) manager.lookup(ContentConsistencySearchModel.ROLE);
058        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
059        _serverCommHelper = (ServerCommHelper) manager.lookup(ServerCommHelper.ROLE);
060    }
061    
062    public void generate() throws IOException, SAXException, ProcessingException
063    {
064        Request request = ObjectModelHelper.getRequest(objectModel);
065        Map<String, Object> jsParameters = _serverCommHelper.getJsParameters();
066        
067        
068        contentHandler.startDocument();
069        XMLUtils.startElement(contentHandler, "export");
070        
071        XMLUtils.startElement(contentHandler, "columns");
072        
073        Map<String, Object> model = _searchModel.getModel();
074        @SuppressWarnings("unchecked")
075        List<Map<String, Object>> modelColumns = (List<Map<String, Object>>) model.get("columns");
076        @SuppressWarnings("unchecked")
077        List<String> columns = jsParameters != null ? (List<String>) jsParameters.get("columns") : List.of();
078        for (Map<String, Object> column : modelColumns)
079        {
080            if (columns.isEmpty() || columns.contains(column.get("path")))
081            {
082                AttributesImpl attrs = new AttributesImpl();
083                attrs.addCDATAAttribute("id", (String) column.get("path"));
084                attrs.addCDATAAttribute("type", (String) column.get("type"));
085                XMLUtils.startElement(contentHandler, "column", attrs);
086                Object label = column.get("label");
087                if (label instanceof I18nizableText i18n)
088                {
089                    i18n.toSAX(contentHandler);
090                }
091                else
092                {
093                    XMLUtils.data(contentHandler, label.toString());
094                    
095                }
096                XMLUtils.endElement(contentHandler, "column");
097            }
098        }
099        XMLUtils.endElement(contentHandler, "columns");
100        
101        XMLUtils.startElement(contentHandler, "contents");
102        
103        @SuppressWarnings("unchecked")
104        List<Map<String, Object>> results = (List<Map<String, Object>>) request.getAttribute(RESULTS_REQUEST_ATTRIBUTE_NAME);
105        if (results != null)
106        {
107            for (Map<String, Object> result : results)
108            {
109                _saxContentConsistency(result, columns);
110            }
111        }
112        
113        XMLUtils.endElement(contentHandler, "contents");
114        XMLUtils.endElement(contentHandler, "export");
115        contentHandler.endDocument();
116    }
117
118    /**
119     * Generate information on content consistency. 
120     * @param result the content consistency checker result
121     * @param columns the names of the columns to SAX
122     * @throws SAXException if an errors occurs generating the data.
123     */
124    protected void _saxContentConsistency(Map<String, Object> result, List<String> columns) throws SAXException
125    {
126        XMLUtils.startElement(contentHandler, "content");
127        // if there is no columns, we use all the keys
128        for (String column : !columns.isEmpty() ? columns : result.keySet())
129        {
130            AttributesImpl attrs = new AttributesImpl();
131            attrs.addCDATAAttribute("fullModelPath", column);
132            Object value = result.get(column);
133            if (value instanceof String str)
134            {
135                XMLUtils.createElement(contentHandler, column, attrs, str);
136            }
137            else if (value instanceof Long count)
138            {
139                XMLUtils.createElement(contentHandler, column, attrs, count.toString());
140            }
141            else if (value instanceof ZonedDateTime date)
142            {
143                XMLUtils.createElement(contentHandler, column, attrs, DateUtils.zonedDateTimeToString(date));
144            }
145            else if (StringUtils.equals(column, "contributor"))
146            {
147                @SuppressWarnings("unchecked")
148                Map<String, Object> mapValue = (Map<String, Object>) value;
149                String fullName = (String) mapValue.get("fullname");
150                if (StringUtils.isNotBlank(fullName))
151                {
152                    XMLUtils.createElement(contentHandler, column, attrs, fullName);
153                }
154                else
155                {
156                    String login = (String) mapValue.get("login");
157                    String population = (String) mapValue.get("population");
158                    if (StringUtils.isNoneBlank(login, population))
159                    {
160                        XMLUtils.createElement(contentHandler, column, attrs, login + "#" + population);
161                    }
162                    else
163                    {
164                        XMLUtils.createElement(contentHandler, column, attrs);
165                    }
166                }
167            }
168            else if (StringUtils.equals(column, "workflowStep"))
169            {
170                @SuppressWarnings("unchecked")
171                Map<String, Object> mapValue = (Map<String, Object>) value;
172                XMLUtils.startElement(contentHandler, column, attrs);
173                I18nizableText label = (I18nizableText) mapValue.get("name");
174                if (label != null)
175                {
176                    label.toSAX(contentHandler);
177                }
178                XMLUtils.endElement(contentHandler, column);
179            }
180            else if (StringUtils.equals(column, "contentTypes"))
181            {
182                @SuppressWarnings("unchecked")
183                List<Map<String, Object>> jsonValues = (List<Map<String, Object>>) value;
184                List<I18nizableText> labels = jsonValues.stream()
185                    .map(map -> (I18nizableText) map.get("label"))
186                    .filter(Objects::nonNull)
187                    .toList();
188                for (I18nizableText label : labels)
189                {
190                    XMLUtils.startElement(contentHandler, column, attrs);
191                    label.toSAX(contentHandler);
192                    XMLUtils.endElement(contentHandler, column);
193                }
194            }
195        }
196        XMLUtils.endElement(contentHandler, "content");
197    }
198    
199    /**
200     * Sax additional data on the content 
201     * @param content the content.
202     * @param atts the attributes the will be saxed
203     */
204    protected void _saxAdditionalContentAttributes (Content content, AttributesImpl atts)
205    {
206        // Nothing to do
207    }
208}