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
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
119    public boolean checkSourceExists(String skinName, String parentRelPath, String fileName) throws IOException
120    {
121        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
122        String parentFileURI = tempDirURI + (parentRelPath.length() > 0 ? "/" + parentRelPath : "");
123        
124        return _fileHelper.hasChild(parentFileURI, fileName);
125    }
126    
127    /**
128     * Copy a file or directory
129     * @param skinName the name of the current skin
130     * @param srcPath the path of the source file or directory
131     * @param parentTargetPath the new path for the source file or directory
132     * @return a map of data
133     * @throws IOException if something went wrong during the source copy processing
134     */
135    @Callable
136    public Map<String, Object> copySource(String skinName, String srcPath, String parentTargetPath) throws IOException
137    {
138        _checkUserRight(skinName);
139        
140        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
141        
142        String srcFileURI = tempDirURI + (srcPath.length() > 0 ? "/" + srcPath : "");
143        String parentTargetURI = tempDirURI + (parentTargetPath.length() > 0 ? "/" + parentTargetPath : "");
144        
145        Map<String, Object> result = _fileHelper.copySource(srcFileURI, parentTargetURI);
146        
147        // Update lock
148        FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI);
149        _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID);
150        
151        if (result.containsKey("uri"))
152        {
153            String folderUri = (String) result.get("uri");
154            String path = folderUri.substring(rootDir.getURI().length());
155            result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path);
156            result.put("skinName", skinName);
157        }
158        
159        return result;
160    }
161    
162    /**
163     * Create a folder
164     * @param skinName the name of the current skin
165     * @param parentRelPath the path of the parent containing the folder
166     * @param originalName the name of the new folder
167     * @param renameIfExists if true, will generate a valid name if "originalName" already exists.
168     * @return a map of data
169     * @throws IOException if an error occurs while manipulating files
170     */
171    @Callable
172    public Map addFolder(String skinName, String parentRelPath, String originalName, boolean renameIfExists) throws IOException
173    {
174        _checkUserRight(skinName);
175        
176        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
177        String parentURI = tempDirURI + (parentRelPath.length() > 0 ? "/" + parentRelPath : "");
178        
179        Map<String, Object> result = _fileHelper.addFolder(parentURI, originalName, renameIfExists);
180        
181        // Update lock
182        FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI);
183        _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID);
184       
185        if (result.containsKey("uri"))
186        {
187            String folderUri = (String) result.get("uri");
188            String path = folderUri.substring(rootDir.getURI().length());
189            result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path);
190            result.put("parentPath", parentRelPath.endsWith("/") ? parentRelPath.substring(0, parentRelPath.length() - 1) : parentRelPath);
191            result.put("skinName", skinName);
192        }
193        
194        return result;
195    }
196    
197    /**
198     * Add or update a file into a skin
199     * @param skinName the name of the skin
200     * @param part The file multipart to upload
201     * @param toPath The path of parent directory
202     * @param mode The insertion mode: 'add-rename' or 'update' or null.
203     * @param unzip true to unzip .zip file
204     * @return the result map
205     * @throws IOException If an error occurred manipulating the file
206     */
207    public Map addOrUpdateFile(String skinName, Part part, String toPath, String mode, boolean unzip) throws IOException
208    {
209        _checkUserRight(skinName);
210        
211        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
212        FileSource tempDir = (FileSource) _srcResolver.resolveURI(tempDirURI);
213        
214        String parentURI = tempDirURI + (toPath.length() > 0 ? "/" + toPath : "");
215        FileSource parentDir = (FileSource) _srcResolver.resolveURI(parentURI);
216        
217        Map<String, Object> result = _fileHelper.addOrUpdateFile(part, parentDir, mode, unzip);
218        
219        // Update lock
220        _lockManager.updateLockFile(tempDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID);
221        
222        if (result.containsKey("uri"))
223        {
224            String fileUri = (String) result.get("uri");
225            String filePath = fileUri.substring(tempDir.getURI().length());
226            result.put("path", filePath.endsWith("/") ? filePath.substring(0, filePath.length() - 1) : filePath);
227            String parentPath = parentDir.getURI().substring(tempDir.getURI().length());
228            result.put("parentPath", parentPath.endsWith("/") ? parentPath.substring(0, parentPath.length() - 1) : parentPath);
229        }
230        else if (result.containsKey("unzip"))
231        {
232            String parentPath = parentDir.getURI().substring(parentDir.getURI().length());
233            result.put("parentPath", parentPath.endsWith("/") ? parentPath.substring(0, parentPath.length() - 1) : parentPath);
234        }
235        
236        return result;
237    }
238
239
240    /**
241     * Delete a file or a directory
242     * @param skinName the name of the current skin
243     * @param relPath the path of the file or directory
244     * @return a map of data
245     * @throws IOException if an error occurs while manipulating files
246     */
247    @Callable
248    public Map deleteFile(String skinName, String relPath) throws IOException
249    {
250        _checkUserRight(skinName);
251        
252        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
253        String fileURI = tempDirURI + (relPath.length() > 0 ? "/" + relPath : "");
254        
255        Map result = _fileHelper.deleteFile(fileURI);
256        
257        // Update lock
258        Path tempDir = _skinHelper.getTempDirectory(skinName);
259        _lockManager.updateLockFile(tempDir, SKIN_EDITOR_TOOL_ID);
260        
261        return result;
262    }
263    
264    /**
265     * Move a file or a directory
266     * @param skinName the name of current skin
267     * @param srcPath the path of the file or directory
268     * @param targetPath the targeted path
269     * @return a map of data
270     * @throws IOException if something goes wrong during the source moving process
271     */
272    @Callable
273    public Map<String, Object> moveSource(String skinName, String srcPath, String targetPath) throws IOException
274    {
275        _checkUserRight(skinName);
276        
277        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
278        
279        String srcFileURI = tempDirURI + (srcPath.length() > 0 ? "/" + srcPath : "");
280        String parentTargetURI = tempDirURI + (targetPath.length() > 0 ? "/" + targetPath : "");
281        
282        Map<String, Object> result = _fileHelper.moveSource(srcFileURI, parentTargetURI);
283        
284        // Update lock
285        FileSource rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI);
286        _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID);
287
288        if (result.containsKey("uri"))
289        {
290            String folderUri = (String) result.get("uri");
291            String path = folderUri.substring(rootDir.getURI().length());
292            result.put("path", path.endsWith("/") ? path.substring(0, path.length() - 1) : path);
293            result.put("skinName", skinName);
294        }
295        
296        return result;
297    }
298    
299    /**
300     * Rename a file or a directory
301     * @param skinName the current skin name
302     * @param relPath the path of the file
303     * @param name the new name
304     * @return a map of data
305     * @throws IOException if something goes wrong when renaming the source
306     */
307    @Callable
308    public Map renameSource(String skinName, String relPath, String name) throws IOException
309    {
310        _checkUserRight(skinName);
311        
312        String tempDirURI = _skinHelper.getTempDirectoryURI(skinName);
313        String fileURI = tempDirURI + (relPath.length() > 0 ? "/" + relPath : "");
314        
315        Map<String, Object> result = _fileHelper.renameFile(fileURI, name);
316        
317        // Update lock
318        FileSource rootDir = null;
319        try
320        {
321            rootDir = (FileSource) _srcResolver.resolveURI(tempDirURI);
322            _lockManager.updateLockFile(rootDir.getFile().toPath(), SKIN_EDITOR_TOOL_ID);
323        }
324        finally
325        {
326            _srcResolver.release(rootDir);
327        }
328        
329        if (result.containsKey("uri"))
330        {
331            String newURI = (String) result.get("uri");
332            String path = newURI.substring(rootDir.getURI().length());
333            result.put("path", path);
334            result.put("name", name);
335            result.put("skinName", skinName);
336        }
337        
338        return result;
339    }
340    
341    private void _checkUserRight(String skinName) throws IllegalStateException
342    {
343        UserIdentity user = _currentUserProvider.getUser();
344        
345        Skin skin = _skinManager.getSkin(skinName);
346        if (skin == null)
347        {
348            throw new IllegalStateException("User '" + user + "' tried to modify an unknown skin '" + skinName + "'");
349        }
350        
351        if (!skin.isModifiable())
352        {
353            throw new IllegalStateException("User '" + user + "' tried to modify an unmodifiable skin '" + skinName + "'");
354        }
355        
356        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())))
357        {
358            throw new IllegalStateException("User '" + user + "' tried to modify skin '" + skinName + "' without convenient right.");
359        }
360    }
361}