001/*
002 *  Copyright 2010 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.clientsideelement;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.activity.Disposable;
024import org.apache.avalon.framework.component.ComponentException;
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;
032
033import org.ametys.core.ui.StaticClientSideElement;
034import org.ametys.runtime.i18n.I18nizableText;
035import org.ametys.runtime.parameter.Enumerator;
036import org.ametys.runtime.parameter.StaticEnumerator;
037import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
038
039/**
040 * Search tool HMI item
041 */
042public class SearchClientSideElement extends StaticClientSideElement implements Contextualizable, Disposable
043{
044    /** Manager component for enumerators */
045    protected ThreadSafeComponentManager<Enumerator> _enumeratorManager;
046    /** The service manager */
047    protected ServiceManager _manager;
048    /** The context. */
049    protected Context _context;
050    
051    private int _criteriaIndex;
052    private int _columnIndex;
053    private int _nbColumns;
054    private int _propertyIndex;
055    
056    private final Map<String, String> _enumeratorsToLookup = new HashMap<>();
057    
058    @Override
059    public void service(ServiceManager smanager) throws ServiceException
060    {
061        super.service(smanager);
062        _manager = smanager;
063    }
064    
065    @Override
066    public void contextualize(Context context) throws ContextException
067    {
068        _context = context;
069    }
070    
071    @Override
072    public void configure(Configuration configuration) throws ConfigurationException
073    {
074        _enumeratorManager = new ThreadSafeComponentManager<>();
075        _enumeratorManager.setLogger(getLogger());
076        _enumeratorManager.contextualize(_context);
077        _enumeratorManager.service(_manager);
078        
079        super.configure(configuration);
080        
081        _configureSearchCriteria(configuration.getChild("search-criteria"));
082        _configureColumns(configuration.getChild("columns"));
083        _configureProperties(configuration.getChild("property-mapping"));
084        _configurePageSize(configuration);
085        _configureSearchUrl(configuration);
086        _configureExportUrl(configuration);
087        
088        try
089        {
090            _enumeratorManager.initialize();
091        }
092        catch (Exception e)
093        {
094            throw new ConfigurationException("Unable to lookup parameter local components", configuration, e);
095        }
096    }
097    
098    @Override
099    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
100    {
101        List<Script> scripts = super.getScripts(ignoreRights, contextParameters);
102        
103        Map<String, Object> parameters = new HashMap<>();
104        List<ScriptFile> cssFiles = new ArrayList<>();
105        List<ScriptFile> scriptFiles = new ArrayList<>();
106        for (Script script : scripts)
107        {
108            cssFiles.addAll(script.getCSSFiles());
109            scriptFiles.addAll(script.getScriptFiles());
110            parameters.putAll(script.getParameters());
111        }
112        Script script = new Script(this.getId(), _script.getScriptClassname(), scriptFiles, cssFiles, parameters);
113
114        try
115        {
116            for (String prefix : _enumeratorsToLookup.keySet())
117            {
118                String className = _enumeratorsToLookup.get(prefix);
119                try
120                {
121                    Enumerator enumerator = _enumeratorManager.lookup(className);
122                    
123                    setEnumeratorValues(script, enumerator, prefix);
124                }
125                catch (ComponentException e)
126                {
127                    getLogger().error("Unable to instantiate enumerator for class: " + className, e);
128                }
129            }
130        }
131        catch (Exception e)
132        {
133            getLogger().error("Unable to lookup parameter local components", e);
134        }
135        
136        List<Script> result = new ArrayList<>();
137        result.add(script);
138        return result;
139    }
140    
141    /**
142     * Configure the search criteria
143     * @param configuration The configuration
144     * @throws ConfigurationException if the configuration is not valid.
145     */
146    protected void _configureSearchCriteria (Configuration configuration) throws ConfigurationException
147    {
148        for (Configuration criteriaConfiguration : configuration.getChildren("criteria"))
149        {
150            _configureCriteria (criteriaConfiguration, "criteria-" + _criteriaIndex);
151            _criteriaIndex++;
152        }
153        this._script.getParameters().put("criteria-size", _criteriaIndex);
154        this._script.getParameters().put("nb-columns", _nbColumns);
155    }
156    
157    /**
158     * Configures the criteria parameters
159     * @param configuration the item configuration
160     * @param prefix the criteria prefix
161     * @throws ConfigurationException if the configuration is not valid.
162     */
163    protected void _configureCriteria (Configuration configuration, String prefix) throws ConfigurationException
164    {
165        Map<String, Object> initialParameters = _script.getParameters();
166        String name = configuration.getAttribute("name");
167        String type = configuration.getAttribute("type", "string");
168        int column = configuration.getAttributeAsInteger("column", 1);
169        I18nizableText label = _configureLabel(configuration);
170        String testOperator = configuration.getChild("test-operator", true).getValue("wd");
171        String defaultValue = configuration.getChild("default-value", true).getValue("");
172        boolean metadata = configuration.getAttributeAsBoolean("metadata", true);
173        boolean multiple = configuration.getAttributeAsBoolean("multiple", false);
174        boolean mandatory = configuration.getAttributeAsBoolean("mandatory", false);
175        boolean hidden = configuration.getAttributeAsBoolean("hidden", false);
176        Enumerator staticEnum = _configureEnumerator(configuration, prefix + "-enumerator");
177        String widget = _configureWidget (configuration);
178        Map<String, Object> widgetParams = _configureWidgetParams(configuration);
179        String width = configuration.getChild("width", true).getValue(null);
180        
181        initialParameters.put(prefix + "-name", name);
182        initialParameters.put(prefix + "-type", type);
183        initialParameters.put(prefix + "-column", column);
184        initialParameters.put(prefix + "-label", label);
185        initialParameters.put(prefix + "-test-operator", testOperator);
186        initialParameters.put(prefix + "-default-value", defaultValue);
187        initialParameters.put(prefix + "-metadata", metadata);
188        initialParameters.put(prefix + "-hidden", hidden);
189        initialParameters.put(prefix + "-multiple", multiple);
190        initialParameters.put(prefix + "-mandatory", mandatory);
191        
192        if (width != null)
193        {
194            initialParameters.put(prefix + "-width", width);
195        }
196        
197        if (widget != null)
198        {
199            initialParameters.put(prefix + "-widget", widget);
200        }
201        
202        initialParameters.put(prefix + "-widget-param-size", widgetParams.size());
203        
204        if (widgetParams.size() > 0)
205        {
206            int i = 0;
207            for (String paramName : widgetParams.keySet())
208            {
209                initialParameters.put(prefix + "-widget-param-" + i, paramName);
210                initialParameters.put(prefix + "-widget-param-" + i + "-value", widgetParams.get(paramName));
211                i++;
212            }
213        }
214        
215        if (staticEnum != null)
216        {
217            setEnumeratorValues(_script, staticEnum, prefix + "-enumerator");
218        }
219        
220        if (column > _nbColumns)
221        {
222            _nbColumns = column;
223        }
224    }
225    
226    /**
227     * Set the enumerator values in script parameters
228     * @param script The script into which set the parameters
229     * @param enumerator The enumerator
230     * @param prefix The parameters prefix
231     */
232    protected void setEnumeratorValues (Script script, Enumerator enumerator, String prefix)
233    {
234        try
235        {
236            script.getParameters().put(prefix, true);
237            Map<Object, I18nizableText> entries = enumerator.getEntries();
238            int index = 0;
239            for (Object value : entries.keySet())
240            {
241                script.getParameters().put(prefix + "-" + index + "-value", value);
242                if (entries.get(value) != null)
243                {
244                    script.getParameters().put(prefix + "-" + index + "-label", entries.get(value));
245                }
246                else
247                {
248                    script.getParameters().put(prefix + "-" + "-label", value);
249                }
250                index++;
251            }
252            
253            script.getParameters().put(prefix + "-size", index);
254        }
255        catch (Exception e)
256        {
257            getLogger().error("Unable to set values for enumerator " + enumerator.getClass().getName(), e);
258        }
259    }
260    /**
261     * Configure the columns
262     * @param configuration The configuration
263     * @throws ConfigurationException if the configuration is not valid.
264     */
265    protected void _configureColumns (Configuration configuration) throws ConfigurationException
266    {
267        for (Configuration colConfiguration : configuration.getChildren("column"))
268        {
269            _configureColumn (colConfiguration, "column-" + _columnIndex);
270            _columnIndex++;
271        }
272        
273        Configuration groupBy = configuration.getChild("group-by", false);
274        if (groupBy != null)
275        {
276            this._script.getParameters().put("group-by", groupBy.getValue());
277        }
278        
279        Configuration sortBy = configuration.getChild("sort-by", false);
280        if (sortBy != null)
281        {
282            this._script.getParameters().put("sort-by", sortBy.getValue());
283        }
284        
285        this._script.getParameters().put("column-size", _columnIndex);
286    }
287    
288    /**
289     * Configures the criteria parameters
290     * @param configuration the item configuration
291     * @param prefix the criteria prefix
292     * @throws ConfigurationException if the configuration is not valid.
293     */
294    protected void _configureColumn (Configuration configuration, String prefix) throws ConfigurationException
295    {
296        Map<String, Object> initialParameters = _script.getParameters();
297        String id = configuration.getAttribute("id");
298        I18nizableText label = _configureLabel(configuration);
299        String width = configuration.getChild("width", true).getValue("");
300        
301        initialParameters.put(prefix + "-id", id);
302        initialParameters.put(prefix + "-width", width);
303        initialParameters.put(prefix + "-label", label);
304        
305        String renderFn = configuration.getChild("renderer", true).getValue(null);
306        if (renderFn != null)
307        {
308            initialParameters.put(prefix + "-renderer", renderFn);
309        }
310        
311        boolean sortable = configuration.getAttributeAsBoolean("sortable", true);
312        boolean hideable = configuration.getAttributeAsBoolean("hideable", true);
313        boolean hidden = configuration.getAttributeAsBoolean("hidden", false);
314        
315        initialParameters.put(prefix + "-sortable", sortable);
316        initialParameters.put(prefix + "-hideable", hideable);
317        initialParameters.put(prefix + "-hidden", hidden);
318    }
319    
320    /**
321     * Configure the columns
322     * @param configuration The configuration
323     * @throws ConfigurationException if the configuration is not valid.
324     */
325    protected void _configureProperties (Configuration configuration) throws ConfigurationException
326    {
327        for (Configuration propConfiguration : configuration.getChildren("property"))
328        {
329            _configureProperty (propConfiguration, "property-" + _propertyIndex);
330            _propertyIndex++;
331        }
332        this._script.getParameters().put("property-size", _propertyIndex);
333    }
334    
335    /**
336     * Configures the criteria parameters
337     * @param configuration the item configuration
338     * @param prefix the criteria prefix
339     * @throws ConfigurationException if the configuration is not valid.
340     */
341    protected void _configureProperty (Configuration configuration, String prefix) throws ConfigurationException
342    {
343        String id = configuration.getAttribute("id");
344        String path = configuration.getAttribute("path");
345        String type = configuration.getAttribute("type", "string");
346        
347        this._script.getParameters().put(prefix + "-id", id);
348        this._script.getParameters().put(prefix + "-path", path);
349        this._script.getParameters().put(prefix + "-type", type);
350    }
351    
352    /**
353     * Configures the label.
354     * @param configuration the configuration to use.
355     * @return the i18n text.
356     * @throws ConfigurationException if the configuration is not valid.
357     */
358    protected I18nizableText _configureLabel (Configuration configuration) throws ConfigurationException
359    {
360        Configuration labelConfig = configuration.getChild("label");
361        boolean i18nSupported = labelConfig.getAttributeAsBoolean("i18n", false);
362        
363        if (i18nSupported)
364        {
365            String catalogue = labelConfig.getAttribute("catalogue", null);
366            if (catalogue == null)
367            {
368                catalogue = "plugin." + _pluginName;
369            }
370            
371            return new I18nizableText(catalogue, labelConfig.getValue());
372        }
373        else
374        {
375            return new I18nizableText(labelConfig.getValue(""));
376        }
377    }
378    
379    /**
380     * Configures the enumerator
381     * @param configuration The configuration to use
382     * @param prefix The prefix
383     * @return The static enumerator or null.
384     * @throws ConfigurationException If the configuraiton has an issue
385     */
386    @SuppressWarnings("unchecked")
387    protected Enumerator _configureEnumerator (Configuration configuration, String prefix) throws ConfigurationException
388    {
389        Configuration enumeratorConfig = configuration.getChild("enumeration", false);
390        if (enumeratorConfig != null)
391        {
392            Configuration customEnumerator = enumeratorConfig.getChild("custom-enumerator", false);
393            if (customEnumerator != null)
394            {
395                String criterionName = configuration.getAttribute("name");
396                String enumeratorClassName = customEnumerator.getAttribute("class");
397                String role = criterionName + "$" + enumeratorClassName;
398                
399                try
400                {
401                    Class enumeratorClass = Class.forName(enumeratorClassName);
402                    _enumeratorManager.addComponent(_pluginName, null, role, enumeratorClass, configuration);
403                }
404                catch (Exception e)
405                {
406                    throw new ConfigurationException("Unable to instantiate enumerator for class: " + enumeratorClassName, e);
407                }
408
409                _enumeratorsToLookup.put(prefix, role);
410            }
411            else
412            {
413                StaticEnumerator staticEnumerator = new StaticEnumerator();
414                for (Configuration entryConfig : enumeratorConfig.getChildren("entry"))
415                {
416                    String value = entryConfig.getChild("value").getValue("");
417                    I18nizableText label = null;
418                    
419                    if (entryConfig.getChild("label", false) != null)
420                    {
421                        label = _configureLabel(entryConfig);
422                    }
423                    staticEnumerator.add(label, value);
424                }
425                return staticEnumerator;
426            }
427        }
428        
429        return null;
430    }
431    
432    /**
433     * Configures the widget
434     * @param configuration The configuration to use
435     * @return The widget id
436     * @throws ConfigurationException if the configuration is not valid.
437     */
438    protected String _configureWidget (Configuration configuration) throws ConfigurationException
439    {
440        Configuration widgetConfig = configuration.getChild("widget", false);
441        if (widgetConfig != null)
442        {
443            return widgetConfig.getValue("*");
444        }
445        
446        return null;
447    }
448    
449    /**
450     * Configures the widget
451     * @param configuration The configuration to use
452     * @return The widget id
453     * @throws ConfigurationException if the configuration is not valid.
454     */
455    protected Map<String, Object> _configureWidgetParams (Configuration configuration) throws ConfigurationException
456    {
457        Map<String, Object> widgetParams = new HashMap<>();
458        
459        Configuration widgetConfig = configuration.getChild("widget-params", false);
460        if (widgetConfig != null)
461        {
462            Configuration[] params = widgetConfig.getChildren("param");
463            for (Configuration paramConfig : params)
464            {
465                boolean i18nSupported = paramConfig.getAttributeAsBoolean("i18n", false);
466                if (i18nSupported)
467                {
468                    String catalogue = paramConfig.getAttribute("catalogue", null);
469                    if (catalogue == null)
470                    {
471                        catalogue = "plugin." + _pluginName;
472                    }
473                    
474                    widgetParams.put(paramConfig.getAttribute("name"), new I18nizableText(catalogue, paramConfig.getValue()));
475                }
476                else
477                {
478                    widgetParams.put(paramConfig.getAttribute("name"), paramConfig.getValue(""));
479                }
480            }
481        }
482        
483        return widgetParams;
484    }
485    
486    /**
487     * Configures the result page size.
488     * @param configuration the configuration to use.
489     * @throws ConfigurationException if the configuration is not valid.
490     */
491    protected void _configurePageSize (Configuration configuration) throws ConfigurationException
492    {
493        Configuration pageSizeConfig = configuration.getChild("nb-result-per-page", false);
494        if (pageSizeConfig != null)
495        {
496            this._script.getParameters().put("page-size", pageSizeConfig.getValue());
497        }
498    }
499    
500    /**
501     * Configure the URL used for search
502     * @param configuration The configuration to use.
503     * @throws ConfigurationException if the configuration is not valid.
504     */
505    protected void _configureSearchUrl  (Configuration configuration) throws ConfigurationException
506    {
507        Configuration urlConfig = configuration.getChild("search-url", false);
508        if (urlConfig != null)
509        {
510            String pluginName = urlConfig.getAttribute("plugin", _pluginName);
511            this._script.getParameters().put("search-url", "/plugins/" + pluginName + "/" + urlConfig.getValue());
512        }
513    }
514    
515    /**
516     * Configure the URL used for XSL export
517     * @param configuration The configuration to use.
518     * @throws ConfigurationException if the configuration is not valid.
519     */
520    protected void _configureExportUrl  (Configuration configuration) throws ConfigurationException
521    {
522        Configuration urlConfig = configuration.getChild("export-url", false);
523        if (urlConfig != null)
524        {
525            String pluginName = urlConfig.getAttribute("plugin", _pluginName);
526            this._script.getParameters().put("export-url", "/plugins/" + pluginName + "/" + urlConfig.getValue());
527        }
528    }
529    
530    @Override
531    public void dispose()
532    {
533        _enumeratorManager.dispose();
534        _enumeratorManager = null;
535    }
536}