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 GmbH & Co. KG, 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.file; 029 030import org.opencms.db.CmsUserSettings; 031import org.opencms.main.CmsException; 032import org.opencms.main.CmsIllegalArgumentException; 033import org.opencms.main.OpenCms; 034import org.opencms.security.CmsPrincipal; 035import org.opencms.security.CmsSecurityException; 036import org.opencms.security.I_CmsPrincipal; 037import org.opencms.util.CmsMacroResolver; 038import org.opencms.util.CmsStringUtil; 039import org.opencms.util.CmsUUID; 040 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.Locale; 044import java.util.Map; 045import java.util.Objects; 046 047/** 048 * A user principal in the OpenCms permission system.<p> 049 * 050 * A user in OpenCms is uniquely defined by its user named returned by 051 * <code>{@link #getName()}</code>.<p> 052 * 053 * Basic users in OpenCms are users that can access the OpenCms Workplace. 054 * Moreover, the user must be created by another user with the 055 * <code>{@link org.opencms.security.CmsRole#ACCOUNT_MANAGER}</code> role. 056 * These users are "content managers" that actually have write permissions in 057 * at last some parts of the VFS.<p> 058 * 059 * Another possibility is to have users in a 'Guests' group. 060 * These users do not have access to the OpenCms Workplace. 061 * However, an user in a 'Guests' group can be created by every user, for example 062 * the "Guest" user. The main use case is that these users are used for users of 063 * the website that can generate their own accounts, in a "please register your 064 * account..." scenario. 065 * These user accounts can then be used to build personalized web sites.<p> 066 * 067 * @since 6.0.0 068 * 069 * @see CmsGroup 070 */ 071public class CmsUser extends CmsPrincipal implements Cloneable { 072 073 /** Flag indicating changed additional infos. */ 074 public static final int FLAG_ADDITIONAL_INFOS = 4; 075 076 /** Flag indicating changed core data. */ 077 public static final int FLAG_CORE_DATA = 8; 078 079 /** Flag indicating a changed last login date. */ 080 public static final int FLAG_LAST_LOGIN = 2; 081 082 /** Storage for additional user information. */ 083 private Map<String, Object> m_additionalInfo; 084 085 /** The creation date. */ 086 private long m_dateCreated; 087 088 /** The email of the user. */ 089 private String m_email; 090 091 /** The first name of this user. */ 092 private String m_firstname; 093 094 /** Boolean flag whether the last-login time stamp of this user was modified. */ 095 private boolean m_isTouched; 096 097 /** The last login date of this user. */ 098 private long m_lastlogin; 099 100 /** The last name of this user. */ 101 private String m_lastname; 102 103 /** The password of this user. */ 104 private String m_password; 105 106 /** 107 * Creates a new, empty OpenCms user principal.<p> 108 * 109 * Mostly intended to be used with the <code>org.opencms.workplace.tools.accounts.A_CmsEditUserDialog</code>.<p> 110 */ 111 public CmsUser() { 112 113 this( 114 null, 115 "", 116 "", 117 "", 118 "", 119 "", 120 0, 121 I_CmsPrincipal.FLAG_ENABLED, 122 System.currentTimeMillis(), 123 Collections.singletonMap(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, (Object)"")); 124 } 125 126 /** 127 * Creates a new OpenCms user principal.<p> 128 * 129 * @param id the unique id of the new user 130 * @param name the fully qualified name of the new user 131 * @param password the password of the user 132 * @param firstname the first name 133 * @param lastname the last name 134 * @param email the email address 135 * @param lastlogin time stamp 136 * @param flags flags 137 * @param dateCreated the creation date 138 * @param additionalInfo user related information 139 */ 140 public CmsUser( 141 CmsUUID id, 142 String name, 143 String password, 144 String firstname, 145 String lastname, 146 String email, 147 long lastlogin, 148 int flags, 149 long dateCreated, 150 Map<String, Object> additionalInfo) { 151 152 m_id = id; 153 m_name = name; 154 m_password = password; 155 m_firstname = firstname; 156 m_lastname = lastname; 157 m_email = email; 158 m_lastlogin = lastlogin; 159 m_flags = flags; 160 m_dateCreated = dateCreated; 161 if (additionalInfo != null) { 162 m_additionalInfo = new HashMap<String, Object>(additionalInfo); 163 } else { 164 m_additionalInfo = new HashMap<String, Object>(); 165 } 166 if (m_additionalInfo.get(CmsUserSettings.ADDITIONAL_INFO_ADDRESS) == null) { 167 m_additionalInfo.put(CmsUserSettings.ADDITIONAL_INFO_ADDRESS, ""); 168 } 169 if (m_additionalInfo.get(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION) == null) { 170 m_additionalInfo.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, ""); 171 } 172 } 173 174 /** 175 * Validates an email address.<p> 176 * 177 * That means, the parameter should only be composed by digits and standard english letters, points, underscores and exact one "At" symbol.<p> 178 * 179 * @param email the email to validate 180 */ 181 public static void checkEmail(String email) { 182 183 OpenCms.getValidationHandler().checkEmail(email); 184 } 185 186 /** 187 * Validates a zip code.<p> 188 * 189 * That means, the parameter should only be composed by digits and standard english letters.<p> 190 * 191 * @param zipcode the zip code to validate 192 */ 193 public static void checkZipCode(String zipcode) { 194 195 OpenCms.getValidationHandler().checkZipCode(zipcode); 196 } 197 198 /** 199 * Returns the "full" name of the given user in the format <code>"{firstname} {lastname} ({username})"</code>, 200 * or the empty String <code>""</code> if the user is null.<p> 201 * 202 * @param user the user to get the full name from 203 * @return the "full" name the user 204 * 205 * @see #getFullName() 206 */ 207 public static String getFullName(CmsUser user) { 208 209 if (user == null) { 210 return ""; 211 } else { 212 return user.getFullName(); 213 } 214 } 215 216 /** 217 * Checks whether the flag indicates additional info changes.<p> 218 * 219 * @param changes the changes flags 220 * 221 * @return <code>true</code> in case the additional infos changed 222 */ 223 public static boolean hasChangedAdditionalInfos(int changes) { 224 225 return (changes & FLAG_ADDITIONAL_INFOS) == FLAG_ADDITIONAL_INFOS; 226 } 227 228 /** 229 * Checks whether the flag indicates core data changes.<p> 230 * 231 * @param changes the changes flags 232 * 233 * @return <code>true</code> in case the core data changed 234 */ 235 public static boolean hasChangedCoreData(int changes) { 236 237 return (changes & FLAG_CORE_DATA) == FLAG_CORE_DATA; 238 } 239 240 /** 241 * Checks whether the flag indicates last login date changes.<p> 242 * 243 * @param changes the changes flags 244 * 245 * @return <code>true</code> in case the last login date changed 246 */ 247 public static boolean hasChangedLastLogin(int changes) { 248 249 return (changes & FLAG_LAST_LOGIN) == FLAG_LAST_LOGIN; 250 } 251 252 /** 253 * Checks if the given String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot.<p> 254 * 255 * <ul> 256 * <li>Works if the given String is <code>null</code>. 257 * <li>Removes white spaces around the String before the check. 258 * <li>Also works with prefixes not being in upper case. 259 * <li>Does not check if the user after the prefix actually exists. 260 * </ul> 261 * 262 * @param principalName the user name to check 263 * 264 * @return <code>true</code> in case the String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot 265 */ 266 public static boolean hasPrefix(String principalName) { 267 268 return CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName) 269 && (principalName.trim().toUpperCase().startsWith(I_CmsPrincipal.PRINCIPAL_USER + ".")); 270 } 271 272 /** 273 * Removes the prefix if the given String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot.<p> 274 * 275 * <ul> 276 * <li>Works if the given String is <code>null</code>. 277 * <li>If the given String does not start with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot it is returned unchanged. 278 * <li>Removes white spaces around the user name. 279 * <li>Also works with prefixes not being in upper case. 280 * <li>Does not check if the user after the prefix actually exists. 281 * </ul> 282 * 283 * @param principalName the user name to remove the prefix from 284 * 285 * @return the given String with the prefix {@link I_CmsPrincipal#PRINCIPAL_USER} and the following dot removed 286 */ 287 public static String removePrefix(String principalName) { 288 289 String result = principalName; 290 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)) { 291 if (hasPrefix(principalName)) { 292 result = principalName.trim().substring(I_CmsPrincipal.PRINCIPAL_USER.length() + 1); 293 } 294 } 295 return result; 296 } 297 298 /** 299 * Checks if the provided user name is a valid user name and can be used as an argument value 300 * for {@link #setName(String)}.<p> 301 * 302 * @param name the user name to check 303 * 304 * @throws CmsIllegalArgumentException if the check fails 305 */ 306 public void checkName(String name) throws CmsIllegalArgumentException { 307 308 OpenCms.getValidationHandler().checkUserName(name); 309 } 310 311 /** 312 * @see java.lang.Object#clone() 313 */ 314 @Override 315 public CmsUser clone() { 316 317 return new CmsUser( 318 m_id, 319 m_name, 320 m_password, 321 m_firstname, 322 m_lastname, 323 m_email, 324 m_lastlogin, 325 m_flags, 326 m_dateCreated, 327 new HashMap<String, Object>(m_additionalInfo)); 328 } 329 330 /** 331 * Deletes a value from this users "additional information" storage map.<p> 332 * 333 * @param key the additional user information to delete 334 * 335 * @see #getAdditionalInfo() 336 */ 337 public void deleteAdditionalInfo(String key) { 338 339 m_additionalInfo.remove(key); 340 } 341 342 /** 343 * Returns this users complete "additional information" storage map.<p> 344 * 345 * The "additional information" storage map is a simple {@link java.util.Map} 346 * that can be used to store any key / value pairs for the user. 347 * Some information parts of the users address are stored in this map 348 * by default.<p> 349 * 350 * @return this users complete "additional information" storage map 351 */ 352 public Map<String, Object> getAdditionalInfo() { 353 354 return m_additionalInfo; 355 } 356 357 /** 358 * Returns a value from this users "additional information" storage map, 359 * or <code>null</code> if no value for the given key is available.<p> 360 * 361 * @param key selects the value to return from the "additional information" storage map 362 * 363 * @return the selected value from this users "additional information" storage map 364 * 365 * @see #getAdditionalInfo() 366 */ 367 public Object getAdditionalInfo(String key) { 368 369 return m_additionalInfo.get(key); 370 } 371 372 /** 373 * Returns the address line of this user.<p> 374 * 375 * @return the address line of this user 376 */ 377 public String getAddress() { 378 379 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ADDRESS); 380 } 381 382 /** 383 * Returns the changes of this user compared to the previous user data.<p> 384 * 385 * @param oldUser the old user 386 * 387 * @return the changes flags 388 */ 389 public int getChanges(CmsUser oldUser) { 390 391 int result = 0; 392 if (oldUser.m_lastlogin != m_lastlogin) { 393 result = result | FLAG_LAST_LOGIN; 394 } 395 if (!oldUser.m_additionalInfo.equals(m_additionalInfo)) { 396 result = result | FLAG_ADDITIONAL_INFOS; 397 } 398 if (!Objects.equals(oldUser.m_email, m_email) 399 || !Objects.equals(oldUser.m_description, m_description) 400 || !Objects.equals(oldUser.m_firstname, m_firstname) 401 || !Objects.equals(oldUser.m_lastname, m_lastname) 402 || !Objects.equals(oldUser.m_password, m_password) 403 || (oldUser.m_flags != m_flags)) { 404 result = result | FLAG_CORE_DATA; 405 } 406 return result; 407 } 408 409 /** 410 * Returns the city information of this user.<p> 411 * 412 * This information is stored in the "additional information" storage map 413 * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_CITY}</code>.<p> 414 * 415 * @return the city information of this user 416 */ 417 public String getCity() { 418 419 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_CITY); 420 } 421 422 /** 423 * Returns the country information of this user.<p> 424 * 425 * This information is stored in the "additional information" storage map 426 * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_COUNTRY}</code>.<p> 427 * 428 * @return the country information of this user 429 */ 430 public String getCountry() { 431 432 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_COUNTRY); 433 } 434 435 /** 436 * Returns the creation date.<p> 437 * 438 * @return the creation date 439 */ 440 public long getDateCreated() { 441 442 return m_dateCreated; 443 } 444 445 /** 446 * @see org.opencms.security.CmsPrincipal#getDescription() 447 */ 448 @Override 449 public String getDescription() { 450 451 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION); 452 } 453 454 /** 455 * Returns the description of this organizational unit.<p> 456 * 457 * @param locale the locale 458 * 459 * @return the description of this organizational unit 460 */ 461 public String getDescription(Locale locale) { 462 463 CmsMacroResolver macroResolver = new CmsMacroResolver(); 464 macroResolver.setMessages(org.opencms.db.generic.Messages.get().getBundle(locale)); 465 return macroResolver.resolveMacros((String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION)); 466 } 467 468 /** 469 * @see org.opencms.security.CmsPrincipal#getDisplayName(org.opencms.file.CmsObject, java.util.Locale) 470 */ 471 @Override 472 public String getDisplayName(CmsObject cms, Locale locale) throws CmsException { 473 474 if (OpenCms.getOrgUnitManager().getOrganizationalUnits(cms, "", true).size() > 0) { 475 return org.opencms.security.Messages.get().getBundle(locale).key( 476 org.opencms.security.Messages.GUI_PRINCIPAL_DISPLAY_NAME_2, 477 getFullName(), 478 OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale)); 479 } else { 480 return getFullName(); 481 } 482 } 483 484 /** 485 * Returns the email address of this user.<p> 486 * 487 * @return the email address of this user 488 */ 489 public String getEmail() { 490 491 return m_email; 492 } 493 494 /** 495 * Returns the first name of this user.<p> 496 * 497 * @return the first name of this user 498 */ 499 public String getFirstname() { 500 501 return m_firstname; 502 } 503 504 /** 505 * Returns the "full" name of the this user in the format <code>"{firstname} {lastname} ({username})"</code>.<p> 506 * 507 * @return the "full" name this user 508 */ 509 public String getFullName() { 510 511 StringBuffer buf = new StringBuffer(); 512 String first = getFirstname(); 513 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(first)) { 514 buf.append(first); 515 buf.append(" "); 516 } 517 String last = getLastname(); 518 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(last)) { 519 buf.append(last); 520 buf.append(" "); 521 } 522 buf.append("("); 523 buf.append(getSimpleName()); 524 buf.append(")"); 525 return buf.toString(); 526 } 527 528 /** 529 * Returns the institution information of this user.<p> 530 * 531 * This information is stored in the "additional information" storage map 532 * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_INSTITUTION}</code>.<p> 533 * 534 * @return the institution information of this user 535 */ 536 public String getInstitution() { 537 538 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_INSTITUTION); 539 } 540 541 /** 542 * Returns the time of the last login of this user.<p> 543 * 544 * @return the time of the last login of this user 545 */ 546 public long getLastlogin() { 547 548 return m_lastlogin; 549 } 550 551 /** 552 * Returns the last name of this user.<p> 553 * 554 * @return the last name of this user 555 */ 556 public String getLastname() { 557 558 return m_lastname; 559 } 560 561 /** 562 * Returns the encrypted user password.<p> 563 * 564 * @return the encrypted user password 565 */ 566 public String getPassword() { 567 568 return m_password; 569 } 570 571 /** 572 * Returns the zip code information of this user.<p> 573 * 574 * This information is stored in the "additional information" storage map 575 * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_ZIPCODE}</code>.<p> 576 * 577 * @return the zip code information of this user 578 */ 579 public String getZipcode() { 580 581 return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ZIPCODE); 582 } 583 584 /** 585 * @see org.opencms.security.I_CmsPrincipal#isGroup() 586 */ 587 @Override 588 public boolean isGroup() { 589 590 return false; 591 } 592 593 /** 594 * Checks if this user is the default guest user.<p> 595 * 596 * @return <code>true</code> if this user is the default guest user 597 */ 598 public boolean isGuestUser() { 599 600 return OpenCms.getDefaultUsers().isUserGuest(getName()); 601 } 602 603 /** 604 * Returns <code>true</code> if this user is not able to manage itself.<p> 605 * 606 * @return <code>true</code> if this user is not able to manage itself 607 */ 608 public boolean isManaged() { 609 610 return (getFlags() & I_CmsPrincipal.FLAG_USER_MANAGED) == I_CmsPrincipal.FLAG_USER_MANAGED; 611 } 612 613 /** 614 * Returns <code>true</code> if this user was touched.<p> 615 * 616 * @return boolean true if this user was touched 617 */ 618 public boolean isTouched() { 619 620 return m_isTouched; 621 } 622 623 /** 624 * @see org.opencms.security.I_CmsPrincipal#isUser() 625 */ 626 @Override 627 public boolean isUser() { 628 629 return true; 630 } 631 632 /** 633 * Checks if the user is marked as webuser.<p> 634 * 635 * @return <code>true</code> if the user is marked as webuser 636 */ 637 public boolean isWebuser() { 638 639 return (getFlags() & FLAG_USER_WEBUSER) == FLAG_USER_WEBUSER; 640 } 641 642 /** 643 * Sets this users complete "additional information" storage map to the given value.<p> 644 * 645 * @param additionalInfo the complete "additional information" map to set 646 * 647 * @see #getAdditionalInfo() 648 */ 649 public void setAdditionalInfo(Map<String, Object> additionalInfo) { 650 651 m_additionalInfo = additionalInfo; 652 } 653 654 /** 655 * Stores a value in this users "additional information" storage map with the given access key.<p> 656 * 657 * @param key the key to store the value under 658 * @param value the value to store in the users "additional information" storage map 659 * 660 * @see #getAdditionalInfo() 661 */ 662 public void setAdditionalInfo(String key, Object value) { 663 664 if (key == null) { 665 throw new CmsIllegalArgumentException( 666 Messages.get().container(Messages.ERR_USER_ADDINFO_KEY_NULL_1, getFullName())); 667 } 668 m_additionalInfo.put(key, value); 669 } 670 671 /** 672 * Sets the address line of this user.<p> 673 * 674 * @param address the address line to set 675 */ 676 public void setAddress(String address) { 677 678 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ADDRESS, address); 679 } 680 681 /** 682 * Sets the city information of this user.<p> 683 * 684 * @param city the city information to set 685 */ 686 public void setCity(String city) { 687 688 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_CITY, city); 689 } 690 691 /** 692 * Sets the country information of this user.<p> 693 * 694 * @param country the city information to set 695 */ 696 public void setCountry(String country) { 697 698 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_COUNTRY, country); 699 } 700 701 /** 702 * @see org.opencms.security.CmsPrincipal#setDescription(java.lang.String) 703 */ 704 @Override 705 public void setDescription(String description) { 706 707 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description); 708 } 709 710 /** 711 * Sets the email address of this user.<p> 712 * 713 * @param email the email address to set 714 */ 715 public void setEmail(String email) { 716 717 checkEmail(email); 718 if (email != null) { 719 email = email.trim(); 720 } 721 m_email = email; 722 } 723 724 /** 725 * Sets the first name of this user.<p> 726 * 727 * @param firstname the name to set 728 */ 729 public void setFirstname(String firstname) { 730 731 OpenCms.getValidationHandler().checkFirstname(firstname); 732 if (firstname != null) { 733 firstname = firstname.trim(); 734 } 735 m_firstname = firstname; 736 } 737 738 /** 739 * Sets the institution information of this user.<p> 740 * 741 * @param institution the institution information to set 742 */ 743 public void setInstitution(String institution) { 744 745 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_INSTITUTION, institution); 746 } 747 748 /** 749 * Sets the last login time stamp of this user.<p> 750 * 751 * @param value the last login time stamp to set 752 */ 753 public void setLastlogin(long value) { 754 755 m_isTouched = true; 756 m_lastlogin = value; 757 } 758 759 /** 760 * Sets the last name of this user.<p> 761 * 762 * @param lastname the name to set 763 */ 764 public void setLastname(String lastname) { 765 766 OpenCms.getValidationHandler().checkLastname(lastname); 767 if (lastname != null) { 768 lastname = lastname.trim(); 769 } 770 m_lastname = lastname; 771 } 772 773 /** 774 * Sets the managed flag for this user to the given value.<p> 775 * 776 * @param value the value to set 777 */ 778 public void setManaged(boolean value) { 779 780 if (isManaged() != value) { 781 setFlags(getFlags() ^ I_CmsPrincipal.FLAG_USER_MANAGED); 782 } 783 } 784 785 /** 786 * Sets the password of this user.<p> 787 * 788 * @param value the password to set 789 */ 790 public void setPassword(String value) { 791 792 try { 793 OpenCms.getPasswordHandler().validatePassword(value); 794 } catch (CmsSecurityException e) { 795 throw new CmsIllegalArgumentException(e.getMessageContainer()); 796 } 797 m_password = value; 798 } 799 800 /** 801 * Sets the zip code information of this user.<p> 802 * 803 * @param zipcode the zip code information to set 804 */ 805 public void setZipcode(String zipcode) { 806 807 checkZipCode(zipcode); 808 if (zipcode != null) { 809 zipcode = zipcode.toUpperCase(); 810 } 811 setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ZIPCODE, zipcode); 812 } 813 814 /** 815 * @see java.lang.Object#toString() 816 */ 817 @Override 818 public String toString() { 819 820 StringBuffer result = new StringBuffer(); 821 result.append("[User]"); 822 result.append(" name:"); 823 result.append(getName()); 824 result.append(" id:"); 825 result.append(m_id); 826 result.append(" flags:"); 827 result.append(getFlags()); 828 result.append(" description:"); 829 result.append(getDescription()); 830 return result.toString(); 831 } 832 833 /** 834 * Sets the "touched" status of this user to <code>true</code>.<p> 835 */ 836 public void touch() { 837 838 m_isTouched = true; 839 } 840}