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