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     * @param columns the names of the columns to SAX
120     * @throws SAXException if an errors occurs generating the data.
121     */
122    protected void _saxContentConsistency(Map<String, Object> result, List<String> columns) throws SAXException
123    {
124        XMLUtils.startElement(contentHandler, "content");
125        // if there is no columns, we use all the keys
126        for (String column : !columns.isEmpty() ? columns : result.keySet())
127        {
128            AttributesImpl attrs = new AttributesImpl();
129            attrs.addCDATAAttribute("fullModelPath", column);
130            Object value = result.get(column);
131            if (value instanceof String str)
132            {
133                XMLUtils.createElement(contentHandler, column, attrs, str);
134            }
135            else if (value instanceof Long count)
136            {
137                XMLUtils.createElement(contentHandler, column, attrs, count.toString());
138            }
139            else if (value instanceof ZonedDateTime date)
140            {
141                XMLUtils.createElement(contentHandler, column, attrs, DateUtils.zonedDateTimeToString(date));
142            }
143            else if (StringUtils.equals(column, "contributor"))
144            {
145                @SuppressWarnings("unchecked")
146                Map<String, Object> mapValue = (Map<String, Object>) value;
147                String fullName = (String) mapValue.get("fullname");
148                if (StringUtils.isNotBlank(fullName))
149                {
150                    XMLUtils.createElement(contentHandler, column, attrs, fullName);
151                }
152                else
153                {
154                    String login = (String) mapValue.get("login");
155                    String population = (String) mapValue.get("population");
156                    if (StringUtils.isNoneBlank(login, population))
157                    {
158                        XMLUtils.createElement(contentHandler, column, attrs, login + "#" + population);
159                    }
160                    else
161                    {
162                        XMLUtils.createElement(contentHandler, column, attrs);
163                    }
164                }
165            }
166            else if (StringUtils.equals(column, "workflowStep"))
167            {
168                @SuppressWarnings("unchecked")
169                Map<String, Object> mapValue = (Map<String, Object>) value;
170                XMLUtils.startElement(contentHandler, column, attrs);
171                I18nizableText label = (I18nizableText) mapValue.get("name");
172                if (label != null)
173                {
174                    label.toSAX(contentHandler);
175                }
176                XMLUtils.endElement(contentHandler, column);
177            }
178            else if (StringUtils.equals(column, "contentTypes"))
179            {
180                @SuppressWarnings("unchecked")
181                List<Map<String, Object>> jsonValues = (List<Map<String, Object>>) value;
182                List<I18nizableText> labels = jsonValues.stream()
183                    .map(map -> (I18nizableText) map.get("label"))
184                    .filter(Objects::nonNull)
185                    .toList();
186                for (I18nizableText label : labels)
187                {
188                    XMLUtils.startElement(contentHandler, column, attrs);
189                    label.toSAX(contentHandler);
190                    XMLUtils.endElement(contentHandler, column);
191                }
192            }
193        }
194        XMLUtils.endElement(contentHandler, "content");
195    }
196    
197    /**
198     * Sax additional data on the content 
199     * @param content the content.
200     * @param atts the attributes the will be saxed
201     */
202    protected void _saxAdditionalContentAttributes (Content content, AttributesImpl atts)
203    {
204        // Nothing to do
205    }
206    
207    /**
208     * Get the user name
209     * @param userIdentity the user
210     * @return the user name
211     */
212    protected String getName(UserIdentity userIdentity)
213    {
214        User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
215        return user != null ? user.getFullName() : "";
216    }
217}