001/*
002 *  Copyright 2015 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 */
016
017package org.ametys.odf.catalog;
018
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.cocoon.ProcessingException;
031import org.apache.commons.lang.StringUtils;
032import org.quartz.JobKey;
033import org.quartz.SchedulerException;
034
035import org.ametys.core.schedule.Runnable;
036import org.ametys.core.schedule.Runnable.FireProcess;
037import org.ametys.core.schedule.Runnable.MisfirePolicy;
038import org.ametys.core.ui.Callable;
039import org.ametys.core.user.CurrentUserProvider;
040import org.ametys.core.util.I18nUtils;
041import org.ametys.odf.ProgramItem;
042import org.ametys.odf.program.Program;
043import org.ametys.odf.schedulable.CopyCatalogSchedulable;
044import org.ametys.plugins.core.impl.schedule.DefaultRunnable;
045import org.ametys.plugins.core.schedule.Scheduler;
046import org.ametys.plugins.repository.AmetysObjectIterable;
047import org.ametys.plugins.repository.AmetysObjectIterator;
048import org.ametys.plugins.repository.AmetysObjectResolver;
049import org.ametys.plugins.repository.UnknownAmetysObjectException;
050import org.ametys.runtime.i18n.I18nizableText;
051import org.ametys.runtime.i18n.I18nizableTextParameter;
052import org.ametys.runtime.plugin.component.AbstractLogEnabled;
053
054/**
055 * DAO for manipulating catalogs.
056 *
057 */
058public class CatalogDAO extends AbstractLogEnabled implements Serviceable, Component
059{
060    /** The Avalon role */
061    public static final String ROLE = CatalogDAO.class.getName();
062    
063    /** The catalog manager */
064    protected CatalogsManager _catalogsManager;
065    
066    /** The ametys object resolver */
067    protected AmetysObjectResolver _resolver;
068    
069    /** The current user provider */
070    protected CurrentUserProvider _currentUserProvider;
071
072    /** The scheduler */
073    protected Scheduler _scheduler;
074    
075    /** The I18N utils */
076    protected I18nUtils _i18nUtils;
077    
078    @Override
079    public void service(ServiceManager manager) throws ServiceException
080    {
081        _catalogsManager = (CatalogsManager) manager.lookup(CatalogsManager.ROLE);
082        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
083        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
084        _scheduler = (Scheduler) manager.lookup(Scheduler.ROLE);
085        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
086    }
087    
088    /**
089     * Creates a new ODF catalog.
090     * @param title The title of the catalog
091     * @param name The code of the catalog
092     * @param catalogNameToCopy The catalog name to copy or null
093     * @return The id and the title of the created catalog, or an error
094     * @throws ProcessingException if creation failed
095     */
096    @Callable
097    public Map<String, String> createCatalog (String title, String name, String catalogNameToCopy) throws ProcessingException
098    {
099        Map<String, String> result = new HashMap<>();
100        
101        // FIXME CMS-5758 FilterNameHelper.filterName do not authorized name with number (so name is computed from JS)
102        
103        Catalog catalog = _catalogsManager.getCatalog(name);
104        if (catalog != null)
105        {
106            result.put("message", "already-exist");
107            return result;
108        }
109        
110        Catalog newCatalog = _catalogsManager.createCatalog(name, title);
111        newCatalog.saveChanges();
112        
113        if (StringUtils.isNotEmpty(catalogNameToCopy))
114        {
115            Catalog catalogToCopy = _catalogsManager.getCatalog(catalogNameToCopy);
116            
117            if (catalogToCopy == null)
118            {
119                result.put("message", "not-found");
120                return result;
121            }
122            
123            Map<String, I18nizableTextParameter> i18nParams = new HashMap<>();
124            i18nParams.put("srcCatalog", new I18nizableText(catalogToCopy.getTitle()));
125            i18nParams.put("destCatalog", new I18nizableText(newCatalog.getTitle()));
126            
127            Map<String, Object> params = new HashMap<>();
128            params.put(CopyCatalogSchedulable.JOBDATAMAP_SRC_CATALOG_KEY, catalogToCopy.getName());
129            params.put(CopyCatalogSchedulable.JOBDATAMAP_DEST_CATALOG_KEY, newCatalog.getName());
130            
131            Runnable runnable = new DefaultRunnable(CopyCatalogSchedulable.SCHEDULABLE_ID + "$" + newCatalog.getName(), 
132                new I18nizableText(_i18nUtils.translate(new I18nizableText("plugin.odf", "PLUGINS_ODF_SCHEDULABLE_COPY_CATALOG_LABEL_WITH_DETAILS", i18nParams))),
133                new I18nizableText(_i18nUtils.translate(new I18nizableText("plugin.odf", "PLUGINS_ODF_SCHEDULABLE_COPY_CATALOG_DESCRIPTION_WITH_DETAILS", i18nParams))),
134                FireProcess.NOW, 
135                null /* cron*/, 
136                CopyCatalogSchedulable.SCHEDULABLE_ID, 
137                false /* removable */, 
138                false /* modifiable */, 
139                false /* deactivatable */, 
140                MisfirePolicy.FIRE_ONCE, 
141                false /* isVolatile */, 
142                _currentUserProvider.getUser(), 
143                params
144            );
145            
146            try
147            {
148                JobKey jobKey = new JobKey(runnable.getId(), Scheduler.JOB_GROUP);
149                if (_scheduler.getScheduler().checkExists(jobKey))
150                {
151                    _scheduler.getScheduler().deleteJob(jobKey);
152                }
153                _scheduler.scheduleJob(runnable);
154            }
155            catch (SchedulerException e)
156            {
157                getLogger().error("An error occured when trying to schedule the copy of the catalog '{}' to '{}'", catalogToCopy.getTitle(), newCatalog.getTitle(), e);
158            }
159        }
160        
161        result.put("id", newCatalog.getId());
162        result.put("title", newCatalog.getTitle());
163        
164        return result;
165    }
166    
167    /**
168     * Edits an ODF catalog.
169     * @param id The id of the catalog to edit
170     * @param title The title of the catalog
171     * @return The id and the title of the edited catalog, or an error
172     */
173    @Callable
174    public Map<String, String> editCatalog (String id, String title)
175    {
176        Map<String, String> result = new HashMap<>();
177        
178        try
179        {
180            Catalog catalog = _resolver.resolveById(id);
181            
182            catalog.setTitle(title);
183            catalog.saveChanges();
184            
185            result.put("id", catalog.getId());
186        }
187        catch (UnknownAmetysObjectException e)
188        {
189            result.put("message", "not-found");
190        }
191        
192        return result;
193    }
194    
195    /**
196     * Set a catalog as default catalog
197     * @param id The id of catalog
198     * @return The id and the title of the edited catalog, or an error
199     */
200    @Callable
201    public synchronized Map<String, String> setDefaultCatalog(String id)
202    {
203        Map<String, String> result = new HashMap<>();
204        
205        try
206        {
207            Catalog catalog = _resolver.resolveById(id);
208            
209            Catalog defaultCatalog = _catalogsManager.getDefaultCatalog();
210            if (defaultCatalog != null)
211            {
212                defaultCatalog.setDefault(false);
213                defaultCatalog.saveChanges();
214            }
215            catalog.setDefault(true);
216            catalog.saveChanges();
217            
218            _catalogsManager.updateDefaultCatalog();
219            
220            result.put("id", catalog.getId());
221        }
222        catch (UnknownAmetysObjectException e)
223        {
224            result.put("message", "not-found");
225        }
226        
227        return result;
228    }
229    
230    /**
231     * Removes an ODF catalog.
232     * @param id The id of the catalog to remove
233     * @return The id of the deleted catalog, or an error
234     */
235    @Callable
236    public Map<String, Object> removeCatalog (String id)
237    {
238        Map<String, Object> result = new HashMap<>();
239        
240        result.put("id", id);
241        
242        Catalog catalog = _resolver.resolveById(id);
243        
244        AmetysObjectIterable<ProgramItem> programItems = _catalogsManager.getProgramItems(catalog.getName());
245        AmetysObjectIterator<ProgramItem> it = programItems.iterator();
246        
247        if (it.hasNext())
248        {
249            // Still referenced
250            result.put("error", new I18nizableText("plugin.odf", "PLUGINS_ODF_CATALOG_CATALOGACTIONS_DELETE_ERROR_STILL_REFERENCED"));
251        }
252        else
253        {
254            _catalogsManager.deleteCatalog(catalog.getId());
255        }
256        
257        return result;
258    }
259    
260    /**
261     * Gets the properties of a catalog.
262     * @param id The catalog id
263     * @return The properties of the catalog in a map
264     */
265    @Callable
266    public Map<String, Object> getCatalogProperties(String id)
267    {
268        Catalog catalog = _resolver.resolveById(id);
269        return getCatalogProperties(catalog);
270    }
271    
272    /**
273     * Gets the properties of a set of catalogs.
274     * @param ids The catalogs' id
275     * @return The properties of the catalogs
276     */
277    @Callable
278    public Map<String, Object> getCatalogsProperties(List<String> ids)
279    {
280        Map<String, Object> result = new HashMap<>();
281        
282        List<Map<String, Object>> catalogs = new LinkedList<>();
283        Set<String> unknownCatalogs = new HashSet<>();
284        
285        for (String id : ids)
286        {
287            try
288            {
289                Catalog catalog = _resolver.resolveById(id);
290                catalogs.add(getCatalogProperties(catalog));
291            }
292            catch (UnknownAmetysObjectException e)
293            {
294                unknownCatalogs.add(id);
295            }
296        }
297        
298        result.put("catalogs", catalogs);
299        result.put("unknownCatalogs", unknownCatalogs);
300        
301        return result;
302    }
303    
304    /**
305     * Get the properties of a catalog as a Map
306     * @param catalog The catalog
307     * @return The properties into a map object
308     */
309    public Map<String, Object> getCatalogProperties(Catalog catalog)
310    {
311        Map<String, Object> result = new HashMap<>();
312        
313        result.put("id", catalog.getId());
314        result.put("title", catalog.getTitle());
315        result.put("isDefault", catalog.isDefault());
316        result.put("code", catalog.getName());
317        
318        AmetysObjectIterable<Program> programs = _catalogsManager.getPrograms(catalog.getName());
319        result.put("nbPrograms", programs.getSize());
320        
321        return result;
322    }
323}