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