001/*
002 *  Copyright 2019 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.web.site;
017
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.Iterator;
021import java.util.Map;
022
023import org.apache.avalon.framework.activity.Disposable;
024import org.apache.avalon.framework.activity.Initializable;
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029
030import org.ametys.plugins.repository.UnknownAmetysObjectException;
031import org.ametys.runtime.i18n.I18nizableText;
032import org.ametys.runtime.model.ElementDefinition;
033import org.ametys.runtime.model.ModelHelper;
034import org.ametys.runtime.model.View;
035import org.ametys.runtime.model.disableconditions.DefaultDisableConditionsEvaluator;
036import org.ametys.runtime.model.disableconditions.DisableConditionsEvaluator;
037import org.ametys.runtime.model.exception.UndefinedItemPathException;
038import org.ametys.runtime.parameter.ValidationResult;
039import org.ametys.runtime.plugin.component.AbstractLogEnabled;
040import org.ametys.web.repository.site.Site;
041import org.ametys.web.repository.site.SiteManager;
042import org.ametys.web.repository.site.SiteType;
043import org.ametys.web.repository.site.SiteTypesExtensionPoint;
044
045/**
046 * Helper component for managing sites configuration.
047 */
048public class SiteConfigurationManager extends AbstractLogEnabled implements Component, Serviceable, Initializable, Disposable
049{
050    /** Avalon Role */
051    public static final String ROLE = SiteConfigurationManager.class.getName();
052    
053    /** The site manager. */
054    protected SiteManager _siteManager;
055    /** The site type extension point. */
056    protected SiteTypesExtensionPoint _siteTypesExtensionPoint;
057    /** The disable conditions evaluator */
058    protected DisableConditionsEvaluator _disableConditionsEvaluator;
059    
060    /** Determines if all parameters are valued, by site. */
061    protected Map<String, Boolean> _areSiteComplete;
062    
063    public void service(ServiceManager manager) throws ServiceException
064    {
065        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
066        _siteTypesExtensionPoint = (SiteTypesExtensionPoint) manager.lookup(SiteTypesExtensionPoint.ROLE);
067        _disableConditionsEvaluator = (DisableConditionsEvaluator) manager.lookup(DefaultDisableConditionsEvaluator.ROLE);
068    }
069
070    public void initialize() throws Exception
071    {
072        _areSiteComplete = new HashMap<>();
073    }
074    
075    public void dispose()
076    {
077        _areSiteComplete = new HashMap<>();
078    }
079    
080    /**
081     * Reload a site's configuration.
082     * @param siteName the site name.
083     * @throws UnknownAmetysObjectException if the site doesn't exist.
084     */
085    public void reloadSiteConfiguration(String siteName) throws UnknownAmetysObjectException
086    {
087        // Check if the site exists.
088        Site site = _siteManager.getSite(siteName);
089        
090        // Reload the site configuration.
091        reloadSiteConfiguration(site);
092    }
093    
094    /**
095     * Reload a site's configuration.
096     * @param site the site.
097     * @throws UnknownAmetysObjectException if the site doesn't exist.
098     */
099    public void reloadSiteConfiguration(Site site) throws UnknownAmetysObjectException
100    {
101        // Reload the site configuration.
102        _areSiteComplete.put(site.getName(), _isSiteConfigurationValid(site));
103    }
104
105    /**
106     * Remove a site's configuration.
107     * @param site the site.
108     */
109    public void removeSiteConfiguration(Site site)
110    {
111        removeSiteConfiguration(site.getName());
112    }
113
114    /**
115     * Remove a site's configuration.
116     * @param siteName the site name.
117     */
118    public void removeSiteConfiguration(String siteName)
119    {
120        if (_areSiteComplete.containsKey(siteName))
121        {
122            _areSiteComplete.remove(siteName);
123        }
124    }
125    
126    /**
127     * Validate the configuration of the given site.
128     * @param siteName the name of the site to check.
129     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
130     * @throws UnknownAmetysObjectException if the site doesn't exist.
131     */
132    public boolean isSiteConfigurationValid(String siteName) throws UnknownAmetysObjectException
133    {
134        if (siteName == null)
135        {
136            throw new IllegalArgumentException("Cannot determine if a null siteName is valid or not");
137        }
138
139        // Check if the site exists.
140        if (!_siteManager.hasSite(siteName))
141        {
142            throw new UnknownAmetysObjectException ("Unknown site '" + siteName + "'. Can not check configuration.");
143        }
144
145        // Validate the site configuration now if it's not already done.
146        Site site = _siteManager.getSite(siteName);
147        return isSiteConfigurationValid(site);
148    }
149    
150    /**
151     * Validate the configuration of the given site.
152     * @param site the site to check.
153     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
154     * @throws UnknownAmetysObjectException if the site doesn't exist.
155     */
156    public boolean isSiteConfigurationValid(Site site) throws UnknownAmetysObjectException
157    {
158        if (site == null)
159        {
160            throw new IllegalArgumentException("Cannot determine if a null site is valid or not");
161        }
162
163        // Validate the site configuration now if it's not already done.
164        if (!_areSiteComplete.containsKey(site.getName()))
165        {
166            _areSiteComplete.put(site.getName(), _isSiteConfigurationValid(site));
167        }
168
169        return _areSiteComplete.get(site.getName());
170    }
171
172    /**
173     * Validate the configuration of the given site.
174     * @param site the site to check.
175     * @return <code>true</code> if the site is correctly configured, <code>false</code> otherwise.
176     */
177    protected boolean _isSiteConfigurationValid(Site site)
178    {
179        if (getLogger().isDebugEnabled())
180        {
181            getLogger().debug("Validating the configuration of site '" + site + "'");
182        }
183
184        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
185        if (siteType == null)
186        {
187            getLogger().error("Site " + site.getName() + " has the unknown type '" + site.getType() + "'");
188            return false;
189        }
190        
191        boolean areParametersValid = true;
192
193        Iterator<ElementDefinition> definitionsIterator = siteType.getModelItems().iterator();
194        while (definitionsIterator.hasNext() && areParametersValid)
195        {
196            ElementDefinition item = definitionsIterator.next();
197            boolean isDisabled = _disableConditionsEvaluator.evaluateDisableConditions(item, item.getPath(), site);
198            if (!isDisabled)
199            {
200                areParametersValid = _isValidSiteParameter(item, site);
201            }
202        }
203        
204        return areParametersValid;
205    }
206    
207    /**
208     * Validate the the given site parameter
209     * @param parameter the site parameter to validate.
210     * @param site the site
211     * @return true if the parameter's value is valid, false otherwise.
212     */
213    protected boolean _isValidSiteParameter(ElementDefinition parameter, Site site)
214    {
215        // TODO RUNTIME-2897: call the validateValue without boolean when multiple values are managed in enumerators
216        Object value = site.getValue(parameter.getName(), true, null);
217        ValidationResult validationResult = ModelHelper.validateValue(parameter, value, false);
218        if (validationResult.hasErrors())
219        {
220            if (getLogger().isWarnEnabled())
221            {
222                StringBuffer sb = new StringBuffer();
223
224                sb.append("The parameter '")
225                    .append(parameter.getPath())
226                    .append("' of site '")
227                    .append(site.getName())
228                    .append("' is not valid");
229                if (site.hasValue(parameter.getName()))
230                {
231                    sb.append(" with value '");
232                    try
233                    {
234                        sb.append(parameter.getType().toString(value));
235                    }
236                    catch (Exception e)
237                    {
238                        sb.append(value.toString());
239                    }
240                    sb.append("'");
241                }
242                else
243                {
244                    sb.append(" with empty value");
245                }
246                sb.append(":");
247                
248                for (I18nizableText error : validationResult.getErrors())
249                {
250                    sb.append("\n* " + error.toString());
251                }
252                
253                sb.append("\nConfiguration is not initialized");
254
255                getLogger().warn(sb.toString());
256            }
257
258            return false;
259        }
260
261        return true;
262    }
263    
264    /**
265     * Retrieves the parameters of the given site
266     * @param siteName the name of the site
267     * @return the site's parameters
268     */
269    public Collection<ElementDefinition> getSiteParameters(String siteName)
270    {
271        Site site = _siteManager.getSite(siteName);
272        return getSiteParameters(site);
273    }
274    
275    /**
276     * Retrieves the parameters of the given site
277     * @param site the site
278     * @return the site's parameters
279     */
280    public Collection<ElementDefinition> getSiteParameters(Site site)
281    {
282        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
283        return siteType.getModelItems();
284    }
285    
286    /**
287     * Retrieves the parameter of the given site with the given name
288     * @param siteName the name of the site
289     * @param parameterName the name of the parameter to retrieve
290     * @return the site's parameter
291     * @throws UndefinedItemPathException if there is no site parameter defined with the given name
292     */
293    public ElementDefinition getSiteParameter(String siteName, String parameterName) throws UndefinedItemPathException
294    {
295        Site site = _siteManager.getSite(siteName);
296        return getSiteParameter(site, parameterName);
297    }
298    
299    /**
300     * Retrieves the parameter of the given site with the given name
301     * @param site the site
302     * @param parameterName the name of the parameter to retrieve
303     * @return the site's parameter
304     * @throws UndefinedItemPathException if there is no site parameter defined with the given name
305     */
306    public ElementDefinition getSiteParameter(Site site, String parameterName) throws UndefinedItemPathException
307    {
308        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
309        return siteType.getModelItem(parameterName);
310    }
311
312    /**
313     * Retrieves the view of the site with the given name
314     * @param siteName the name of the site
315     * @return the site's view
316     */
317    public View getSiteView(String siteName)
318    {
319        Site site = _siteManager.getSite(siteName);
320        return getSiteView(site);
321    }
322
323    /**
324     * Retrieves the view of the site with the given name
325     * @param site the site
326     * @return the site's view
327     */
328    public View getSiteView(Site site)
329    {
330        SiteType siteType = _siteTypesExtensionPoint.getExtension(site.getType());
331        return siteType.getView();
332    }
333}