001/* 002 * Copyright 2019 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.data.type; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Locale; 023import java.util.Map; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.configuration.ConfigurationException; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.XMLUtils; 034import org.apache.commons.lang3.StringUtils; 035import org.xml.sax.ContentHandler; 036import org.xml.sax.SAXException; 037 038import org.ametys.cms.contenttype.ContentTypesHelper; 039import org.ametys.cms.data.ContentValue; 040import org.ametys.cms.repository.Content; 041import org.ametys.core.model.type.AbstractElementType; 042import org.ametys.core.util.DateUtils; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.runtime.model.exception.BadItemTypeException; 045 046/** 047 * Abstract class for content type of elements 048 */ 049public abstract class AbstractContentElementType extends AbstractElementType<ContentValue> implements Component, Serviceable 050{ 051 /** Ametys object resolver */ 052 protected AmetysObjectResolver _resolver; 053 054 /** Helper for content types */ 055 protected ContentTypesHelper _contentTypesHelper; 056 057 public void service(ServiceManager manager) throws ServiceException 058 { 059 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 060 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 061 } 062 063 public ContentValue castValue(String value) throws BadItemTypeException 064 { 065 return new ContentValue(_resolver, value); 066 } 067 068 @Override 069 public String toString(ContentValue value) 070 { 071 return value.getContentId(); 072 } 073 074 @Override 075 public void valueToSAX(ContentHandler contentHandler, String tagName, Object value, Locale locale) throws SAXException 076 { 077 if (value != null) 078 { 079 if (value instanceof String) 080 { 081 Content content = _resolver.resolveById((String) value); 082 _singleContentToSAX(contentHandler, tagName, content, null); 083 } 084 else if (value instanceof String[]) 085 { 086 for (String contentId : (String[]) value) 087 { 088 Content content = _resolver.resolveById(contentId); 089 _singleContentToSAX(contentHandler, tagName, content, null); 090 } 091 } 092 else if (value instanceof Content) 093 { 094 _singleContentToSAX(contentHandler, tagName, (Content) value, null); 095 } 096 else if (value instanceof Content[]) 097 { 098 for (Content content : (Content[]) value) 099 { 100 _singleContentToSAX(contentHandler, tagName, content, null); 101 } 102 } 103 else if (value instanceof ContentValue) 104 { 105 _singleContentToSAX(contentHandler, tagName, ((ContentValue) value).getContent(), null); 106 } 107 else if (value instanceof ContentValue[]) 108 { 109 for (ContentValue contentWrapper : (ContentValue[]) value) 110 { 111 _singleContentToSAX(contentHandler, tagName, contentWrapper.getContent(), null); 112 } 113 } 114 else 115 { 116 throw new IllegalArgumentException("Try to sax the non content value '" + value + "' in tag name '" + tagName + "'"); 117 } 118 } 119 } 120 121 private void _singleContentToSAX(ContentHandler contentHandler, String tagName, Content content, Locale locale) throws SAXException 122 { 123 AttributesImpl attrs = new AttributesImpl(); 124 attrs.addCDATAAttribute("id", content.getId()); 125 attrs.addCDATAAttribute("name", content.getName()); 126 attrs.addCDATAAttribute("title", content.getTitle(locale)); 127 if (content.getLanguage() != null) 128 { 129 attrs.addCDATAAttribute("language", content.getLanguage()); 130 } 131 attrs.addCDATAAttribute("createdAt", DateUtils.dateToString(content.getCreationDate())); 132 attrs.addCDATAAttribute("creator", content.getCreator().getLogin()); 133 attrs.addCDATAAttribute("lastModifiedAt", DateUtils.dateToString(content.getLastModified())); 134 135 XMLUtils.createElement(contentHandler, tagName, attrs); 136 } 137 138 @Override 139 public ContentValue parseConfiguration(Configuration configuration) throws ConfigurationException 140 { 141 if (configuration != null) 142 { 143 String contentId = configuration.getAttribute("id", null); 144 if (StringUtils.isNotEmpty(contentId)) 145 { 146 return new ContentValue(_resolver, contentId); 147 } 148 } 149 150 return null; 151 } 152 153 @Override 154 public Object valueToJSONForClient(Object value) 155 { 156 if (value == null) 157 { 158 return value; 159 } 160 else if (value instanceof String) 161 { 162 return _content2Json((String) value, null); 163 } 164 else if (value instanceof String[]) 165 { 166 return Arrays.asList((String[]) value).stream() 167 .map(contentId -> _content2Json(contentId, null)) 168 .collect(Collectors.toList()); 169 } 170 else if (value instanceof Content) 171 { 172 return _content2Json((Content) value, null); 173 } 174 else if (value instanceof Content[]) 175 { 176 return Arrays.asList((Content[]) value).stream() 177 .map(content -> _content2Json(content, null)) 178 .collect(Collectors.toList()); 179 } 180 else if (value instanceof ContentValue) 181 { 182 return _content2Json(((ContentValue) value).getContent(), null); 183 } 184 else if (value instanceof ContentValue[]) 185 { 186 return Arrays.asList((ContentValue[]) value).stream() 187 .map(contentWrapper -> _content2Json(contentWrapper.getContent(), null)) 188 .collect(Collectors.toList()); 189 } 190 else 191 { 192 throw new IllegalArgumentException("Try to convert the non content value '" + value + "' to JSON"); 193 } 194 } 195 196 private Map<String, Object> _content2Json(String contentId, Locale locale) 197 { 198 Content content = _resolver.resolveById(contentId); 199 return _content2Json(content, locale); 200 } 201 202 private Map<String, Object> _content2Json(Content content, Locale locale) 203 { 204 Map<String, Object> contentProperties = new HashMap<>(); 205 206 contentProperties.put("id", content.getId()); 207 contentProperties.put("title", content.getTitle(locale)); 208 contentProperties.put("iconGlyph", StringUtils.defaultString(_contentTypesHelper.getIconGlyph(content))); 209 contentProperties.put("iconDecorator", StringUtils.defaultString(_contentTypesHelper.getIconDecorator(content))); 210 contentProperties.put("smallIcon", StringUtils.defaultString(_contentTypesHelper.getSmallIcon(content))); 211 contentProperties.put("mediumIcon", StringUtils.defaultString(_contentTypesHelper.getMediumIcon(content))); 212 contentProperties.put("largeIcon", StringUtils.defaultString(_contentTypesHelper.getLargeIcon(content))); 213 contentProperties.put("contentTypes", content.getTypes()); 214 contentProperties.put("mixins", content.getMixinTypes()); 215 216 return contentProperties; 217 } 218 219 public Object fromJSONForClient(Object json) 220 { 221 if (json == null) 222 { 223 return json; 224 } 225 else if (json instanceof String) 226 { 227 return castValue((String) json); 228 } 229 else if (json instanceof List) 230 { 231 @SuppressWarnings("unchecked") 232 List<String> jsonList = (List<String>) json; 233 List<ContentValue> contentList = new ArrayList<>(); 234 for (String singleJSON : jsonList) 235 { 236 contentList.add(castValue(singleJSON)); 237 } 238 return contentList.toArray(new ContentValue[contentList.size()]); 239 } 240 else 241 { 242 throw new IllegalArgumentException("Try to convert the non content time JSON object '" + json + "' into a content"); 243 } 244 } 245 246 @Override 247 public boolean isCompatible(Object value) 248 { 249 return super.isCompatible(value) 250 || (value instanceof String && _hasAmetysObjectsForIdentifiers((String) value)) || (value instanceof String[] && _hasAmetysObjectsForIdentifiers((String[]) value)) 251 || value instanceof Content || value instanceof Content[]; 252 } 253 254 public boolean isSimple() 255 { 256 return false; 257 } 258 259 /** 260 * Check if ametys objects exist with the given identifiers 261 * @param identifiers the identifiers 262 * @return <code>true</code> if ametys object exist for each identifier, <code>false</code> otherwise 263 */ 264 protected boolean _hasAmetysObjectsForIdentifiers(String... identifiers) 265 { 266 for (String identifier : identifiers) 267 { 268 // If there is at list one identifier that does not match an ametys object => return false 269 if (identifier.indexOf("://") < 0 || !_resolver.hasAmetysObjectForId(identifier)) 270 { 271 return false; 272 } 273 } 274 275 // All identifiers match an ametys object 276 return true; 277 } 278}