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}