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}