001/*
002 *  Copyright 2025 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.trash.model;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.configuration.ConfigurationException;
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030
031import org.ametys.cms.data.holder.DataHolderElementDefinitionParser;
032import org.ametys.plugins.repository.AmetysRepositoryException;
033import org.ametys.plugins.repository.data.type.ModelItemTypeExtensionPoint;
034import org.ametys.plugins.repository.trash.TrashElementType;
035import org.ametys.plugins.repository.trash.TrashElementTypeEnumerator;
036import org.ametys.plugins.repository.trash.TrashElementTypeExtensionPoint;
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.runtime.model.DefaultElementDefinition;
039import org.ametys.runtime.model.ElementDefinition;
040import org.ametys.runtime.model.Enumerator;
041import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName;
042import org.ametys.runtime.model.Model;
043import org.ametys.runtime.model.ModelItem;
044import org.ametys.runtime.model.disableconditions.DisableConditions;
045import org.ametys.runtime.model.type.ModelItemTypeConstants;
046import org.ametys.runtime.parameter.Validator;
047import org.ametys.runtime.plugin.component.AbstractLogEnabled;
048import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
049
050/**
051 * The model for trash elements.
052 */
053public class TrashElementModel extends AbstractLogEnabled implements Model, Component, Serviceable, Contextualizable
054{
055    /** The avalon role */
056    public static final String ROLE = TrashElementModel.class.getName();
057    
058    /** Item name for the title of the element */
059    public static final String TITLE = "title";
060    /** Item name for the type of the element */
061    public static final String TRASH_TYPE = "trashType";
062    /** Item name for the author of the deletion */
063    public static final String AUTHOR = "author";
064    /** Item name for the deletion date */
065    public static final String DATE = "date";
066    /** Item name for the parent path of the object */
067    public static final String PARENT_PATH = "parentPath";
068    /** Item name for the reference to the deleted object */
069    public static final String DELETED_OBJECT = "deletedObject";
070    /** Item name for the reference to the deleted object */
071    public static final String HIDDEN = "hidden";
072    /** Item name for the reference to the linked objects */
073    public static final String LINKED_OBJECTS = "linkedObjects";
074    
075    /** The trash element type enumerator */
076    protected TrashElementTypeEnumerator _trashElementTypeEnum;
077    /** The trash element type extension point */
078    protected TrashElementTypeExtensionPoint _trashElementTypeEP;
079    /** The model item type */
080    protected ModelItemTypeExtensionPoint _modelAwareItemTypeEP;
081    
082    private Collection<? extends ModelItem> _modelItems;
083    
084    /** Component manager for the model */
085    private ThreadSafeComponentManager<Enumerator> _enumeratorsManager;
086    private ThreadSafeComponentManager<DisableConditions> _disableConditionsManager;
087    private ThreadSafeComponentManager<Validator> _validatorsManager;
088
089    /** the avalon context */
090    private Context _context;
091    
092    public void contextualize(Context context) throws ContextException
093    {
094        _context = context;
095    }
096    
097    public void service(ServiceManager manager) throws ServiceException
098    {
099        _trashElementTypeEP = (TrashElementTypeExtensionPoint) manager.lookup(TrashElementTypeExtensionPoint.ROLE);
100        _trashElementTypeEnum = (TrashElementTypeEnumerator) manager.lookup(TrashElementTypeEnumerator.ROLE);
101        
102        _modelAwareItemTypeEP = (ModelItemTypeExtensionPoint) manager.lookup(ModelItemTypeExtensionPoint.ROLE_MODEL_AWARE_BASIC);
103        
104        _enumeratorsManager = new ThreadSafeComponentManager<>();
105        _enumeratorsManager.setLogger(getLogger());
106        _enumeratorsManager.contextualize(_context);
107        _enumeratorsManager.service(manager);
108        
109        _disableConditionsManager = new ThreadSafeComponentManager<>();
110        _disableConditionsManager.setLogger(getLogger());
111        _disableConditionsManager.contextualize(_context);
112        _disableConditionsManager.service(manager);
113        
114        _validatorsManager = new ThreadSafeComponentManager<>();
115        _validatorsManager.setLogger(getLogger());
116        _validatorsManager.contextualize(_context);
117        _validatorsManager.service(manager);
118    }
119    
120    public String getId()
121    {
122        return "TrashElement";
123    }
124
125    public String getFamilyId()
126    {
127        return "Trash";
128    }
129
130    public Collection<? extends ModelItem> getModelItems()
131    {
132        if (_modelItems == null)
133        {
134            try
135            {
136                _modelItems = List.copyOf(createModelItems());
137            }
138            catch (Exception e)
139            {
140                throw new AmetysRepositoryException("An error occurred while create the trash element model", e);
141            }
142        }
143        return _modelItems;
144    }
145
146    /**
147     * Create the model items present in the trash element model
148     * @return the model items
149     * @throws Exception if an occurs when creating the model items
150     */
151    protected List<ElementDefinition> createModelItems() throws Exception
152    {
153        List<ElementDefinition> items = new ArrayList<>();
154        
155        items.add(_getDefinition(TITLE, ModelItemTypeConstants.STRING_TYPE_ID, false));
156        ElementDefinition<String> trashType = _getDefinition(TRASH_TYPE, ModelItemTypeConstants.STRING_TYPE_ID, false);
157        trashType.setEnumerator(_trashElementTypeEnum);
158        items.add(trashType);
159        
160        items.add(_getDefinition(DATE, ModelItemTypeConstants.DATETIME_TYPE_ID, false));
161        items.add(_getDefinition(AUTHOR, org.ametys.cms.data.type.ModelItemTypeConstants.USER_ELEMENT_TYPE_ID, false));
162        items.add(_getDefinition(PARENT_PATH, ModelItemTypeConstants.STRING_TYPE_ID, false));
163        items.add(_getDefinition(DELETED_OBJECT, ModelItemTypeConstants.STRING_TYPE_ID, false));
164        items.add(_getDefinition(HIDDEN, ModelItemTypeConstants.BOOLEAN_TYPE_ID, false));
165        items.add(_getDefinition(LINKED_OBJECTS, ModelItemTypeConstants.STRING_TYPE_ID, true));
166        
167        // There is no check done on the definition path so watch out for conflict
168        items.addAll(_getModelItemsFromType());
169        
170        return items;
171    }
172
173    private List<ElementDefinition> _getModelItemsFromType() throws ConfigurationException, Exception
174    {
175        // Add model items provided by the element types
176        List<ElementDefinition> typeItems = new ArrayList<>();
177        DataHolderElementDefinitionParser definitionParser = new DataHolderElementDefinitionParser(_modelAwareItemTypeEP, _disableConditionsManager, _enumeratorsManager, _validatorsManager);
178        for (String id :_trashElementTypeEP.getExtensionsIds())
179        {
180            TrashElementType trashElementType = _trashElementTypeEP.getExtension(id);
181            List<ConfigurationAndPluginName> confs = trashElementType.getAdditionalModelItemsConfigurationAndPluginName();
182            for (ConfigurationAndPluginName conf:confs)
183            {
184                ElementDefinition item = definitionParser.parse(null, conf.pluginName(), conf.configuration(), this, null);
185                // There is no check done on the definition path so watch out for conflict
186                typeItems.add(item);
187            }
188        }
189        
190        definitionParser.lookupComponents();
191        
192        return typeItems;
193    }
194    
195    private <T> ElementDefinition<T> _getDefinition(String name, String type, boolean isMultiple) throws ServiceException
196    {
197        DefaultElementDefinition<T> definition = DefaultElementDefinition.of(name, isMultiple, type, ModelItemTypeExtensionPoint.ROLE_MODEL_AWARE_BASIC);
198        definition.setLabel(new I18nizableText("plugin.cms", "PLUGINS_CMS_TRASH_MODEL_" + name.toUpperCase()));
199        definition.setDescription(new I18nizableText("plugin.cms", "PLUGINS_CMS_TRASH_MODEL_" + name.toUpperCase() + "_DESC"));
200        definition.setModel(this);
201        return definition;
202    }
203}