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}