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}