001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui;
029
030import com.alkacon.simapi.IdentIcon;
031import com.alkacon.simapi.Simapi;
032
033import org.opencms.cache.CmsVfsNameBasedDiskCache;
034import org.opencms.file.CmsFile;
035import org.opencms.file.CmsObject;
036import org.opencms.file.CmsProject;
037import org.opencms.file.CmsProperty;
038import org.opencms.file.CmsPropertyDefinition;
039import org.opencms.file.CmsResource;
040import org.opencms.file.CmsResourceFilter;
041import org.opencms.file.CmsUser;
042import org.opencms.file.types.CmsResourceTypeImage;
043import org.opencms.loader.CmsImageScaler;
044import org.opencms.main.CmsException;
045import org.opencms.main.CmsLog;
046import org.opencms.main.OpenCms;
047import org.opencms.security.CmsRole;
048import org.opencms.util.CmsStringUtil;
049import org.opencms.workplace.CmsWorkplace;
050
051import java.awt.Color;
052import java.awt.image.BufferedImage;
053import java.io.IOException;
054import java.util.Collections;
055
056import org.apache.commons.logging.Log;
057
058/**
059 * Generates user ident-icons.<p>
060 */
061public class CmsUserIconHelper {
062
063    /** The color reserved for admin users. */
064    public static final Color ADMIN_COLOR = new Color(0x00, 0x30, 0x82);
065
066    /** The big icon suffix. */
067    public static final String BIG_ICON_SUFFIX = "_big_icon.png";
068
069    /** The target folder name. */
070    public static final String ICON_FOLDER = "user_icons";
071
072    /** The small icon suffix. */
073    public static final String SMALL_ICON_SUFFIX = "_small_icon.png";
074
075    /** The temp folder name. */
076    public static final String TEMP_FOLDER = "temp/";
077
078    /** The user image folder. */
079    public static final String USER_IMAGE_FOLDER = "/system/userimages/";
080
081    /** The user image additional info key. */
082    public static final String USER_IMAGE_INFO = "USER_IMAGE";
083
084    /** Logger instance for this class. */
085    private static final Log LOG = CmsLog.getLog(CmsUserIconHelper.class);
086
087    /** The admin cms context. */
088    private CmsObject m_adminCms;
089
090    /** The image cache. */
091    private CmsVfsNameBasedDiskCache m_cache;
092
093    /** The icon renderer. */
094    private IdentIcon m_renderer;
095
096    /**
097     * Constructor.<p>
098     *
099     * @param adminCms the admin cms context
100     */
101    public CmsUserIconHelper(CmsObject adminCms) {
102        m_adminCms = adminCms;
103        m_renderer = new IdentIcon();
104        m_renderer.setReservedColor(ADMIN_COLOR);
105
106        m_cache = new CmsVfsNameBasedDiskCache(
107            OpenCms.getSystemInfo().getWebApplicationRfsPath() + "/" + CmsWorkplace.RFS_PATH_RESOURCES,
108            ICON_FOLDER);
109    }
110
111    /**
112     * Returns the big ident-icon path for the given user.<p>
113     *
114     * @param cms the cms context
115     * @param user the user
116     *
117     * @return the icon path
118     */
119    public String getBigIconPath(CmsObject cms, CmsUser user) {
120
121        return getIconPath(cms, user, true);
122    }
123
124    /**
125     * Returns the small ident-icon path for the given user.<p>
126     *
127     * @param cms the cms context
128     * @param user the user
129     *
130     * @return the icon path
131     */
132    public String getSmallIconPath(CmsObject cms, CmsUser user) {
133
134        return getIconPath(cms, user, false);
135    }
136
137    /**
138     * Handles a user image upload.
139     * The uploaded file will be scaled and save as a new file beneath /system/userimages/, the original file will be deleted.<p>
140     *
141     * @param cms the cms context
142     * @param user the user
143     * @param uploadedFile the uploaded file
144     */
145    public void handleImageUpload(CmsObject cms, CmsUser user, String uploadedFile) {
146
147        try {
148            setUserImage(cms, user, uploadedFile);
149
150        } catch (CmsException e) {
151            LOG.error("Error setting user image.", e);
152        }
153        try {
154            cms.lockResource(uploadedFile);
155            cms.deleteResource(uploadedFile, CmsResource.DELETE_REMOVE_SIBLINGS);
156        } catch (CmsException e) {
157            LOG.error("Error deleting user image temp file.", e);
158        }
159    }
160
161    /**
162     * Sets the user image for the given user.<p>
163     *
164     * @param cms the cms context
165     * @param user the user
166     * @param rootPath the image root path
167     *
168     * @throws CmsException in case anything goes wrong
169     */
170    public void setUserImage(CmsObject cms, CmsUser user, String rootPath) throws CmsException {
171
172        CmsFile tempFile = cms.readFile(cms.getRequestContext().removeSiteRoot(rootPath));
173        CmsImageScaler scaler = new CmsImageScaler(tempFile.getContents(), tempFile.getRootPath());
174
175        if (scaler.isValid()) {
176            scaler.setType(2);
177            scaler.setHeight(192);
178            scaler.setWidth(192);
179            byte[] content = scaler.scaleImage(tempFile);
180            String previousImage = null;
181            String newFileName = USER_IMAGE_FOLDER
182                + user.getId().toString()
183                + "_"
184                + System.currentTimeMillis()
185                + getSuffix(tempFile.getName());
186            CmsObject adminCms = OpenCms.initCmsObject(m_adminCms);
187            CmsProject tempProject = adminCms.createTempfileProject();
188            adminCms.getRequestContext().setCurrentProject(tempProject);
189            if (adminCms.existsResource(newFileName)) {
190                // a user image of the given name already exists, just write the new content
191                CmsFile imageFile = adminCms.readFile(newFileName);
192                adminCms.lockResource(imageFile);
193                imageFile.setContents(content);
194                adminCms.writeFile(imageFile);
195                adminCms.writePropertyObject(
196                    newFileName,
197                    new CmsProperty(CmsPropertyDefinition.PROPERTY_IMAGE_SIZE, null, "w:192,h:192"));
198            } else {
199                previousImage = (String)user.getAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO);
200                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(previousImage)
201                    && cms.existsResource(newFileName, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
202                    try {
203                        adminCms.lockResource(previousImage);
204                        adminCms.deleteResource(previousImage, CmsResource.DELETE_REMOVE_SIBLINGS);
205                    } catch (CmsException e) {
206                        LOG.error("Error deleting previous user image.", e);
207                    }
208                }
209                // create a new user image file
210                adminCms.createResource(
211                    newFileName,
212                    OpenCms.getResourceManager().getResourceType(CmsResourceTypeImage.getStaticTypeName()),
213                    content,
214                    Collections.singletonList(
215                        new CmsProperty(CmsPropertyDefinition.PROPERTY_IMAGE_SIZE, null, "w:192,h:192")));
216            }
217            user.setAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO, newFileName);
218            adminCms.writeUser(user);
219
220            try {
221                OpenCms.getPublishManager().publishProject(adminCms);
222            } catch (Exception e) {
223                LOG.error("Error publishing user image resources.", e);
224            }
225
226        }
227    }
228
229    /**
230     * Returns the ident-icon path for the given user.<p>
231     *
232     * @param cms the cms context
233     * @param user the user
234     * @param big <code>true</code> to retrieve the big icon
235     *
236     * @return the icon path
237     */
238    private String getIconPath(CmsObject cms, CmsUser user, boolean big) {
239
240        String userIconPath = (String)user.getAdditionalInfo(USER_IMAGE_INFO);
241        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(userIconPath)) {
242            userIconPath += big ? "" : "?__scale=h:32,w:32,t:2";
243            return OpenCms.getLinkManager().substituteLinkForRootPath(cms, userIconPath);
244        }
245
246        boolean isAdmin = OpenCms.getRoleManager().hasRole(cms, user.getName(), CmsRole.ADMINISTRATOR);
247        String name = user.getName() + Boolean.toString(isAdmin);
248        String rfsName = toRfsName(name, big);
249        String path = toPath(name, big);
250        if (!m_cache.hasCacheContent(rfsName)) {
251
252            BufferedImage icon = m_renderer.render(name, isAdmin, big ? 96 : 32);
253            try {
254                m_cache.saveCacheFile(rfsName, getImageBytes(icon));
255            } catch (Exception e) {
256                LOG.error(e.getLocalizedMessage(), e);
257            }
258        }
259        return path;
260    }
261
262    /**
263     * Returns the image data
264     * @param image the image
265     *
266     * @return the data
267     *
268     * @throws IOException in case writing to the output stream failed
269     */
270    private byte[] getImageBytes(BufferedImage image) throws IOException {
271
272        return Simapi.getImageBytes(image, Simapi.TYPE_PNG);
273    }
274
275    /**
276     * Returns the file suffix.<p>
277     *
278     * @param fileName the file name
279     *
280     * @return the suffix
281     */
282    private String getSuffix(String fileName) {
283
284        int index = fileName.lastIndexOf(".");
285        if (index > 0) {
286            return fileName.substring(index);
287        } else {
288            return fileName;
289        }
290    }
291
292    /**
293     * Transforms user name and icon size into the image path.
294     *
295     * @param name the user name
296     * @param big <code>true</code> in case of big icons
297     *
298     * @return the path
299     */
300    private String toPath(String name, boolean big) {
301
302        String result = CmsStringUtil.joinPaths(CmsWorkplace.getSkinUri(), ICON_FOLDER, "" + name.hashCode());
303        if (big) {
304            result += BIG_ICON_SUFFIX;
305        } else {
306            result += SMALL_ICON_SUFFIX;
307        }
308        return result;
309    }
310
311    /**
312     * Transforms user name and icon size into the rfs image path.
313     *
314     * @param name the user name
315     * @param big <code>true</code> in case of big icons
316     *
317     * @return the path
318     */
319    private String toRfsName(String name, boolean big) {
320
321        String result = CmsStringUtil.joinPaths(m_cache.getRepositoryPath(), "" + name.hashCode());
322        if (big) {
323            result += BIG_ICON_SUFFIX;
324        } else {
325            result += SMALL_ICON_SUFFIX;
326        }
327        return result;
328    }
329}