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.plugins.workspaces.tasks;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.components.ContextHelper;
028import org.apache.cocoon.environment.Request;
029import org.apache.commons.lang.IllegalClassException;
030
031import org.ametys.cms.data.Binary;
032import org.ametys.core.right.RightManager.RightResult;
033import org.ametys.core.ui.Callable;
034import org.ametys.core.user.UserIdentity;
035import org.ametys.plugins.explorer.ExplorerNode;
036import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
037import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollectionFactory;
038import org.ametys.plugins.repository.AmetysRepositoryException;
039import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
040import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
041import org.ametys.plugins.workspaces.AbstractWorkspaceModule;
042import org.ametys.plugins.workspaces.ObservationConstants;
043import org.ametys.plugins.workspaces.project.objects.Project;
044import org.ametys.plugins.workspaces.util.StatisticColumn;
045import org.ametys.plugins.workspaces.util.StatisticsColumnType;
046import org.ametys.runtime.i18n.I18nizableText;
047import org.ametys.web.repository.page.ModifiablePage;
048import org.ametys.web.repository.page.ModifiableZone;
049import org.ametys.web.repository.page.ModifiableZoneItem;
050import org.ametys.web.repository.page.ZoneItem.ZoneType;
051
052import com.google.common.collect.ImmutableSet;
053
054/**
055 * Tasks manager for workspaces
056 */
057public class TasksWorkspaceModule extends AbstractWorkspaceModule
058{
059    /** The id of task module */
060    public static final String TASK_MODULE_ID = TasksWorkspaceModule.class.getName();
061    
062    /** Workspaces tasks list node name */
063    private static final String __WORKSPACES_TASKS_NODE_NAME = "tasks";
064    
065    /** Workspaces root tasks node name */
066    private static final String __WORKSPACES_TASKS_ROOT_NODE_NAME = "tasks-root";
067
068    /** Workspaces root tasks lists node name */
069    private static final String __WORKSPACES_TASKS_LIST_ROOT_NODE_NAME = "tasks-list-root";
070
071    private static final String __TASK_NUMBER_HEADER_ID = __WORKSPACES_TASKS_NODE_NAME + "$task_number";
072    
073    /** The tasks module DAO */
074    private WorkspaceTaskDAO _workspaceTaskDAO;
075
076    @Override
077    public void service(ServiceManager serviceManager) throws ServiceException
078    {
079        super.service(serviceManager);
080        _workspaceTaskDAO = (WorkspaceTaskDAO) serviceManager.lookup(WorkspaceTaskDAO.ROLE);
081    }
082    
083    @Override
084    public String getId()
085    {
086        return TASK_MODULE_ID;
087    }
088    
089    @Override
090    public String getModuleName()
091    {
092        return __WORKSPACES_TASKS_NODE_NAME;
093    }
094    
095    public int getOrder()
096    {
097        return ORDER_TASKS;
098    }
099    
100    @Override
101    protected String getModulePageName()
102    {
103        return "tasks";
104    }
105    
106    public I18nizableText getModuleTitle()
107    {
108        return new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_TASK_LABEL");
109    }
110    public I18nizableText getModuleDescription()
111    {
112        return new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_TASK_DESCRIPTION");
113    }
114    @Override
115    protected I18nizableText getModulePageTitle()
116    {
117        return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_TASKS_TITLE");
118    }
119    
120    @Override
121    protected void initializeModulePage(ModifiablePage taskPage)
122    {
123        ModifiableZone defaultZone = taskPage.createZone("default");
124        
125        String serviceId = "org.ametys.plugins.workspaces.module.Tasks";
126        ModifiableZoneItem defaultZoneItem = defaultZone.addZoneItem();
127        defaultZoneItem.setType(ZoneType.SERVICE);
128        defaultZoneItem.setServiceId(serviceId);
129        
130        ModifiableModelAwareDataHolder serviceDataHolder = defaultZoneItem.getServiceParameters();
131        serviceDataHolder.setValue("xslt", _getDefaultXslt(serviceId));
132    }
133
134    /**
135     * Retrieves the rights for the current user in the project
136     * @return The project
137     */
138    @Callable
139    public Map<String, Object> getTasksModuleRights()
140    {
141        Map<String, Object> rights = new HashMap<>();
142        
143        
144        Request request = ContextHelper.getRequest(_context);
145        String projectName = (String) request.getAttribute("projectName");
146        Project project = _projectManager.getProject(projectName);
147        ModifiableResourceCollection tasksRoot = getModuleRoot(project, false);
148        
149        UserIdentity currentUser = _currentUserProvider.getUser();
150        rights.put("view", tasksRoot != null && _rightManager.hasRight(currentUser, AbstractWorkspaceTaskDAO.RIGHTS_HANDLE_TASK, tasksRoot) == RightResult.RIGHT_ALLOW);
151        rights.put("add", tasksRoot != null && _rightManager.hasRight(currentUser, AbstractWorkspaceTaskDAO.RIGHTS_HANDLE_TASK, tasksRoot) == RightResult.RIGHT_ALLOW);
152        
153        return rights;
154    }
155    
156    /**
157     * Retrieves the rights for the current user in the projects list
158     * @param projectNames The name of projects to checks rights from
159     * @return The rights data
160     */
161    @Callable
162    public Map<String, Object> getTasksServiceRights(List<String> projectNames)
163    {
164        Map<String, Object> rights = new HashMap<>();
165        
166        List<String> rightAdd = new ArrayList<>();
167        
168        UserIdentity currentUser = _currentUserProvider.getUser();
169        for (String projectName : projectNames)
170        {
171            Project project = _projectManager.getProject(projectName);
172            ModifiableResourceCollection tasksRoot = getModuleRoot(project, false);
173            
174            if (tasksRoot != null && _rightManager.hasRight(currentUser, AbstractWorkspaceTaskDAO.RIGHTS_HANDLE_TASK, tasksRoot) == RightResult.RIGHT_ALLOW)
175            {
176                rightAdd.add(projectName);
177            }
178        }
179        
180        rights.put("add", rightAdd);
181        
182        return rights;
183    }
184    
185    /**
186     * Get the URI of a task in project'site
187     * @param project The project
188     * @param taskId The id of the task
189     * @return The thread uri
190     */
191    public String getTaskUri(Project project, String taskId)
192    {
193        String moduleUrl = getModuleUrl(project);
194        if (moduleUrl != null)
195        {
196            StringBuilder sb = new StringBuilder();
197            sb.append(moduleUrl);
198            sb.append("#task-").append(taskId);
199            
200            return sb.toString();
201        }
202        
203        return null;
204    }
205    
206    @Override
207    public ModifiableResourceCollection getModuleRoot(Project project, boolean create)
208    {
209        try
210        {
211            ExplorerNode projectRootNode = project.getExplorerRootNode();
212            
213            if (projectRootNode instanceof ModifiableResourceCollection)
214            {
215                ModifiableResourceCollection projectRootNodeRc = (ModifiableResourceCollection) projectRootNode;
216                return _getAmetysObject(projectRootNodeRc, __WORKSPACES_TASKS_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE, create);
217            }
218            else
219            {
220                throw new IllegalClassException(ModifiableResourceCollection.class, projectRootNode.getClass());
221            }
222        }
223        catch (AmetysRepositoryException e)
224        {
225            throw new AmetysRepositoryException("Error getting the documents root node.", e);
226        }
227    }
228    
229    /**
230     * Get the root for tasks
231     * @param project The project
232     * @param create true to create root if not exists
233     * @return The root for tasks
234     */
235    public DefaultTraversableAmetysObject getTasksRoot(Project project, boolean create)
236    {
237        ModifiableResourceCollection moduleRoot = getModuleRoot(project, create);
238        return _getAmetysObject(moduleRoot, __WORKSPACES_TASKS_ROOT_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE, create);
239    }
240    
241    /**
242     * Get the root for tasks lists
243     * @param project The project
244     * @param create true to create root if not exists
245     * @return The root for tasks lists
246     */
247    public DefaultTraversableAmetysObject getTasksListsRoot(Project project, boolean create)
248    {
249        ModifiableResourceCollection moduleRoot = getModuleRoot(project, create);
250        return _getAmetysObject(moduleRoot, __WORKSPACES_TASKS_LIST_ROOT_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE, create);
251    }
252
253
254    @Override
255    public Set<String> getAllowedEventTypes()
256    {
257        return ImmutableSet.of(ObservationConstants.EVENT_TASK_CREATED, ObservationConstants.EVENT_TASK_ASSIGNED, ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED, ObservationConstants.EVENT_TASK_DELETING);
258    }
259    
260    @Override
261    public Map<String, Object> _getInternalStatistics(Project project, boolean isActive)
262    { 
263        if (isActive)
264        {
265            return Map.of(__TASK_NUMBER_HEADER_ID, _workspaceTaskDAO.getTasksCount(project));
266        }
267        else
268        {
269            return Map.of(__TASK_NUMBER_HEADER_ID, __SIZE_INACTIVE);
270        }
271    }
272    
273    @Override
274    protected long _getModuleSize(Project project)
275    {
276        return _workspaceTaskDAO.getProjectTasks(project)
277                         .stream()
278                         .map(Task::getAttachments)
279                         .flatMap(Collection::stream)
280                         .mapToLong(Binary::getLength)
281                         .sum();
282    }
283    
284    @Override
285    public List<StatisticColumn> _getInternalStatisticModel()
286    {
287        return List.of(new StatisticColumn(__TASK_NUMBER_HEADER_ID, new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_PROJECT_STATISTICS_TOOL_COLUMN_TASK_NUMBER"))
288                .withRenderer("Ametys.plugins.workspaces.project.tool.ProjectsGridHelper.renderElements")
289                .withType(StatisticsColumnType.LONG)
290                .withGroup(GROUP_HEADER_ELEMENTS_ID));
291    }
292
293    @Override
294    protected boolean _showModuleSize()
295    {
296        return true;
297    }
298}