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