001/* 002 * Copyright 2010 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 */ 016 017package org.ametys.cms.content; 018 019import java.io.IOException; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.ProcessingException; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.generation.ServiceableGenerator; 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.content.references.OutgoingReferences; 035import org.ametys.cms.contenttype.ContentConstants; 036import org.ametys.cms.contenttype.ContentType; 037import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 038import org.ametys.cms.contenttype.MetadataDefinition; 039import org.ametys.cms.contenttype.MetadataDefinitionHolder; 040import org.ametys.cms.contenttype.RepeaterDefinition; 041import org.ametys.cms.repository.Content; 042import org.ametys.cms.transformation.ConsistencyChecker; 043import org.ametys.cms.transformation.ConsistencyChecker.CHECK; 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.plugins.repository.UnknownAmetysObjectException; 046import org.ametys.runtime.i18n.I18nizableText; 047 048/** 049 * Generates the consistency report for the given content 050 */ 051public class ConsistencyGenerator extends ServiceableGenerator 052{ 053 /** Repository content */ 054 protected AmetysObjectResolver _resolver; 055 /** The consistency checker */ 056 protected ConsistencyChecker _consistencyChecker; 057 /** The content types extension point */ 058 protected ContentTypeExtensionPoint _cTypeEP; 059 060 @Override 061 public void service(ServiceManager smanager) throws ServiceException 062 { 063 super.service(smanager); 064 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 065 _consistencyChecker = (ConsistencyChecker) smanager.lookup(ConsistencyChecker.ROLE); 066 _cTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 067 } 068 069 @Override 070 public void generate() throws IOException, SAXException, ProcessingException 071 { 072 contentHandler.startDocument(); 073 XMLUtils.startElement(contentHandler, "contents"); 074 075 @SuppressWarnings("unchecked") 076 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 077 @SuppressWarnings("unchecked") 078 List<String> contentsId = (List<String>) jsParameters.get("contentsId"); 079 for (String contentId : contentsId) 080 { 081 try 082 { 083 Content content = _resolver.resolveById(contentId); 084 085 AttributesImpl attrs = new AttributesImpl(); 086 attrs.addCDATAAttribute("id", content.getId()); 087 attrs.addCDATAAttribute("title", content.getTitle()); 088 attrs.addCDATAAttribute("name", content.getName()); 089 attrs.addCDATAAttribute("path", content.getPath()); 090 attrs.addCDATAAttribute("type", StringUtils.join(content.getTypes(), ',')); 091 attrs.addCDATAAttribute("lang", content.getLanguage()); 092 093 XMLUtils.startElement(contentHandler, "content", attrs); 094 095 Map<String, MetadataDefinition> metadataDefToSax = new HashMap<>(); 096 Map<String, OutgoingReferences> referencesByPath = content.getOutgoingReferences(); 097 098 for (String valuePath : referencesByPath.keySet()) 099 { 100 // The metadata path is the value path with repeater indices stripped. 101 String metaPath = valuePath.replaceAll("\\[[0-9]+\\]", ""); 102 103 Map<String, MetadataDefinition> metadataDefByPath = getMetadataDefinitionListByPath(valuePath, content.getTypes(), content.getMixinTypes()); 104 105 // Add these metadata definitions to the set of metadata definition to SAX. 106 metadataDefToSax.putAll(metadataDefByPath); 107 108 // SAX'ing consistency info 109 OutgoingReferences references = referencesByPath.get(valuePath); 110 for (String referenceType : references.keySet()) 111 { 112 for (String referenceValue : references.get(referenceType)) 113 { 114 attrs.clear(); 115 attrs.addCDATAAttribute("type", referenceType); 116 attrs.addCDATAAttribute("element", referenceValue); 117 attrs.addCDATAAttribute("path", metaPath); 118 I18nizableText label = _consistencyChecker.getLabel(referenceType, referenceValue); 119 120 CHECK check = CHECK.SERVER_ERROR; 121 try 122 { 123 check = _consistencyChecker.checkConsistency(referenceType, referenceValue, false); 124 } 125 catch (Exception e) 126 { 127 // Ignore, consider it a failure. 128 } 129 130 switch (check) 131 { 132 case SUCCESS: 133 XMLUtils.startElement(contentHandler, "success", attrs); 134 label.toSAX(contentHandler); 135 XMLUtils.endElement(contentHandler, "success"); 136 break; 137 case UNKNOWN: 138 XMLUtils.startElement(contentHandler, "unknown", attrs); 139 label.toSAX(contentHandler); 140 XMLUtils.endElement(contentHandler, "unknown"); 141 break; 142 case UNAUTHORIZED: 143 XMLUtils.startElement(contentHandler, "unauthorized", attrs); 144 label.toSAX(contentHandler); 145 XMLUtils.endElement(contentHandler, "unauthorized"); 146 break; 147 case NOT_FOUND: 148 XMLUtils.startElement(contentHandler, "not-found", attrs); 149 label.toSAX(contentHandler); 150 XMLUtils.endElement(contentHandler, "not-found"); 151 break; 152 case SERVER_ERROR: 153 default: 154 XMLUtils.startElement(contentHandler, "server-error", attrs); 155 label.toSAX(contentHandler); 156 XMLUtils.endElement(contentHandler, "server-error"); 157 break; 158 } 159 } 160 } 161 } 162 163 // SAX metadata definition for used for this content 164 for (String path : metadataDefToSax.keySet()) 165 { 166 MetadataDefinition metadataDefinition = metadataDefToSax.get(path); 167 168 attrs.clear(); 169 attrs.addCDATAAttribute("path", path); 170 attrs.addCDATAAttribute("is-repeater", Boolean.toString(metadataDefinition instanceof RepeaterDefinition)); 171 XMLUtils.startElement(contentHandler, "metadata-definition", attrs); 172 metadataDefinition.getLabel().toSAX(contentHandler); 173 XMLUtils.endElement(contentHandler, "metadata-definition"); 174 } 175 176 XMLUtils.endElement(contentHandler, "content"); 177 } 178 catch (UnknownAmetysObjectException e) 179 { 180 getLogger().warn("Can not check consistency of non existing content '" + contentId + "'"); 181 } 182 } 183 184 XMLUtils.endElement(contentHandler, "contents"); 185 contentHandler.endDocument(); 186 } 187 188 /** 189 * Retrieve the list of metadata definition for the given path 190 * @param metadataValuePath the metadata value path. It is slash-separated and repeaters are present as indices, eg "mycomposite/myrepeater[3]/mymetadata". 191 * @param cTypes The id of content types 192 * @param mixins The id of mixins 193 * @return The list of metadata definition (or null) 194 */ 195 protected Map<String, MetadataDefinition> getMetadataDefinitionListByPath(String metadataValuePath, String[] cTypes, String[] mixins) 196 { 197 Map<String, MetadataDefinition> metadataDefByPath = new HashMap<>(); 198 199 for (String id : cTypes) 200 { 201 ContentType cType = _cTypeEP.getExtension(id); 202 203 _getMetadataDefinitionListByPath(metadataValuePath, cType, metadataDefByPath, ""); 204 if (!metadataDefByPath.isEmpty()) 205 { 206 return metadataDefByPath; 207 } 208 } 209 210 for (String id : mixins) 211 { 212 ContentType cType = _cTypeEP.getExtension(id); 213 214 _getMetadataDefinitionListByPath(metadataValuePath, cType, metadataDefByPath, ""); 215 if (!metadataDefByPath.isEmpty()) 216 { 217 return metadataDefByPath; 218 } 219 } 220 221 return metadataDefByPath; 222 } 223 224 /** 225 * Retrieve the list of metadata definition for the given path 226 * @param metadataValuePath the metadata value path. It is slash-separated and repeaters are present as indices, eg "mycomposite/myrepeater[3]/mymetadata". 227 * @param metadataDefHolder The metadata def holder (such as the content type) 228 * @param metadataDefByPath The map of metadata definition by path, to be filled. 229 * @param prefix The metadata path prefix. 230 */ 231 protected void _getMetadataDefinitionListByPath(String metadataValuePath, MetadataDefinitionHolder metadataDefHolder, Map<String, MetadataDefinition> metadataDefByPath, String prefix) 232 { 233 String metadataName = StringUtils.substringBefore(metadataValuePath, ContentConstants.METADATA_PATH_SEPARATOR); 234 String subMetadataPath = StringUtils.substringAfter(metadataValuePath, ContentConstants.METADATA_PATH_SEPARATOR); 235 // Ignore the repeater entry if any. 236 metadataName = StringUtils.substringBefore(metadataName, "["); 237 238 MetadataDefinition metadataDefinition = metadataDefHolder.getMetadataDefinition(metadataName); 239 240 if (metadataDefinition != null) 241 { 242 String metaPath = prefix + metadataName; 243 metadataDefByPath.put(metaPath, metadataDefinition); 244 245 // If there is some path remaining, recurse. 246 if (StringUtils.isNotEmpty(subMetadataPath)) 247 { 248 _getMetadataDefinitionListByPath(subMetadataPath, metadataDefinition, metadataDefByPath, metaPath + ContentConstants.METADATA_PATH_SEPARATOR); 249 } 250 } 251 } 252}