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