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.apache.commons.lang3.Strings; 035import org.xml.sax.SAXException; 036 037import org.ametys.cms.repository.Content; 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 ContentConsistencyResultGenerator extends AbstractGenerator implements Serviceable 047{ 048 /** the name of the attribute use to recover the list of result */ 049 public static final String RESULTS_REQUEST_ATTRIBUTE_NAME = "contentConsistencyResult"; 050 051 /** The ametys object resolver */ 052 protected AmetysObjectResolver _resolver; 053 private ServerCommHelper _serverCommHelper; 054 private ContentConsistencySearchModel _searchModel; 055 056 public void service(ServiceManager manager) throws ServiceException 057 { 058 _searchModel = (ContentConsistencySearchModel) manager.lookup(ContentConsistencySearchModel.ROLE); 059 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 060 _serverCommHelper = (ServerCommHelper) manager.lookup(ServerCommHelper.ROLE); 061 } 062 063 public void generate() throws IOException, SAXException, ProcessingException 064 { 065 Request request = ObjectModelHelper.getRequest(objectModel); 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("path"))) 082 { 083 AttributesImpl attrs = new AttributesImpl(); 084 attrs.addCDATAAttribute("id", (String) column.get("path")); 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 104 @SuppressWarnings("unchecked") 105 List<Map<String, Object>> results = (List<Map<String, Object>>) request.getAttribute(RESULTS_REQUEST_ATTRIBUTE_NAME); 106 if (results != null) 107 { 108 for (Map<String, Object> result : results) 109 { 110 _saxContentConsistency(result, columns); 111 } 112 } 113 114 XMLUtils.endElement(contentHandler, "contents"); 115 XMLUtils.endElement(contentHandler, "export"); 116 contentHandler.endDocument(); 117 } 118 119 /** 120 * Generate information on content consistency. 121 * @param result the content consistency checker result 122 * @param columns the names of the columns to SAX 123 * @throws SAXException if an errors occurs generating the data. 124 */ 125 protected void _saxContentConsistency(Map<String, Object> result, List<String> columns) throws SAXException 126 { 127 XMLUtils.startElement(contentHandler, "content"); 128 // if there is no columns, we use all the keys 129 for (String column : !columns.isEmpty() ? columns : result.keySet()) 130 { 131 AttributesImpl attrs = new AttributesImpl(); 132 attrs.addCDATAAttribute("fullModelPath", column); 133 Object value = result.get(column); 134 if (value instanceof String str) 135 { 136 XMLUtils.createElement(contentHandler, column, attrs, str); 137 } 138 else if (value instanceof Long count) 139 { 140 XMLUtils.createElement(contentHandler, column, attrs, count.toString()); 141 } 142 else if (value instanceof ZonedDateTime date) 143 { 144 XMLUtils.createElement(contentHandler, column, attrs, DateUtils.zonedDateTimeToString(date)); 145 } 146 else if (Strings.CS.equals(column, "contributor")) 147 { 148 @SuppressWarnings("unchecked") 149 Map<String, Object> mapValue = (Map<String, Object>) value; 150 String fullName = (String) mapValue.get("fullname"); 151 if (StringUtils.isNotBlank(fullName)) 152 { 153 XMLUtils.createElement(contentHandler, column, attrs, fullName); 154 } 155 else 156 { 157 String login = (String) mapValue.get("login"); 158 String population = (String) mapValue.get("population"); 159 if (StringUtils.isNoneBlank(login, population)) 160 { 161 XMLUtils.createElement(contentHandler, column, attrs, login + "#" + population); 162 } 163 else 164 { 165 XMLUtils.createElement(contentHandler, column, attrs); 166 } 167 } 168 } 169 else if (Strings.CS.equals(column, "workflowStep")) 170 { 171 @SuppressWarnings("unchecked") 172 Map<String, Object> mapValue = (Map<String, Object>) value; 173 XMLUtils.startElement(contentHandler, column, attrs); 174 I18nizableText label = (I18nizableText) mapValue.get("name"); 175 if (label != null) 176 { 177 label.toSAX(contentHandler); 178 } 179 XMLUtils.endElement(contentHandler, column); 180 } 181 else if (Strings.CS.equals(column, "contentTypes")) 182 { 183 @SuppressWarnings("unchecked") 184 List<Map<String, Object>> jsonValues = (List<Map<String, Object>>) value; 185 List<I18nizableText> labels = jsonValues.stream() 186 .map(map -> (I18nizableText) map.get("label")) 187 .filter(Objects::nonNull) 188 .toList(); 189 for (I18nizableText label : labels) 190 { 191 XMLUtils.startElement(contentHandler, column, attrs); 192 label.toSAX(contentHandler); 193 XMLUtils.endElement(contentHandler, column); 194 } 195 } 196 } 197 XMLUtils.endElement(contentHandler, "content"); 198 } 199 200 /** 201 * Sax additional data on the content 202 * @param content the content. 203 * @param atts the attributes the will be saxed 204 */ 205 protected void _saxAdditionalContentAttributes (Content content, AttributesImpl atts) 206 { 207 // Nothing to do 208 } 209}