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