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.skin;
017
018import java.io.IOException;
019import java.nio.file.Files;
020import java.nio.file.Path;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026import java.util.function.Predicate;
027import java.util.stream.Collectors;
028
029import org.apache.avalon.framework.component.Component;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033
034import org.ametys.core.right.RightManager;
035import org.ametys.core.right.RightManager.RightResult;
036import org.ametys.core.ui.Callable;
037import org.ametys.core.user.CurrentUserProvider;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.core.util.path.PathUtils;
040import org.ametys.plugins.skincommons.SkinEditionHelper;
041import org.ametys.plugins.skincommons.SkinLockManager;
042import org.ametys.runtime.i18n.I18nizableText;
043import org.ametys.web.repository.site.Site;
044import org.ametys.web.repository.site.SiteManager;
045import org.ametys.web.skin.Skin;
046import org.ametys.web.skin.SkinsManager;
047
048/**
049 * DAO for files and folders inside a skin directory
050 */
051public class SkinDAO implements Serviceable, Component
052{
053    /** The Avalon role */
054    public static final String ROLE = SkinDAO.class.getName();
055    
056    /** Constant for skin editor tool id */
057    public static final String SKIN_EDITOR_TOOL_ID = "uitool-skineditor";
058    
059    private static final String __WORK_MODE = "work";
060    private static final String __PROD_MODE = "prod";
061    
062    /** The lock manager */
063    protected SkinLockManager _lockManager;
064    /** The skin edition helper */
065    protected SkinEditionHelper _skinHelper;
066
067    private SkinsManager _skinManager;
068
069    private CurrentUserProvider _userProvider;
070
071    private RightManager _rightManager;
072
073    private SiteManager _siteManager;
074    
075    @Override
076    public void service(ServiceManager manager) throws ServiceException
077    {
078        _skinHelper = (SkinEditionHelper) manager.lookup(SkinEditionHelper.ROLE);
079        _lockManager = (SkinLockManager) manager.lookup(SkinLockManager.ROLE);
080        _skinManager = (SkinsManager) manager.lookup(SkinsManager.ROLE);
081        _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
082        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
083        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
084    }
085    
086    /**
087     * Get the list of available and modifiable skins for a site
088     * @param siteName The site name
089     * @return the list of skins
090     */
091    @Callable
092    public List<Object> getSkinsList(String siteName) 
093    {
094        Site site = _siteManager.getSite(siteName);
095        String currentSkinId = site.getSkinId();
096        
097        Set<String> skins = new HashSet<>();
098        
099        UserIdentity user = _userProvider.getUser();
100        if (_rightManager.hasRight(user, "Plugins_SkinEditor_EditAllSkin", "/cms") == RightResult.RIGHT_ALLOW)
101        {
102            skins.addAll(_skinManager.getSkins());
103        }
104        else if (_rightManager.hasRight(user, "Plugins_SkinEditor_EditCurrentSkin", "/cms") == RightResult.RIGHT_ALLOW)
105        {
106            skins.add(currentSkinId);
107        }
108        
109        return skins.stream()
110            .map(id -> _skinManager.getSkin(id))
111            .filter(Skin::isModifiable)
112            .filter(Predicate.not(Skin::isAbstract))
113            .map(s -> _skin2JsonObject(s, s.getId().equals(currentSkinId)))
114            .collect(Collectors.toList());
115    }
116    
117    private Map<String, Object> _skin2JsonObject (Skin skin, boolean current)
118    {
119        Map<String, Object> jsonObject = new HashMap<>();
120        
121        I18nizableText label = skin.getLabel();
122        String icon = skin.getLargeImage();
123        
124        jsonObject.put("id", skin.getId());
125        jsonObject.put("current", current);
126        jsonObject.put("label", label);
127        jsonObject.put("icon", icon);
128        
129        return jsonObject;
130    }
131    
132    /**
133     * Open a skin for editing
134     * @param skinId the skin id
135     * @param mode the edition mode
136     * @param unlinkModel True to remove any existing change
137     * @return the skin id
138     * @throws Exception if an error occurs during the skin opening process
139     */
140    @Callable
141    public String openSkin(String skinId, String mode, boolean unlinkModel) throws Exception
142    {
143        Skin skin = _skinManager.getSkin(skinId);
144        if (!skin.isModifiable())
145        {
146            throw new IllegalStateException("The skin '" + skinId + "' is not modifiable and thus cannot be opened in skin editor.");
147        }
148        
149        Path tempDir = _skinHelper.getTempDirectory(skinId);
150        Path workDir = _skinHelper.getWorkDirectory(skinId);
151        Path skinDir = _skinHelper.getSkinDirectory(skinId);
152        
153        if (unlinkModel)
154        {
155            unlinkModel (skinDir);
156            unlinkModel(workDir);
157            unlinkModel(tempDir);
158        }
159        
160        if (__PROD_MODE.equals(mode) || __WORK_MODE.equals(mode))
161        {
162            // Delete temp directory if exists
163            if (Files.exists(tempDir))
164            {
165                _skinHelper.deleteQuicklyDirectory(tempDir);
166            }
167            
168            if (__PROD_MODE.equals(mode))
169            {
170                // Delete work directory if exists
171                if (Files.exists(workDir))
172                {
173                    _skinHelper.deleteQuicklyDirectory(workDir);
174                }
175                
176                // Copy from skin
177                PathUtils.copyDirectory(skinDir, workDir);
178            }
179                    
180            // Copy work in temp
181            PathUtils.copyDirectory(workDir, tempDir);
182            
183            // Create .lock file
184            _lockManager.updateLockFile(tempDir, SKIN_EDITOR_TOOL_ID);
185        }
186        else
187        {
188            // Update .lock file
189            _lockManager.updateLockFile(tempDir, SKIN_EDITOR_TOOL_ID);
190        }
191        
192        return skinId;
193    }
194    
195    /**
196     * Unlink model
197     * @param skinDir The skin directory
198     * @throws IOException If an error occurred
199     */
200    protected void unlinkModel (Path skinDir) throws IOException
201    {
202        Path modelFile = skinDir.resolve("model.xml");
203        Path bakFile = skinDir.resolve("model.xml.bak");
204        
205        if (Files.exists(bakFile))
206        {
207            // Delete old bak file if exists
208            PathUtils.deleteQuietly(bakFile);
209        }
210        
211        if (Files.exists(modelFile))
212        {
213            Files.move(modelFile, bakFile);
214        }
215    }
216}