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.Collections; 019import java.util.Iterator; 020import java.util.List; 021 022import org.apache.avalon.framework.configuration.Configurable; 023import org.apache.avalon.framework.configuration.Configuration; 024import org.apache.avalon.framework.configuration.ConfigurationException; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028 029import org.ametys.cms.contenttype.ContentType; 030import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 031import org.ametys.cms.contenttype.ContentTypesHelper; 032import org.ametys.cms.contenttype.MetadataDefinition; 033import org.ametys.cms.contenttype.MetadataType; 034import org.ametys.cms.contenttype.RepeaterDefinition; 035import org.ametys.cms.repository.Content; 036import org.ametys.cms.search.SearchField; 037import org.ametys.cms.search.content.ContentSearchHelper; 038import org.ametys.cms.search.model.MetadataResultField; 039 040/** 041 * This class is a search column on a metadata of a content 042 * 043 */ 044public class MetadataSearchUIColumn extends AbstractSearchUIColumn implements MetadataResultField, Serviceable, Configurable 045{ 046 /** The content type extension point. */ 047 protected ContentTypeExtensionPoint _cTypeEP; 048 049 /** The content type helper. */ 050 protected ContentTypesHelper _cTypeHelper; 051 052 /** The search helper. */ 053 protected ContentSearchHelper _searchHelper; 054 055 /** The full metadata path. */ 056 protected String _fullMetadataPath; 057 058 /** True if the metadata is joined, false otherwise. */ 059 protected boolean _joinedMetadata; 060 061 /** The ID of the content type which contains the metadata. */ 062 protected String _contentTypeId; 063 064 @Override 065 public void service(ServiceManager manager) throws ServiceException 066 { 067 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 068 _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 069 _searchHelper = (ContentSearchHelper) manager.lookup(ContentSearchHelper.ROLE); 070 } 071 072 @Override 073 public void configure(Configuration configuration) throws ConfigurationException 074 { 075 // The full path can contain "join" paths. 076 _fullMetadataPath = configuration.getChild("metadata").getAttribute("path"); 077 _contentTypeId = configuration.getChild("contentTypes").getAttribute("baseId", null); 078 079 List<MetadataDefinition> metadataDefinitions = _configureMetadataDefinitions(); 080 081 if (metadataDefinitions.isEmpty()) 082 { 083 throw new ConfigurationException("Unknown metadata '" + _fullMetadataPath + "' in content type '" + _contentTypeId + "'"); 084 } 085 086 // Compute "join" and "multiple" status. 087 _joinedMetadata = false; 088 boolean multiple = false; 089 boolean multiLevelMultiple = false; 090 Iterator<MetadataDefinition> defIt = metadataDefinitions.iterator(); 091 while (defIt.hasNext()) 092 { 093 MetadataDefinition metaDef = defIt.next(); 094 MetadataType type = metaDef.getType(); 095 // The column has multiple values if the full path contains a multiple metadata or a repeater. 096 if (metaDef.isMultiple() || metaDef instanceof RepeaterDefinition) 097 { 098 multiple = true; 099 if (defIt.hasNext()) 100 { 101 multiLevelMultiple = true; 102 } 103 } 104 // The column represents a "joined" value if it has a content metadata (except if it's the last one). 105 if ((type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) && defIt.hasNext()) 106 { 107 _joinedMetadata = true; 108 } 109 } 110 111 MetadataDefinition metadataDefinition = metadataDefinitions.get(metadataDefinitions.size() - 1); 112 MetadataType type = metadataDefinition.getType(); 113 setId(_fullMetadataPath); 114 setLabel(_configureI18nizableText(configuration.getChild("label", false), metadataDefinition.getLabel())); 115 setDescription(_configureI18nizableText(configuration.getChild("description", false), metadataDefinition.getDescription())); 116 setType(type); 117 setWidth(configuration.getChild("width").getValueAsInteger(200)); 118 setHidden(configuration.getChild("hidden").getValueAsBoolean(false)); 119 // The metadata is not editable if it is on another content. 120 setEditable(configuration.getChild("editable").getValueAsBoolean(true) && !_joinedMetadata && !multiLevelMultiple); 121 // The metadata is not sortable if it is on another content. 122 boolean isSortable = configuration.getChild("sortable").getValueAsBoolean(true) 123 && _isSortableMetadata(metadataDefinition) 124 && !_joinedMetadata; 125 setSortable(isSortable); 126 if (isSortable) 127 { 128 setDefaultSorter(configuration.getChild("default-sorter").getValue(null)); 129 } 130 setValidator(metadataDefinition.getValidator()); 131 setEnumerator(metadataDefinition.getEnumerator()); 132 setWidget(metadataDefinition.getWidget()); 133 setWidgetParameters(metadataDefinition.getWidgetParameters()); 134 setMultiple(multiple); 135 setContentTypeId(metadataDefinition.getContentType()); 136 137 configureRenderer(configuration, type); 138 configureConverter(configuration, type); 139 } 140 141 private List<MetadataDefinition> _configureMetadataDefinitions() throws ConfigurationException 142 { 143 List<MetadataDefinition> metadataDefinitions; 144 145 if (_contentTypeId != null) 146 { 147 ContentType cType = _cTypeEP.getExtension(_contentTypeId); 148 metadataDefinitions = _cTypeHelper.getMetadataDefinitionPath(_fullMetadataPath, cType); 149 } 150 else if ("title".equals(_fullMetadataPath)) 151 { 152 // Only the title metadata is allowed if the base content type is null. 153 metadataDefinitions = Collections.singletonList(ContentTypesHelper.getTitleMetadataDefinition()); 154 } 155 else 156 { 157 throw new ConfigurationException("The metadata '" + _fullMetadataPath + "' is forbidden when no content type is specified: only title can be used."); 158 } 159 160 return metadataDefinitions; 161 } 162 163 /** 164 * Configure the column renderer. 165 * @param configuration The column configuration. 166 * @param type The metadata type. 167 */ 168 protected void configureRenderer(Configuration configuration, MetadataType type) 169 { 170 String renderer = configuration.getChild("renderer").getValue(null); 171 if (renderer != null) 172 { 173 setRenderer(renderer); 174 } 175 else if ("title".equals(_fullMetadataPath)) 176 { 177 setRenderer("Ametys.cms.content.EditContentsGrid.renderTitle"); 178 } 179 } 180 181 /** 182 * Configure the column converter. 183 * @param configuration The column configuration. 184 * @param type The metadata type. 185 */ 186 protected void configureConverter(Configuration configuration, MetadataType type) 187 { 188 String converter = configuration.getChild("converter").getValue(null); 189 if (converter != null) 190 { 191 setConverter(converter); 192 } 193 else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) 194 { 195 setConverter("Ametys.cms.content.EditContentsGrid.convertContent"); 196 } 197 else if (type == MetadataType.USER) 198 { 199 setConverter("Ametys.cms.content.EditContentsGrid.convertUser"); 200 } 201 } 202 203 private boolean _isSortableMetadata(MetadataDefinition metadataDefinition) 204 { 205 switch (metadataDefinition.getType()) 206 { 207 case STRING: 208 case LONG: 209 case DATE: 210 case DATETIME: 211 case BOOLEAN: 212 case CONTENT: 213 case SUB_CONTENT: 214 case DOUBLE: 215 case USER: 216 return true; 217 case COMPOSITE: 218 case BINARY: 219 case FILE: 220 case RICH_TEXT: 221 case REFERENCE: 222 return false; 223 default: 224 return false; 225 } 226 } 227 228 /** 229 * Set the metadata path 230 * @param metadataPath the path to metadata 231 */ 232 public void setFieldPath(String metadataPath) 233 { 234 _fullMetadataPath = metadataPath; 235 } 236 237 /** 238 * Get the path of metadata (separated by '/') 239 * @return the path of metadata 240 */ 241 public String getFieldPath() 242 { 243 return _fullMetadataPath; 244 } 245 246 @Override 247 public Object getValue(Content content) 248 { 249 return _searchHelper.getMetadataValues(content, _fullMetadataPath, getType(), isMultiple(), getEnumerator(), false); 250 } 251 252 @Override 253 public Object getFullValue(Content content) 254 { 255 return _searchHelper.getMetadataValues(content, _fullMetadataPath, getType(), isMultiple(), getEnumerator(), true); 256 } 257 258 @Override 259 public SearchField getSearchField() 260 { 261 if (!_joinedMetadata) 262 { 263 return _searchHelper.getMetadataSearchField(getFieldPath(), getType()); 264 } 265 266 return null; 267 } 268 269}