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}