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.documents; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Set; 022 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 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.fo.ForceDefaultRepositoryWorkspaceCallableDecorator; 031import org.ametys.core.right.RightManager.RightResult; 032import org.ametys.core.ui.Callable; 033import org.ametys.core.user.UserIdentity; 034import org.ametys.plugins.explorer.ExplorerNode; 035import org.ametys.plugins.explorer.ObservationConstants; 036import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 037import org.ametys.plugins.explorer.resources.Resource; 038import org.ametys.plugins.explorer.resources.ResourceCollection; 039import org.ametys.plugins.repository.AmetysObject; 040import org.ametys.plugins.repository.AmetysRepositoryException; 041import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 042import org.ametys.plugins.workspaces.AbstractWorkspaceModule; 043import org.ametys.plugins.workspaces.documents.jcr.FolderFactory; 044import org.ametys.plugins.workspaces.project.objects.Project; 045import org.ametys.plugins.workspaces.util.StatisticColumn; 046import org.ametys.plugins.workspaces.util.StatisticsColumnType; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.web.repository.page.ModifiablePage; 049import org.ametys.web.repository.page.ModifiableZone; 050import org.ametys.web.repository.page.ModifiableZoneItem; 051import org.ametys.web.repository.page.ZoneItem.ZoneType; 052 053import com.google.common.collect.ImmutableSet; 054 055/** 056 * Helper component for managing documents 057 */ 058public class DocumentWorkspaceModule extends AbstractWorkspaceModule 059{ 060 /** The id of document module */ 061 public static final String DOCUMENT_MODULE_ID = DocumentWorkspaceModule.class.getName(); 062 063 /** Workspaces documents node name */ 064 public static final String WORKSPACES_DOCUMENTS_NODE_NAME = "documents"; 065 066 private static final String __DOCUMENT_NUMBER_HEADER_ID = WORKSPACES_DOCUMENTS_NODE_NAME + "$document_number"; 067 068 private WorkspaceExplorerTrashDAO _workspacesExplorerTrashDAO; 069 070 @Override 071 public void service(ServiceManager manager) throws ServiceException 072 { 073 super.service(manager); 074 _workspacesExplorerTrashDAO = (WorkspaceExplorerTrashDAO) manager.lookup(WorkspaceExplorerTrashDAO.ROLE); 075 } 076 077 @Override 078 public void contextualize(Context context) throws ContextException 079 { 080 _context = context; 081 } 082 083 @Override 084 public String getId() 085 { 086 return DOCUMENT_MODULE_ID; 087 } 088 089 @Override 090 public String getModuleName() 091 { 092 return WORKSPACES_DOCUMENTS_NODE_NAME; 093 } 094 095 public int getOrder() 096 { 097 return ORDER_DOCUMENTS; 098 } 099 100 @Override 101 protected String getModulePageName() 102 { 103 return "documents"; 104 } 105 106 public I18nizableText getModuleTitle() 107 { 108 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_DOCUMENT_LABEL"); 109 } 110 public I18nizableText getModuleDescription() 111 { 112 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_DOCUMENT_DESCRIPTION"); 113 } 114 @Override 115 protected I18nizableText getModulePageTitle() 116 { 117 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_DOCUMENTS_TITLE"); 118 } 119 120 @Override 121 protected void initializeModulePage(ModifiablePage documentPage) 122 { 123 ModifiableZone defaultZone = documentPage.createZone("default"); 124 125 String serviceId = "org.ametys.plugins.workspaces.module.Document"; 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 * Get the URI of a folder in project'site 136 * @param project The project 137 * @param collectionId The id of collection 138 * @return The thread uri 139 */ 140 public String getFolderUri(Project project, String collectionId) 141 { 142 String moduleUrl = getModuleUrl(project); 143 if (moduleUrl != null) 144 { 145 StringBuilder sb = new StringBuilder(); 146 sb.append(moduleUrl); 147 sb.append("#").append(collectionId); 148 149 return sb.toString(); 150 } 151 152 return null; 153 } 154 155 /** 156 * Retrieves the set of general rights used in the document module for the current user 157 * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not. 158 */ 159 @Callable (rights = Callable.NO_CHECK_REQUIRED, decorators = ForceDefaultRepositoryWorkspaceCallableDecorator.DECORATOR_ID) 160 public Map<String, Object> getModuleBaseRights() 161 { 162 Request request = ContextHelper.getRequest(_context); 163 164 String projectName = (String) request.getAttribute("projectName"); 165 Project project = _projectManager.getProject(projectName); 166 167 ModifiableResourceCollection documentRoot = getModuleRoot(project, false); 168 169 Map<String, Object> rightsData = new HashMap<>(); 170 UserIdentity user = _currentUserProvider.getUser(); 171 172 // Add file / folder 173 rightsData.put("add-file", _rightManager.hasRight(user, "Plugin_Explorer_File_Add", documentRoot) == RightResult.RIGHT_ALLOW); 174 rightsData.put("add-folder", _rightManager.hasRight(user, "Plugin_Explorer_Folder_Add", documentRoot) == RightResult.RIGHT_ALLOW); 175 rightsData.put("add-cmis-folder", _rightManager.hasRight(user, "Plugin_Explorer_CMIS_Add", documentRoot) == RightResult.RIGHT_ALLOW); 176 177 // Tags 178 rightsData.put("add-tag", _projectRightHelper.canAddTag(project)); 179 rightsData.put("remove-tag", _projectRightHelper.canRemoveTag(project)); 180 181 return rightsData; 182 } 183 184 @Override 185 public Set<String> getAllowedEventTypes() 186 { 187 return ImmutableSet.of(ObservationConstants.EVENT_RESOURCE_CREATED, 188 org.ametys.plugins.workspaces.ObservationConstants.EVENT_RESOURCE_COMMENTED, 189 ObservationConstants.EVENT_RESOURCE_RENAMED, 190 ObservationConstants.EVENT_RESOURCE_UPDATED, 191 ObservationConstants.EVENT_RESOURCE_DELETED); 192 } 193 194 @Override 195 public Map<String, Object> _getInternalStatistics(Project project, boolean isActive) 196 { 197 if (isActive) 198 { 199 ModifiableResourceCollection documentRoot = getModuleRoot(project, false); 200 long fileNumber = documentRoot == null ? 0 : documentRoot.getChildren() 201 .stream() 202 .mapToLong(child -> _getFileNumber(child)) 203 .sum(); 204 return Map.of(__DOCUMENT_NUMBER_HEADER_ID, fileNumber); 205 } 206 else 207 { 208 return Map.of(__DOCUMENT_NUMBER_HEADER_ID, __SIZE_INACTIVE); 209 } 210 } 211 212 /** 213 * Get the size of a resource (file or folder) 214 * @param document The resource 215 * @return The size in bytes 216 */ 217 public long getRessourceSize(AmetysObject document) 218 { 219 if (document instanceof Resource) 220 { 221 Resource file = (Resource) document; 222 return file.getLength(); 223 } 224 else if (document instanceof ResourceCollection) 225 { 226 ResourceCollection folder = (ResourceCollection) document; 227 228 return folder.getChildren() 229 .stream() 230 .mapToLong(child -> getRessourceSize(child)) 231 .sum(); 232 } 233 return 0; 234 } 235 236 private long _getFileNumber(AmetysObject document) 237 { 238 if (document instanceof Resource) 239 { 240 return 1; 241 } 242 else if (document instanceof ResourceCollection) 243 { 244 ResourceCollection folder = (ResourceCollection) document; 245 return folder.getChildren() 246 .stream() 247 .mapToLong(child -> _getFileNumber(child)) 248 .sum(); 249 } 250 return 0; 251 } 252 253 @Override 254 public List<StatisticColumn> _getInternalStatisticModel() 255 { 256 257 return List.of(new StatisticColumn(__DOCUMENT_NUMBER_HEADER_ID, new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_STATISTICS_TOOL_COLUMN_FILE_NUMBER")) 258 .withRenderer("Ametys.plugins.workspaces.project.tool.ProjectsGridHelper.renderElements") 259 .withType(StatisticsColumnType.LONG) 260 .withGroup(GROUP_HEADER_ELEMENTS_ID)); 261 } 262 263 @Override 264 public long getModuleSize(Project project) 265 { 266 ModifiableResourceCollection collection = getModuleRoot(project, false); 267 if (collection == null) 268 { 269 return 0; 270 } 271 Long usedTrashStorageSpace = _workspacesExplorerTrashDAO.getUsedTrashStorageSpace(project); 272 273 Long usedStorageSpace = getModuleRoot(project, false).getChildren() 274 .stream() 275 .mapToLong(child -> getRessourceSize(child)) 276 .sum(); 277 278 return usedStorageSpace + usedTrashStorageSpace; 279 } 280 281 @Override 282 protected boolean _showModuleSize() 283 { 284 return true; 285 } 286 287 @Override 288 public Set<String> getAllEventTypes() 289 { 290 return Set.of(ObservationConstants.EVENT_RESOURCE_CREATED, 291 org.ametys.plugins.workspaces.ObservationConstants.EVENT_RESOURCE_COMMENTED, 292 ObservationConstants.EVENT_RESOURCE_DELETED, 293 ObservationConstants.EVENT_RESOURCE_MOVED, 294 ObservationConstants.EVENT_RESOURCE_RENAMED, 295 ObservationConstants.EVENT_RESOURCE_UPDATED); 296 } 297 298 @Override 299 public ModifiableResourceCollection getModuleRoot(Project project, boolean create) 300 { 301 try 302 { 303 ExplorerNode projectRootNode = project.getExplorerRootNode(); 304 305 if (projectRootNode instanceof ModifiableResourceCollection projectRootNodeRc) 306 { 307 return _getAmetysObject(projectRootNodeRc, getModuleName(), FolderFactory.FOLDER_NODETYPE, create); 308 } 309 else 310 { 311 throw new IllegalArgumentException("Error getting the " + getModuleName() + " root node : it should be a ModifiableResourceCollection and not " + projectRootNode.getClass()); 312 } 313 } 314 catch (AmetysRepositoryException e) 315 { 316 throw new AmetysRepositoryException("Error getting the " + getModuleName() + " root node.", e); 317 } 318 } 319 320 /** 321 * Get the URI of a fileId in project'site 322 * @param project The project 323 * @param fileId The id of the file 324 * @return The fileId uri 325 */ 326 public String getFileUri(Project project, String fileId) 327 { 328 String moduleUrl = getModuleUrl(project); 329 if (moduleUrl != null) 330 { 331 StringBuilder sb = new StringBuilder(); 332 sb.append(moduleUrl); 333 sb.append("?route=file-").append(fileId); 334 335 return sb.toString(); 336 } 337 338 return null; 339 } 340}