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