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