001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (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; 055import java.util.List; 056 057import org.apache.commons.logging.Log; 058 059/** 060 * Generates user ident-icons.<p> 061 */ 062public class CmsUserIconHelper { 063 064 /** 065 * Available icon sizes.<p> 066 */ 067 private enum IconSize { 068 /**The big icon size. */ 069 Big(96, BIG_ICON_SUFFIX), 070 /**The small icon size.*/ 071 Small(32, SMALL_ICON_SUFFIX), 072 /**The tiny icon size. */ 073 Tiny(23, TINY_ICON_SUFFIX); 074 075 /**Size in pixel.*/ 076 private int m_size; 077 078 /**Suffix to append to filename.*/ 079 private String m_suffix; 080 081 /** 082 * constructor.<p> 083 * 084 * @param size in pixel 085 * @param suffix for filename 086 */ 087 private IconSize(int size, String suffix) { 088 m_size = size; 089 m_suffix = suffix; 090 } 091 092 /** 093 * Gets size in pixel.<p> 094 * 095 * @return icon size in pixel 096 */ 097 public int getSize() { 098 099 return m_size; 100 } 101 102 /** 103 * Gets the suffix.<p> 104 * 105 * @return string 106 */ 107 public String getSuffix() { 108 109 return m_suffix; 110 } 111 112 } 113 114 /** The color reserved for admin users. */ 115 public static final Color ADMIN_COLOR = new Color(0x00, 0x30, 0x82); 116 117 /** The big icon suffix. */ 118 public static final String BIG_ICON_SUFFIX = "_big_icon.png"; 119 120 /** The target folder name. */ 121 public static final String ICON_FOLDER = "user_icons"; 122 123 /** The small icon suffix. */ 124 public static final String SMALL_ICON_SUFFIX = "_small_icon.png"; 125 126 /** The tiny icon suffix. */ 127 public static final String TINY_ICON_SUFFIX = "_tiny_icon.png"; 128 129 /** The temp folder name. */ 130 public static final String TEMP_FOLDER = "temp/"; 131 132 /** The user image folder. */ 133 public static final String USER_IMAGE_FOLDER = "/system/userimages/"; 134 135 /** The user image additional info key. */ 136 public static final String USER_IMAGE_INFO = "USER_IMAGE"; 137 138 /** Logger instance for this class. */ 139 private static final Log LOG = CmsLog.getLog(CmsUserIconHelper.class); 140 141 /** The admin cms context. */ 142 private CmsObject m_adminCms; 143 144 /** The image cache. */ 145 private CmsVfsNameBasedDiskCache m_cache; 146 147 /** The icon renderer. */ 148 private IdentIcon m_renderer; 149 150 /** 151 * Constructor.<p> 152 * 153 * @param adminCms the admin cms context 154 */ 155 public CmsUserIconHelper(CmsObject adminCms) { 156 m_adminCms = adminCms; 157 m_renderer = new IdentIcon(); 158 m_renderer.setReservedColor(ADMIN_COLOR); 159 160 m_cache = new CmsVfsNameBasedDiskCache( 161 OpenCms.getSystemInfo().getWebApplicationRfsPath() + "/" + CmsWorkplace.RFS_PATH_RESOURCES, 162 ICON_FOLDER); 163 } 164 165 /** 166 * Checks whether the given user has an individual user image.<p> 167 * 168 * @param user the user 169 * 170 * @return <code>true</code> if the given user has an individual user image 171 */ 172 public static boolean hasUserImage(CmsUser user) { 173 174 return CmsStringUtil.isNotEmptyOrWhitespaceOnly((String)user.getAdditionalInfo(USER_IMAGE_INFO)); 175 } 176 177 /** 178 * Deletes the user image of the current user.<p> 179 * 180 * @param cms the cms context 181 */ 182 public void deleteUserImage(CmsObject cms) { 183 184 CmsUser user = cms.getRequestContext().getCurrentUser(); 185 String userIconPath = (String)user.getAdditionalInfo(USER_IMAGE_INFO); 186 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(userIconPath)) { 187 try { 188 CmsObject adminCms = OpenCms.initCmsObject(m_adminCms); 189 if (adminCms.existsResource(userIconPath)) { 190 191 CmsProject tempProject = adminCms.createTempfileProject(); 192 adminCms.getRequestContext().setCurrentProject(tempProject); 193 adminCms.lockResource(userIconPath); 194 adminCms.deleteResource(userIconPath, CmsResource.DELETE_REMOVE_SIBLINGS); 195 } 196 user.deleteAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO); 197 adminCms.writeUser(user); 198 199 try { 200 OpenCms.getPublishManager().publishProject(adminCms); 201 } catch (Exception e) { 202 LOG.error("Error publishing user image resources.", e); 203 } 204 } catch (CmsException e) { 205 LOG.error("Error deleting previous user image.", e); 206 } 207 } 208 } 209 210 /** 211 * Returns the big ident-icon path for the given user.<p> 212 * 213 * @param cms the cms context 214 * @param user the user 215 * 216 * @return the icon path 217 */ 218 public String getBigIconPath(CmsObject cms, CmsUser user) { 219 220 return getIconPath(cms, user, IconSize.Big); 221 } 222 223 /** 224 * Returns the small ident-icon path for the given user.<p> 225 * 226 * @param cms the cms context 227 * @param user the user 228 * 229 * @return the icon path 230 */ 231 public String getSmallIconPath(CmsObject cms, CmsUser user) { 232 233 return getIconPath(cms, user, IconSize.Small); 234 } 235 236 /** 237 * Returns the tiny ident-icon path for the given user.<p> 238 * 239 * @param cms the cms context 240 * @param user the user 241 * 242 * @return the icon path 243 */ 244 public String getTinyIconPath(CmsObject cms, CmsUser user) { 245 246 return getIconPath(cms, user, IconSize.Tiny); 247 } 248 249 /** 250 * Handles a user image upload. 251 * The uploaded file will be scaled and save as a new file beneath /system/userimages/, the original file will be deleted.<p> 252 * 253 * @param cms the cms context 254 * @param user the user 255 * @param uploadedFile the uploaded file 256 * 257 * @return <code>true</code> in case the image was set successfully 258 */ 259 public boolean handleImageUpload(CmsObject cms, CmsUser user, String uploadedFile) { 260 261 boolean result = false; 262 try { 263 setUserImage(cms, user, uploadedFile); 264 result = true; 265 } catch (CmsException e) { 266 LOG.error("Error setting user image.", e); 267 } 268 try { 269 cms.lockResource(uploadedFile); 270 cms.deleteResource(uploadedFile, CmsResource.DELETE_REMOVE_SIBLINGS); 271 } catch (CmsException e) { 272 LOG.error("Error deleting user image temp file.", e); 273 } 274 return result; 275 } 276 277 /** 278 * Handles a user image upload. 279 * The uploaded file will be scaled and save as a new file beneath /system/userimages/, the original file will be deleted.<p> 280 * 281 * @param cms the cms context 282 * @param uploadedFiles the uploaded file paths 283 * 284 * @return <code>true</code> in case the image was set successfully 285 */ 286 public boolean handleImageUpload(CmsObject cms, List<String> uploadedFiles) { 287 288 boolean result = false; 289 if (uploadedFiles.size() == 1) { 290 String tempFile = CmsStringUtil.joinPaths(USER_IMAGE_FOLDER, TEMP_FOLDER, uploadedFiles.get(0)); 291 result = handleImageUpload(cms, cms.getRequestContext().getCurrentUser(), tempFile); 292 } 293 return result; 294 } 295 296 /** 297 * Sets the user image for the given user.<p> 298 * 299 * @param cms the cms context 300 * @param user the user 301 * @param rootPath the image root path 302 * 303 * @throws CmsException in case anything goes wrong 304 */ 305 public void setUserImage(CmsObject cms, CmsUser user, String rootPath) throws CmsException { 306 307 CmsFile tempFile = cms.readFile(cms.getRequestContext().removeSiteRoot(rootPath)); 308 CmsImageScaler scaler = new CmsImageScaler(tempFile.getContents(), tempFile.getRootPath()); 309 310 if (scaler.isValid()) { 311 scaler.setType(2); 312 scaler.setHeight(192); 313 scaler.setWidth(192); 314 byte[] content = scaler.scaleImage(tempFile); 315 String previousImage = (String)user.getAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO); 316 String newFileName = USER_IMAGE_FOLDER 317 + user.getId().toString() 318 + "_" 319 + System.currentTimeMillis() 320 + getSuffix(tempFile.getName()); 321 CmsObject adminCms = OpenCms.initCmsObject(m_adminCms); 322 CmsProject tempProject = adminCms.createTempfileProject(); 323 adminCms.getRequestContext().setCurrentProject(tempProject); 324 if (adminCms.existsResource(newFileName)) { 325 // a user image of the given name already exists, just write the new content 326 CmsFile imageFile = adminCms.readFile(newFileName); 327 adminCms.lockResource(imageFile); 328 imageFile.setContents(content); 329 adminCms.writeFile(imageFile); 330 adminCms.writePropertyObject( 331 newFileName, 332 new CmsProperty(CmsPropertyDefinition.PROPERTY_IMAGE_SIZE, null, "w:192,h:192")); 333 334 } else { 335 // create a new user image file 336 adminCms.createResource( 337 newFileName, 338 OpenCms.getResourceManager().getResourceType(CmsResourceTypeImage.getStaticTypeName()), 339 content, 340 Collections.singletonList( 341 new CmsProperty(CmsPropertyDefinition.PROPERTY_IMAGE_SIZE, null, "w:192,h:192"))); 342 } 343 if (newFileName.equals(previousImage)) { 344 previousImage = null; 345 } 346 347 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(previousImage)) { 348 previousImage = (String)user.getAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO); 349 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(previousImage) 350 && cms.existsResource(newFileName, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) { 351 try { 352 adminCms.lockResource(previousImage); 353 adminCms.deleteResource(previousImage, CmsResource.DELETE_REMOVE_SIBLINGS); 354 } catch (CmsException e) { 355 LOG.error("Error deleting previous user image.", e); 356 } 357 } 358 } 359 user.setAdditionalInfo(CmsUserIconHelper.USER_IMAGE_INFO, newFileName); 360 adminCms.writeUser(user); 361 362 try { 363 OpenCms.getPublishManager().publishProject(adminCms); 364 } catch (Exception e) { 365 LOG.error("Error publishing user image resources.", e); 366 } 367 368 } 369 } 370 371 /** 372 * Returns the ident-icon path for the given user.<p> 373 * 374 * @param cms the cms context 375 * @param user the user 376 * @param size IconSize to get icon for 377 * 378 * @return the icon path 379 */ 380 private String getIconPath(CmsObject cms, CmsUser user, IconSize size) { 381 382 String userIconPath = (String)user.getAdditionalInfo(USER_IMAGE_INFO); 383 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(userIconPath)) { 384 userIconPath += size.equals(IconSize.Big) 385 ? "" 386 : "?__scale=h:" + size.getSize() + ",w:" + size.getSize() + ",t:2"; 387 return OpenCms.getLinkManager().substituteLinkForRootPath(cms, userIconPath); 388 } 389 390 boolean isAdmin = OpenCms.getRoleManager().hasRole(cms, user.getName(), CmsRole.ADMINISTRATOR); 391 String name = user.getName() + Boolean.toString(isAdmin); 392 String rfsName = toRfsName(name, size); 393 String path = toPath(name, size); 394 if (!m_cache.hasCacheContent(rfsName)) { 395 396 BufferedImage icon = m_renderer.render(name, isAdmin, size.getSize()); 397 try { 398 m_cache.saveCacheFile(rfsName, getImageBytes(icon)); 399 } catch (Exception e) { 400 LOG.error(e.getLocalizedMessage(), e); 401 } 402 } 403 return path; 404 } 405 406 /** 407 * Returns the image data. 408 * @param image the image 409 * 410 * @return the data 411 * 412 * @throws IOException in case writing to the output stream failed 413 */ 414 private byte[] getImageBytes(BufferedImage image) throws IOException { 415 416 return Simapi.getImageBytes(image, Simapi.TYPE_PNG); 417 } 418 419 /** 420 * Returns the file suffix.<p> 421 * 422 * @param fileName the file name 423 * 424 * @return the suffix 425 */ 426 private String getSuffix(String fileName) { 427 428 int index = fileName.lastIndexOf("."); 429 if (index > 0) { 430 return fileName.substring(index); 431 } else { 432 return fileName; 433 } 434 } 435 436 /** 437 * Transforms user name and icon size into the image path. 438 * 439 * @param name the user name 440 * @param size IconSize to get icon for 441 * 442 * @return the path 443 */ 444 private String toPath(String name, IconSize size) { 445 446 return CmsStringUtil.joinPaths(CmsWorkplace.getSkinUri(), ICON_FOLDER, "" + name.hashCode()) + size.getSuffix(); 447 } 448 449 /** 450 * Transforms user name and icon size into the rfs image path. 451 * 452 * @param name the user name 453 * @param size IconSize to get icon for 454 * 455 * @return the path 456 */ 457 private String toRfsName(String name, IconSize size) { 458 459 return CmsStringUtil.joinPaths(m_cache.getRepositoryPath(), "" + name.hashCode()) + size.getSuffix(); 460 } 461}