001/* 002 * Copyright 2010 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.skineditor.resources; 017 018import java.io.IOException; 019import java.nio.file.Path; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.component.Component; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.cocoon.servlet.multipart.Part; 028import org.apache.excalibur.source.SourceResolver; 029import org.apache.excalibur.source.impl.FileSource; 030 031import org.ametys.core.file.FileHelper; 032import org.ametys.core.right.RightManager; 033import org.ametys.core.right.RightManager.RightResult; 034import org.ametys.core.ui.Callable; 035import org.ametys.core.user.CurrentUserProvider; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.plugins.skincommons.SkinEditionHelper; 038import org.ametys.plugins.skincommons.SkinLockManager; 039import org.ametys.plugins.skineditor.skin.SkinDAO; 040import org.ametys.web.skin.Skin; 041import org.ametys.web.skin.SkinsManager; 042 043/** 044 * DAO for files and folders inside a skin directory 045 */ 046public class SkinResourceDAO implements Serviceable, Component 047{ 048 /** Constant for skin editor tool id */ 049 public static final String ROLE = SkinResourceDAO.class.getName(); 050 /** Constant for skin editor tool id */ 051 public static final String SKIN_EDITOR_TOOL_ID = "uitool-skineditor"; 052 053 private static final List<String> __IGNORED_SOURCE = List.of(".svn", ".cvs", ".lock", "CVS", ".gitignore"); 054 055 /** The lock manager */ 056 protected SkinLockManager _lockManager; 057 /** The skin edition helper */ 058 protected SkinEditionHelper _skinHelper; 059 /** The file helper */ 060 protected FileHelper _fileHelper; 061 /** The source resolver */ 062 protected SourceResolver _srcResolver; 063 /** The current user provider */ 064 protected CurrentUserProvider _currentUserProvider; 065 /** The rights manager */ 066 protected RightManager _rightManager; 067 /** The skins manager */ 068 protected SkinsManager _skinManager; 069 070 @Override 071 public void service(ServiceManager manager) throws ServiceException 072 { 073 _skinHelper = (SkinEditionHelper) manager.lookup(SkinEditionHelper.ROLE); 074 _skinManager = (SkinsManager) manager.lookup(SkinsManager.ROLE); 075 _fileHelper = (FileHelper) manager.lookup(FileHelper.ROLE); 076 _lockManager = (SkinLockManager) manager.lookup(SkinLockManager.ROLE); 077 _srcResolver = (org.apache.excalibur.source.SourceResolver) manager.lookup(org.apache.excalibur.source.SourceResolver.ROLE); 078 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 079 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 080 } 081 082 /** 083 * Gets skin's files and folders contained in the given path. 084 * @param skinName The skin name 085 * @param path the relative file's path from temporary skin root directory 086 * @return the list of skin's files and folders 087 * @throws IOException If an error occurred while listing files 088 */ 089 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 090 public List<Map<String, Object>> getFiles(String skinName, String path) throws IOException 091 { 092 _checkUserRight(skinName); 093 094 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 095 096 List<Map<String, Object>> files = _fileHelper.getFiles(tempDirURI, path, __IGNORED_SOURCE); 097 098 files.stream().forEach(file -> file.put("skinName", skinName)); 099 100 return files; 101 } 102 103 /** 104 * Saves the text in file 105 * @param skinName The name of the skin containing the resource 106 * @param relPath the relative path of file under the skin directory 107 * @param text the file content to save 108 * @return The result map 109 * @throws IOException If an error occurred while saving 110 */ 111 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 112 public Map<String, Object> save(String skinName, String relPath, String text) throws IOException 113 { 114 _checkUserRight(skinName); 115 116 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 117 118 String fileURI = tempDirURI + (relPath.length() > 0 ? "/" + relPath : ""); 119 120 Map<String, Object> result = _fileHelper.saveFile(fileURI, text); 121 122 if (result.containsKey("isI18n")) 123 { 124 _skinHelper.invalidateTempSkinCatalogues(skinName); 125 } 126 127 Path tempDir = _skinHelper.getTempDirectory(skinName); 128 // Update lock 129 _lockManager.updateLockFile(tempDir, SKIN_EDITOR_TOOL_ID); 130 131 return result; 132 } 133 134 /** 135 * checks if the resource already exists in the parent 136 * @param skinName The name of the skin containing the resource 137 * @param parentRelPath The parent path 138 * @param fileName the file name to check 139 * @return true if the file exists 140 * @throws IOException if something goes wrong while trying to retrieve a file 141 */ 142 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 143 public boolean checkSourceExists(String skinName, String parentRelPath, String fileName) throws IOException 144 { 145 _checkUserRight(skinName); 146 147 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 148 String parentFileURI = tempDirURI + (parentRelPath.length() > 0 ? "/" + parentRelPath : ""); 149 150 return _fileHelper.hasChild(parentFileURI, fileName); 151 } 152 153 /** 154 * Copy a file or directory 155 * @param skinName the name of the current skin 156 * @param srcPath the path of the source file or directory 157 * @param parentTargetPath the new path for the source file or directory 158 * @return a map of data 159 * @throws IOException if something went wrong during the source copy processing 160 */ 161 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 162 public Map<String, Object> copySource(String skinName, String srcPath, String parentTargetPath) throws IOException 163 { 164 _checkUserRight(skinName); 165 166 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 167 168 String srcFileURI = tempDirURI + (srcPath.length() > 0 ? "/" + srcPath : ""); 169 String parentTargetURI = tempDirURI + (parentTargetPath.length() > 0 ? "/" + parentTargetPath : ""); 170 171 Map<String, Object> result = _fileHelper.copySource(srcFileURI, parentTargetURI); 172 173 // Update lock 174 FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI); 175 _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID); 176 177 if (result.containsKey("uri")) 178 { 179 String folderUri = (String) result.get("uri"); 180 String path = folderUri.substring(rootDir.getURI().length()); 181 result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path); 182 result.put("skinName", skinName); 183 } 184 185 return result; 186 } 187 188 /** 189 * Create a folder 190 * @param skinName the name of the current skin 191 * @param parentRelPath the path of the parent containing the folder 192 * @param originalName the name of the new folder 193 * @param renameIfExists if true, will generate a valid name if "originalName" already exists. 194 * @return a map of data 195 * @throws IOException if an error occurs while manipulating files 196 */ 197 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 198 public Map addFolder(String skinName, String parentRelPath, String originalName, boolean renameIfExists) throws IOException 199 { 200 _checkUserRight(skinName); 201 202 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 203 String parentURI = tempDirURI + (parentRelPath.length() > 0 ? "/" + parentRelPath : ""); 204 205 Map<String, Object> result = _fileHelper.addFolder(parentURI, originalName, renameIfExists); 206 207 // Update lock 208 FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI); 209 _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID); 210 211 if (result.containsKey("uri")) 212 { 213 String folderUri = (String) result.get("uri"); 214 String path = folderUri.substring(rootDir.getURI().length()); 215 result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path); 216 result.put("parentPath", parentRelPath.endsWith("/") ? parentRelPath.substring(0, parentRelPath.length() - 1) : parentRelPath); 217 result.put("skinName", skinName); 218 } 219 220 return result; 221 } 222 223 /** 224 * Add or update a file into a skin 225 * @param skinName the name of the skin 226 * @param part The file multipart to upload 227 * @param toPath The path of parent directory 228 * @param mode The insertion mode: 'add-rename' or 'update' or null. 229 * @param unzip true to unzip .zip file 230 * @return the result map 231 * @throws IOException If an error occurred manipulating the file 232 */ 233 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 234 public Map addOrUpdateFile(String skinName, Part part, String toPath, String mode, boolean unzip) throws IOException 235 { 236 _checkUserRight(skinName); 237 238 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 239 FileSource tempDir = (FileSource) _srcResolver.resolveURI(tempDirURI); 240 241 String parentURI = tempDirURI + (toPath.length() > 0 ? "/" + toPath : ""); 242 FileSource parentDir = (FileSource) _srcResolver.resolveURI(parentURI); 243 244 Map<String, Object> result = _fileHelper.addOrUpdateFile(part, parentDir, mode, unzip); 245 246 // Update lock 247 _lockManager.updateLockFile(tempDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID); 248 249 if (result.containsKey("uri")) 250 { 251 String fileUri = (String) result.get("uri"); 252 String filePath = fileUri.substring(tempDir.getURI().length()); 253 result.put("path", filePath.endsWith("/") ? filePath.substring(0, filePath.length() - 1) : filePath); 254 String parentPath = parentDir.getURI().substring(tempDir.getURI().length()); 255 result.put("parentPath", parentPath.endsWith("/") ? parentPath.substring(0, parentPath.length() - 1) : parentPath); 256 } 257 else if (result.containsKey("unzip")) 258 { 259 String parentPath = parentDir.getURI().substring(tempDir.getURI().length()); 260 result.put("parentPath", parentPath.endsWith("/") ? parentPath.substring(0, parentPath.length() - 1) : parentPath); 261 } 262 263 return result; 264 } 265 266 267 /** 268 * Delete a file or a directory 269 * @param skinName the name of the current skin 270 * @param relPath the path of the file or directory 271 * @return a map of data 272 * @throws IOException if an error occurs while manipulating files 273 */ 274 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 275 public Map deleteFile(String skinName, String relPath) throws IOException 276 { 277 _checkUserRight(skinName); 278 279 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 280 String fileURI = tempDirURI + (relPath.length() > 0 ? "/" + relPath : ""); 281 282 Map result = _fileHelper.deleteFile(fileURI); 283 284 // Update lock 285 Path tempDir = _skinHelper.getTempDirectory(skinName); 286 _lockManager.updateLockFile(tempDir, SKIN_EDITOR_TOOL_ID); 287 288 return result; 289 } 290 291 /** 292 * Move a file or a directory 293 * @param skinName the name of current skin 294 * @param srcPath the path of the file or directory 295 * @param targetPath the targeted path 296 * @return a map of data 297 * @throws IOException if something goes wrong during the source moving process 298 */ 299 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 300 public Map<String, Object> moveSource(String skinName, String srcPath, String targetPath) throws IOException 301 { 302 _checkUserRight(skinName); 303 304 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 305 306 String srcFileURI = tempDirURI + (srcPath.length() > 0 ? "/" + srcPath : ""); 307 String parentTargetURI = tempDirURI + (targetPath.length() > 0 ? "/" + targetPath : ""); 308 309 Map<String, Object> result = _fileHelper.moveSource(srcFileURI, parentTargetURI); 310 311 // Update lock 312 FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI); 313 _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID); 314 315 if (result.containsKey("uri")) 316 { 317 String folderUri = (String) result.get("uri"); 318 String path = folderUri.substring(rootDir.getURI().length()); 319 result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path); 320 result.put("skinName", skinName); 321 } 322 323 return result; 324 } 325 326 /** 327 * Rename a file or a directory 328 * @param skinName the current skin name 329 * @param relPath the path of the file 330 * @param name the new name 331 * @return a map of data 332 * @throws IOException if something goes wrong when renaming the source 333 */ 334 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 335 public Map renameSource(String skinName, String relPath, String name) throws IOException 336 { 337 _checkUserRight(skinName); 338 339 String tempDirURI = _skinHelper.getTempDirectoryURI(skinName); 340 String fileURI = tempDirURI + (relPath.length() > 0 ? "/" + relPath : ""); 341 342 Map<String, Object> result = _fileHelper.renameFile(fileURI, name); 343 344 // Update lock 345 FileSource rootDir = null; 346 try 347 { 348 rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI); 349 _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID); 350 } 351 finally 352 { 353 _srcResolver.release(rootDir); 354 } 355 356 if (result.containsKey("uri")) 357 { 358 String newURI = (String) result.get("uri"); 359 String path = newURI.substring(rootDir.getURI().length()); 360 result.put("path", path); 361 result.put("name", name); 362 result.put("skinName", skinName); 363 } 364 365 return result; 366 } 367 368 private void _checkUserRight(String skinName) throws IllegalStateException 369 { 370 UserIdentity user = _currentUserProvider.getUser(); 371 372 Skin skin = _skinManager.getSkin(skinName); 373 if (skin == null) 374 { 375 throw new IllegalStateException("User '" + user + "' tried to modify an unknown skin '" + skinName + "'"); 376 } 377 378 if (!skin.isModifiable()) 379 { 380 throw new IllegalStateException("User '" + user + "' tried to modify an unmodifiable skin '" + skinName + "'"); 381 } 382 383 if (!(_rightManager.hasRight(user, SkinDAO.EDIT_SKINS_RIGHT_ID, "/${WorkspaceName}") == RightResult.RIGHT_ALLOW || _rightManager.hasRight(user, SkinDAO.EDIT_CURRENT_SKIN_RIGHT_ID, "/${WorkspaceName}") == RightResult.RIGHT_ALLOW && skinName.equals(_skinManager.getSkinNameFromRequest()))) 384 { 385 throw new IllegalStateException("User '" + user + "' tried to modify skin '" + skinName + "' without convenient right."); 386 } 387 } 388}