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