001/* 002 * Copyright 2013 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.search.ui.model.impl; 017 018import java.util.Arrays; 019import java.util.Collection; 020import java.util.HashSet; 021import java.util.LinkedHashSet; 022import java.util.List; 023import java.util.Locale; 024import java.util.Set; 025 026import org.apache.avalon.framework.activity.Disposable; 027import org.apache.avalon.framework.configuration.Configurable; 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.avalon.framework.configuration.ConfigurationException; 030import org.apache.avalon.framework.context.Context; 031import org.apache.avalon.framework.context.ContextException; 032import org.apache.avalon.framework.context.Contextualizable; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.slf4j.Logger; 037 038import org.ametys.cms.content.ContentHelper; 039import org.ametys.cms.contenttype.ContentConstants; 040import org.ametys.cms.contenttype.ContentType; 041import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 042import org.ametys.cms.contenttype.ContentTypesHelper; 043import org.ametys.cms.contenttype.MetadataDefinition; 044import org.ametys.cms.contenttype.RepeaterDefinition; 045import org.ametys.cms.repository.Content; 046import org.ametys.cms.search.SearchField; 047import org.ametys.cms.search.model.SystemProperty; 048import org.ametys.cms.search.model.SystemProperty.EnumeratorDefinition; 049import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 050import org.ametys.cms.search.model.SystemResultField; 051import org.ametys.plugins.repository.AmetysObjectResolver; 052import org.ametys.runtime.i18n.I18nizableText; 053import org.ametys.runtime.parameter.Enumerator; 054import org.ametys.runtime.parameter.StaticEnumerator; 055import org.ametys.runtime.plugin.component.LogEnabled; 056import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 057 058/** 059 * This class is a search column on a system property (author, lastModified, with-comments, ...) 060 */ 061public class SystemSearchUIColumn extends AbstractSearchUIColumn implements SystemResultField, Contextualizable, LogEnabled, Configurable, Serviceable, Disposable 062{ 063 064 /** ComponentManager for {@link Enumerator}s. */ 065 protected ThreadSafeComponentManager<Enumerator> _enumeratorManager; 066 067 /** The system property extension point. */ 068 protected SystemPropertyExtensionPoint _systemPropEP; 069 070 /** The content type extension point. */ 071 protected ContentTypeExtensionPoint _cTypeEP; 072 073 /** The content helper. */ 074 protected ContentHelper _contentHelper; 075 076 /** The content type helper. */ 077 protected ContentTypesHelper _contentTypeHelper; 078 079 /** The Ametys object resolver. */ 080 protected AmetysObjectResolver _resolver; 081 082 /** The system property. */ 083 protected SystemProperty _systemProperty; 084 085 /** The join paths. */ 086 protected String _joinPaths; 087 088 /** The content types */ 089 private Set<String> _contentTypes; 090 091 private ServiceManager _manager; 092 private Logger _logger; 093 private Context _context; 094 095 @Override 096 public void contextualize(Context context) throws ContextException 097 { 098 _context = context; 099 } 100 101 @Override 102 public void setLogger(Logger logger) 103 { 104 _logger = logger; 105 } 106 107 @Override 108 public void service(ServiceManager manager) throws ServiceException 109 { 110 _manager = manager; 111 _systemPropEP = (SystemPropertyExtensionPoint) manager.lookup(SystemPropertyExtensionPoint.ROLE); 112 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 113 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 114 _contentTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 115 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 116 } 117 118 @Override 119 public void dispose() 120 { 121 _enumeratorManager.dispose(); 122 _enumeratorManager = null; 123 } 124 125 @Override 126 public void configure(Configuration configuration) throws ConfigurationException 127 { 128 try 129 { 130 _enumeratorManager = new ThreadSafeComponentManager<>(); 131 _enumeratorManager.setLogger(_logger); 132 _enumeratorManager.contextualize(_context); 133 _enumeratorManager.service(_manager); 134 135 String fullPath = configuration.getChild("systemProperty").getAttribute("name"); 136 int pos = fullPath.lastIndexOf(ContentConstants.METADATA_PATH_SEPARATOR); 137 138 String systemPropertyId = pos > -1 ? fullPath.substring(pos + ContentConstants.METADATA_PATH_SEPARATOR.length()) : fullPath; 139 140 if (!_systemPropEP.isDisplayable(systemPropertyId)) 141 { 142 throw new ConfigurationException("The property '" + systemPropertyId + "' doesn't exist or is not displayable."); 143 } 144 145 _joinPaths = pos > -1 ? fullPath.substring(0, pos) : ""; 146 _systemProperty = _systemPropEP.getExtension(systemPropertyId); 147 148 _contentTypes = new HashSet<>(); 149 for (Configuration cTypeConf : configuration.getChild("contentTypes").getChildren("type")) 150 { 151 _contentTypes.add(cTypeConf.getAttribute("id")); 152 } 153 154 setId(fullPath); 155 156 setLabel(_systemProperty.getLabel()); 157 setDescription(_systemProperty.getDescription()); 158 setType(_systemProperty.getType()); 159 160 String contentTypeId = configuration.getChild("contentTypes").getAttribute("baseId", null); 161 boolean multiple = testMultiple(contentTypeId); 162 setMultiple(multiple); 163 164 String enumeratorRole = null; 165 EnumeratorDefinition enumDef = _systemProperty.getEnumeratorDefinition(configuration); 166 if (enumDef != null) 167 { 168 enumeratorRole = _initializeEnumerator(enumDef); 169 if (enumeratorRole != null) 170 { 171 _enumeratorManager.initialize(); 172 setEnumerator(_enumeratorManager.lookup(enumeratorRole)); 173 } 174 } 175 176 setEditable(false); 177 setWidth(_systemProperty.getColumnWidth() != null ? _systemProperty.getColumnWidth() : 150); 178 setHidden(configuration.getChild("hidden").getValueAsBoolean(false)); 179 setSortable(configuration.getChild("sortable").getValueAsBoolean(_systemProperty.isSortable())); 180 if (isSortable()) 181 { 182 setDefaultSorter(configuration.getChild("default-sorter").getValue(null)); 183 } 184 // Potentially replace the standard label and description by the custom ones. 185 I18nizableText userLabel = _configureI18nizableText(configuration.getChild("label", false), null); 186 if (userLabel != null) 187 { 188 setLabel(userLabel); 189 } 190 I18nizableText userDescription = _configureI18nizableText(configuration.getChild("description", false), null); 191 if (userDescription != null) 192 { 193 setDescription(userDescription); 194 } 195 196 if (_systemProperty.getRenderer() != null) 197 { 198 setRenderer(_systemProperty.getRenderer()); 199 } 200 if (_systemProperty.getConverter() != null) 201 { 202 setConverter(_systemProperty.getConverter()); 203 } 204 } 205 catch (Exception e) 206 { 207 throw new ConfigurationException("Error configuring the system search column.", configuration, e); 208 } 209 } 210 211 private boolean testMultiple(String contentTypeId) 212 { 213 // if system property is multiple, then the column is multiple 214 if (_systemProperty.isMultiple()) 215 { 216 return true; 217 } 218 219 // Otherwise, the column is multiple if at least one of the join path definitions is multiple 220 if (contentTypeId != null) 221 { 222 ContentType cType = _cTypeEP.getExtension(contentTypeId); 223 List<MetadataDefinition> definitions = _contentTypeHelper.getMetadataDefinitionPath(_joinPaths, cType); 224 for (MetadataDefinition definition : definitions) 225 { 226 if (definition.isMultiple() || definition instanceof RepeaterDefinition) 227 { 228 return true; 229 } 230 } 231 } 232 233 return false; 234 } 235 236 /** 237 * Get the system property name 238 * @return The property name 239 */ 240 public String getSystemPropertyId() 241 { 242 return _systemProperty.getId(); 243 } 244 245 @Override 246 public Object getValue(Content content, Locale defaultLocale) 247 { 248 return _getValue(content, false); 249 } 250 251 @Override 252 public Object getFullValue(Content content, Locale defaultLocale) 253 { 254 return _getValue(content, true); 255 } 256 257 private Object _getValue(Content content, boolean full) 258 { 259 if (isMultiple()) 260 { 261 Set<Object> values = new LinkedHashSet<>(); 262 263 for (Content targetContent : _contentHelper.getTargetContents(content, _joinPaths)) 264 { 265 Object value = full ? _systemProperty.getJsonValue(targetContent, true) : _systemProperty.getValue(targetContent); 266 if (value != null) 267 { 268 if (value instanceof Collection<?>) 269 { 270 // Flatten values 271 values.addAll((Collection<?>) value); 272 } 273 else if (value instanceof Object[]) 274 { 275 // Flatten values 276 values.addAll(Arrays.asList((Object[]) value)); 277 } 278 else 279 { 280 values.add(value); 281 } 282 } 283 } 284 285 return values; 286 } 287 else 288 { 289 Content targetContent = _contentHelper.getTargetContent(content, _joinPaths); 290 if (targetContent != null) 291 { 292 return full ? _systemProperty.getJsonValue(_contentHelper.getTargetContent(content, _joinPaths), true) : _systemProperty.getValue(targetContent); 293 } 294 } 295 296 return null; 297 } 298 299 @Override 300 public SearchField getSearchField() 301 { 302 return _systemProperty.getSearchField(); 303 } 304 305 private String _initializeEnumerator(EnumeratorDefinition enumDef) 306 { 307 String role = null; 308 309 if (enumDef.isStatic()) 310 { 311 StaticEnumerator enumerator = new StaticEnumerator(); 312 enumDef.getStaticEntries().entrySet().forEach(entry -> enumerator.add(entry.getValue(), entry.getKey())); 313 setEnumerator(enumerator); 314 } 315 else 316 { 317 role = "enumerator"; 318 _enumeratorManager.addComponent("cms", null, role, enumDef.getEnumeratorClass(), enumDef.getConfiguration()); 319 } 320 321 return role; 322 } 323 324}