001/*
002 *  Copyright 2016 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.core.ui;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.context.Context;
026import org.apache.avalon.framework.context.ContextException;
027import org.apache.avalon.framework.context.Contextualizable;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.cocoon.components.ContextHelper;
031import org.apache.cocoon.environment.Request;
032import org.quartz.SchedulerException;
033
034import org.ametys.core.schedule.AmetysJob;
035import org.ametys.plugins.core.schedule.Scheduler;
036import org.ametys.plugins.core.ui.log.LogManager;
037import org.ametys.runtime.authentication.AccessDeniedException;
038import org.ametys.runtime.workspace.WorkspaceMatcher;
039
040/**
041 * This implementation creates an element for adding a new task
042 */
043public class AddTaskClientSideElement extends StaticClientSideElement implements Contextualizable
044{
045    /** The Avalon context */
046    protected Context _context;
047    /** The Ametys scheduler */
048    protected Scheduler _scheduler;
049    /** The log manager */
050    protected LogManager _logManager;
051
052    private String _schedulableId;
053    private List<String> _logCategories;
054    
055    @Override
056    public void contextualize(Context context) throws ContextException
057    {
058        _context = context;
059    }
060    
061    @Override
062    public void service(ServiceManager smanager) throws ServiceException
063    {
064        super.service(smanager);
065        _scheduler = (Scheduler) smanager.lookup(Scheduler.ROLE);
066        _logManager = (LogManager) smanager.lookup(LogManager.ROLE);
067    }
068    
069    /**
070     * Get event logs
071     * @param timestamp Events after this timestamp will be retrieved
072     * @param categories Events will be filtered by these categories. If empty, all categories configured by the client side are accepted.
073     * @return the target types
074     */
075    @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION)
076    public List<Map<String, Object>> getEvents (Long timestamp, List<String> categories)
077    {
078        if (!hasRight(getRights(Map.of())))
079        {
080            throw new AccessDeniedException("The user '" + _currentUserProvider.getUser() + "' try to get logs without sufficient rights");
081        }
082        
083        // Filter allowed categories
084        List<String> allowedCategories = categories
085                .stream()
086                .filter(c -> _logCategories.isEmpty() || _logCategories.contains(c))
087                .toList();
088        
089        return _logManager.getEvents(timestamp, allowedCategories.isEmpty() ? _logCategories : allowedCategories);
090    }
091
092    @Override
093    protected String _configureClass(Configuration configuration) throws ConfigurationException
094    {
095        return "Ametys.plugins.coreui.schedule.AddTaskButtonController";
096    }
097    
098    @SuppressWarnings("unchecked")
099    @Override
100    protected Map<String, Object> configureInitialParameters(Configuration configuration) throws ConfigurationException
101    {
102        Map<String, Object> initialParameters = super.configureInitialParameters(configuration);
103        _schedulableId = (String) initialParameters.get("schedulable");
104        
105        initialParameters.put("action", _configureClass(configuration) + ".act");
106        
107        // Always have at least the log category 'org.ametys.plugins.core.schedule.Scheduler',
108        // because in case of error in AmetysJob during the execution of the Schedulable, it will log in that category
109        _logCategories = new ArrayList<>();
110        String jobLogCategory = AmetysJob.class.getName() + "$" + _schedulableId;
111        _logCategories.add(jobLogCategory);
112        if (initialParameters.containsKey("log-category"))
113        {
114            Object initialLogCategory = initialParameters.get("log-category");
115            
116            if (initialLogCategory instanceof String)
117            {
118                _logCategories.add((String) initialLogCategory);
119            }
120            else if (initialLogCategory instanceof List)
121            {
122                _logCategories.addAll((List<String>) initialLogCategory);
123            }
124        }
125        initialParameters.put("log-category", _logCategories);
126        
127        return initialParameters;
128    }
129    
130    @Override
131    protected Script _configureScript(Configuration configuration) throws ConfigurationException
132    {
133        List<ScriptFile> scriptsImports = _configureImports(configuration.getChild("scripts"));
134        scriptsImports.add(new ScriptFile("/plugins/core-ui/resources/js/Ametys/plugins/coreui/schedule/AddTaskButtonController.js"));
135        List<ScriptFile> cssImports = _configureImports(configuration.getChild("css"));
136        String jsClassName = _configureClass(configuration.getChild("class"));
137        Map<String, Object> initialParameters = configureInitialParameters(configuration);
138        
139        return new Script(this.getId(), jsClassName, scriptsImports, cssImports, initialParameters);
140    }
141    
142    @Override
143    protected Map<String, List<String>> _configureDependencies(Configuration configuration) throws ConfigurationException
144    {
145        Map<String, List<String>> dependencies = super._configureDependencies(configuration);
146        
147        List<String> importDependencies = dependencies.computeIfAbsent("org.ametys.core.ui.StaticFileImportsManager", __ -> new ArrayList<>());
148        importDependencies.add("org.ametys.core.schedule.Scheduler");
149
150        List<String> uitoolsDependencies = dependencies.computeIfAbsent("org.ametys.core.ui.UIToolsFactoriesManager", __ -> new ArrayList<>());
151        uitoolsDependencies.add("uitool-server-logs");
152        
153        return dependencies;
154    }
155    
156    @Override
157    public Map<String, List<String>> getDependencies()
158    {
159        // RUNTIME-2913 The task scheduler is opened in the CMS while using the AddTaskButtonController
160        // Load the task scheduler only on admin context
161        Request request = ContextHelper.getRequest(_context);
162        String workspaceName = (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_NAME);
163        if ("admin".equals(workspaceName))
164        {
165            Map<String, List<String>> clonedDependencies = new HashMap<>(_dependencies);
166            List<String> uitoolsDependencies = new ArrayList<>();
167            if (clonedDependencies.containsKey("org.ametys.core.ui.UIToolsFactoriesManager"))
168            {
169                uitoolsDependencies.addAll(clonedDependencies.get("org.ametys.core.ui.UIToolsFactoriesManager"));
170            }
171            uitoolsDependencies.add("uitool-scheduled-tasks");
172            clonedDependencies.put("org.ametys.core.ui.UIToolsFactoriesManager", uitoolsDependencies);
173            return clonedDependencies;
174        }
175        return _dependencies;
176    }
177    
178    /**
179     * Schedule a task
180     * @param label the task label
181     * @param description the task description
182     * @param fireProcess the fire process
183     * @param cron the cron
184     * @param schedulableId the id of the schedulable
185     * @param params the schedulable parameters
186     * @return a json
187     * @throws SchedulerException if an error occurs when scheduling the task
188     */
189    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
190    public Map<String, Object> add(String label, String description, String fireProcess, String cron, String schedulableId, Map<String, Object> params) throws SchedulerException
191    {
192        if (hasRight(getRights(null)) && schedulableId.equals(_schedulableId))
193        {
194            return _scheduler.add(label, description, fireProcess, cron, schedulableId, params);
195        }
196        throw new AccessDeniedException(_currentUserProvider.getUser() + "' tried to execute schedulable '" + schedulableId + "' without any of the following rights: " + getRights(null));
197    }
198    
199    /**
200     * Get the schedulable parameters
201     * @param schedulableId the id of the schedulable
202     * @return the parameters as json
203     * @throws Exception if an error occurs
204     */
205    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
206    public Map<String, Object> getParameters(String schedulableId) throws Exception
207    {
208        if (hasRight(getRights(null)) && schedulableId.equals(_schedulableId))
209        {
210            return _scheduler.getParameters(_schedulableId);
211        }
212        throw new AccessDeniedException(_currentUserProvider.getUser() + "' tried to fetch the schedulable " + _schedulableId + " parameters without any of the following rights: " + getRights(null));
213    }
214}