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