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