001/* 002 * Copyright 2017 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.Collection; 019import java.util.Locale; 020 021import org.apache.avalon.framework.service.ServiceException; 022import org.apache.avalon.framework.service.ServiceManager; 023import org.apache.avalon.framework.service.Serviceable; 024 025import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 026import org.ametys.cms.contenttype.ContentTypesHelper; 027import org.ametys.cms.contenttype.MetadataDefinition; 028import org.ametys.cms.contenttype.MetadataType; 029import org.ametys.cms.contenttype.RepeaterDefinition; 030import org.ametys.cms.repository.Content; 031import org.ametys.cms.search.SearchField; 032import org.ametys.cms.search.content.ContentSearchHelper; 033import org.ametys.cms.search.model.MetadataResultField; 034import org.ametys.runtime.i18n.I18nizableText; 035 036/** 037 * Default implementation of a search ui column for the metadata of a content 038 */ 039public class DefaultMetadataSearchUIColumn extends AbstractSearchUIColumn implements MetadataResultField, Serviceable 040{ 041 /** The content type extension point. */ 042 protected ContentTypeExtensionPoint _cTypeEP; 043 044 /** The content type helper. */ 045 protected ContentTypesHelper _cTypeHelper; 046 047 /** The search helper. */ 048 protected ContentSearchHelper _searchHelper; 049 050 /** The full metadata path. */ 051 protected String _fullMetadataPath; 052 053 /** True if the metadata is joined, false otherwise. */ 054 protected boolean _joinedMetadata; 055 056 /** The content types of the contents on which this metadata column applies */ 057 protected Collection<String> _contentTypes; 058 059 /** 060 * Default empty constructor 061 */ 062 public DefaultMetadataSearchUIColumn() 063 { 064 // 065 } 066 067 /** 068 * Default constructor. Fill the search ui column from the metadata definition 069 * @param metadataDefinition The metadata definition 070 * @param fullMetadataPath The full metadata path 071 * @param contentTypes The content types of the contents on which this metadata column applies 072 */ 073 public DefaultMetadataSearchUIColumn(MetadataDefinition metadataDefinition, String fullMetadataPath, Collection<String> contentTypes) 074 { 075 _fullMetadataPath = fullMetadataPath; 076 077 _configure(metadataDefinition, metadataDefinition.getLabel(), metadataDefinition.getDescription(), 200, false, true, false, metadataDefinition.isMultiple(), false, null, null, null, contentTypes, false); 078 } 079 080 @Override 081 public void service(ServiceManager manager) throws ServiceException 082 { 083 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 084 _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 085 _searchHelper = (ContentSearchHelper) manager.lookup(ContentSearchHelper.ROLE); 086 } 087 088 /** 089 * Configure the metadata search ui column 090 * @param metadataDefinition The definition 091 * @param label The column label 092 * @param description The column description 093 * @param width The column width 094 * @param hidden True if the column should be hidden 095 * @param editable True if the column is editable 096 * @param sortable True to allow sorting the column 097 * @param multiple True if the value is multiple 098 * @param multiLevelMultiple If the metadata path contains a multiple metadata or a repeater 099 * @param defaultSorter The default column sorter 100 * @param renderer A specific renderer to use. If null, it will be deduced from the metadata definition 101 * @param converter A specific converter to use. If null, it will be deduced from the metadata definition 102 * @param contentTypes The content types of the contents on which this metadata column applies 103 * @param allowSortOnMultipleJoin True to allow sort when the join path contains at least one mutliple metadata (intermediates only) 104 */ 105 protected void _configure(MetadataDefinition metadataDefinition, I18nizableText label, I18nizableText description, int width, boolean hidden, boolean editable, boolean sortable, boolean multiple, boolean multiLevelMultiple, String defaultSorter, String renderer, String converter, Collection<String> contentTypes, boolean allowSortOnMultipleJoin) 106 { 107 MetadataType type = metadataDefinition.getType(); 108 109 setId(_fullMetadataPath); 110 setLabel(label); 111 setDescription(description); 112 setType(type); 113 setWidth(width); 114 setHidden(hidden); 115 // The metadata is not editable if it is on another content. 116 setEditable(editable && !multiLevelMultiple && isEditionAllowed(metadataDefinition)); 117 boolean finalMetaDefMultiple = metadataDefinition.isMultiple() || metadataDefinition instanceof RepeaterDefinition; 118 boolean isSortable = sortable 119 && (allowSortOnMultipleJoin && !finalMetaDefMultiple // if path is not multiple, but an intermediate in the path is, it is OK => consider as sortable 120 || !allowSortOnMultipleJoin && !multiple) // if path is multiple => do not consider as sortable 121 && _isSortableMetadata(metadataDefinition); 122 setSortable(isSortable); 123 if (isSortable) 124 { 125 setDefaultSorter(defaultSorter); 126 } 127 setValidator(metadataDefinition.getValidator()); 128 setEnumerator(metadataDefinition.getEnumerator()); 129 setWidget(metadataDefinition.getWidget()); 130 setWidgetParameters(metadataDefinition.getWidgetParameters()); 131 setMultiple(multiple); 132 setContentTypeId(metadataDefinition.getContentType()); 133 134 configureRenderer(renderer, metadataDefinition); 135 configureConverter(converter, metadataDefinition); 136 137 _contentTypes = contentTypes; 138 } 139 140 /** 141 * Determines if the inline edition is allowed 142 * @param definition The metadata definition 143 * @return true if the metadata is editable 144 */ 145 protected boolean isEditionAllowed(MetadataDefinition definition) 146 { 147 if (_joinedMetadata) 148 { 149 // metadata is not editable if it is on another content 150 return false; 151 } 152 153 switch (definition.getType()) 154 { 155 case COMPOSITE: 156 case RICH_TEXT: 157 // richtext and composite are never editable inline 158 return false; 159 default: 160 break; 161 } 162 163 return true; 164 } 165 166 /** 167 * Configure the column renderer. 168 * @param renderer A specific renderer. If null, it will be deduced from the metadata definition. 169 * @param metadataDefinition The metadata definition. 170 */ 171 protected void configureRenderer(String renderer, MetadataDefinition metadataDefinition) 172 { 173 MetadataType type = metadataDefinition.getType(); 174 if (renderer != null) 175 { 176 setRenderer(renderer); 177 } 178 else if ("title".equals(_fullMetadataPath) && type == MetadataType.STRING) 179 { 180 setRenderer("Ametys.plugins.cms.search.SearchGridHelper.renderTitle"); 181 } 182 else if ("title".equals(_fullMetadataPath) && type == MetadataType.MULTILINGUAL_STRING) 183 { 184 setRenderer("Ametys.plugins.cms.search.SearchGridHelper.renderMultilingualTitle"); 185 } 186 else if (metadataDefinition instanceof RepeaterDefinition) 187 { 188 setRenderer("Ametys.plugins.cms.search.SearchGridHelper.renderRepeater"); 189 } 190 } 191 192 /** 193 * Configure the column converter. 194 * @param converter A specific converter. If null, it will be deduced from the metadata definition. 195 * @param metadataDefinition The metadata definition. 196 */ 197 protected void configureConverter(String converter, MetadataDefinition metadataDefinition) 198 { 199 MetadataType type = metadataDefinition.getType(); 200 if (converter != null) 201 { 202 setConverter(converter); 203 } 204 else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) 205 { 206 setConverter("Ametys.plugins.cms.search.SearchGridHelper.convertContent"); 207 } 208 else if (metadataDefinition instanceof RepeaterDefinition) 209 { 210 setConverter("Ametys.plugins.cms.search.SearchGridHelper.convertRepeater"); 211 } 212 } 213 214 private boolean _isSortableMetadata(MetadataDefinition metadataDefinition) 215 { 216 switch (metadataDefinition.getType()) 217 { 218 case STRING: 219 case MULTILINGUAL_STRING: 220 case LONG: 221 case DATE: 222 case DATETIME: 223 case BOOLEAN: 224 case CONTENT: 225 case SUB_CONTENT: 226 case DOUBLE: 227 case USER: 228 return true; 229 case COMPOSITE: 230 case BINARY: 231 case FILE: 232 case RICH_TEXT: 233 case REFERENCE: 234 return false; 235 default: 236 return false; 237 } 238 } 239 240 /** 241 * Set the metadata path 242 * @param metadataPath the path to metadata 243 */ 244 public void setFieldPath(String metadataPath) 245 { 246 _fullMetadataPath = metadataPath; 247 } 248 249 /** 250 * Get the path of metadata (separated by '/') 251 * @return the path of metadata 252 */ 253 public String getFieldPath() 254 { 255 return _fullMetadataPath; 256 } 257 258 @Override 259 public Object getValue(Content content, Locale defaultLocale) 260 { 261 return _searchHelper.getMetadataValue(content, _fullMetadataPath, getType(), isMultiple(), getEnumerator(), defaultLocale, false); 262 } 263 264 @Override 265 public Object getFullValue(Content content, Locale defaultLocale) 266 { 267 return _searchHelper.getMetadataValue(content, _fullMetadataPath, getType(), isMultiple(), getEnumerator(), defaultLocale, true); 268 } 269 270 @Override 271 public SearchField getSearchField() 272 { 273 if (_joinedMetadata) 274 { 275 return _searchHelper.getSearchField(_contentTypes, _fullMetadataPath); 276 } 277 else 278 { 279 return _searchHelper.getMetadataSearchField(getFieldPath(), getType()); 280 } 281 } 282}