com.bloatit.model.Member.java Source code

Java tutorial

Introduction

Here is the source code for com.bloatit.model.Member.java

Source

//
// Copyright (c) 2011 Linkeos.
//
// This file is part of Elveos.org.
// Elveos.org is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// Elveos.org is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
// You should have received a copy of the GNU General Public License along
// with Elveos.org. If not, see http://www.gnu.org/licenses/.
//
package com.bloatit.model;

import java.util.Date;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Set;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;

import com.bloatit.data.DaoExternalAccount;
import com.bloatit.data.DaoExternalServiceMembership;
import com.bloatit.data.DaoExternalServiceMembership.RightLevel;
import com.bloatit.data.DaoFileMetadata;
import com.bloatit.data.DaoFollowActor;
import com.bloatit.data.DaoFollowFeature;
import com.bloatit.data.DaoFollowSoftware;
import com.bloatit.data.DaoInternalAccount;
import com.bloatit.data.DaoJoinTeamInvitation;
import com.bloatit.data.DaoJoinTeamInvitation.State;
import com.bloatit.data.DaoMember;
import com.bloatit.data.DaoMember.EmailStrategy;
import com.bloatit.data.DaoMember.Role;
import com.bloatit.data.DaoTeamRight.UserTeamRight;
import com.bloatit.data.DaoUserContent;
import com.bloatit.framework.exceptions.lowlevel.MalformedArgumentException;
import com.bloatit.framework.exceptions.lowlevel.NonOptionalParameterException;
import com.bloatit.framework.utils.Hash;
import com.bloatit.framework.utils.PageIterable;
import com.bloatit.framework.utils.datetime.DateUtils;
import com.bloatit.framework.webprocessor.context.User;
import com.bloatit.model.feature.FeatureList;
import com.bloatit.model.feature.FeatureManager;
import com.bloatit.model.lists.CommentList;
import com.bloatit.model.lists.ContributionList;
import com.bloatit.model.lists.JoinTeamInvitationList;
import com.bloatit.model.lists.KudosList;
import com.bloatit.model.lists.ListBinder;
import com.bloatit.model.lists.MilestoneList;
import com.bloatit.model.lists.MoneyWithdrawalList;
import com.bloatit.model.lists.OfferList;
import com.bloatit.model.lists.TeamList;
import com.bloatit.model.lists.TranslationList;
import com.bloatit.model.lists.UserContentList;
import com.bloatit.model.right.Action;
import com.bloatit.model.right.AuthToken;
import com.bloatit.model.right.RgtMember;
import com.bloatit.model.right.RightManager;
import com.bloatit.model.right.RightManager.Private;
import com.bloatit.model.right.UnauthorizedOperationException;
import com.bloatit.model.right.UnauthorizedOperationException.SpecialCode;
import com.bloatit.model.right.UnauthorizedPrivateAccessException;
import com.bloatit.model.right.UnauthorizedPublicAccessException;
import com.bloatit.model.visitor.ModelClassVisitor;

public final class Member extends Actor<DaoMember> implements User {

    // /////////////////////////////////////////////////////////////////////////////////////////
    // CONSTRUCTION
    // /////////////////////////////////////////////////////////////////////////////////////////

    private static final int PASSWORD_SALT_LENGTH = 50;
    private static final String RESET_SALT = "GSQISUDHOI1232193IOSDJHOQIOOISQJD";
    private static final String ACTIVATE_SALT = "1Z9UI901IE09II90I31JD091J09DJ01KPO";

    private static final class MyCreator extends Creator<DaoMember, Member> {
        @SuppressWarnings("synthetic-access")
        @Override
        public Member doCreate(final DaoMember dao) {
            return new Member(dao);
        }
    }

    /**
     * Create a new member using its Dao version.
     *
     * @param dao a DaoMember
     * @return the new member or null if dao is null.
     */
    @SuppressWarnings("synthetic-access")
    public static Member create(final DaoMember dao) {
        return new MyCreator().create(dao);
    }

    /**
     * Create a new DaoActor. Initialize the creation date to now. Create a new
     * {@link DaoInternalAccount} and a new {@link DaoExternalAccount}.
     *
     * @param login is the login or name of this actor. It must be non null,
     *            unique, longer than 2 chars and do not contains space chars
     *            ("[^\\p{Space}]+").
     * @throws NonOptionalParameterException if login or mail is null.
     * @throws MalformedArgumentException if the login is to small or contain
     *             space chars.
     */
    private static DaoMember createDaoMember(final String login, final String password, final String email,
            final Locale locale) {
        final String salt = RandomStringUtils.randomAscii(PASSWORD_SALT_LENGTH);
        final String passwd = Hash.calculateHash(password, salt);
        Reporting.reporter.reportMemberCreation(login);
        return DaoMember.createAndPersist(login, passwd, salt, email, locale);
    }

    public Member(final String login, final String password, final String email, final Locale locale) {
        super(createDaoMember(login, password, email, locale));

    }

    public Member(final String login, final String password, final String email, final String fullname,
            final Locale locale) {
        this(login, password, email, locale);
        getDao().setFullname(fullname);
    }

    private Member(final DaoMember dao) {
        super(dao);
    }

    // /////////////////////////////////////////////////////////////////////////////////////////
    // Accessors
    // /////////////////////////////////////////////////////////////////////////////////////////

    /**
     * To invite a member into a team you have to have the WRITE right on the
     * "invite" property.
     *
     * @param member The member you want to invite
     * @param team The team in which you invite a member.
     * @throws UnauthorizedOperationException
     */
    public void sendInvitation(final Member member, final Team team) throws UnauthorizedOperationException {
        if (!hasInviteTeamRight(team)) {
            throw new UnauthorizedOperationException(SpecialCode.INVITATION_SEND_NO_RIGHT);
        }
        DaoJoinTeamInvitation.createAndPersist(getDao(), member.getDao(), team.getDao());
    }

    /**
     * To accept an invitation you must have the DELETED right on the "invite"
     * property. If the invitation is not in PENDING state then nothing is done,
     * and <i>false</i> is returned.
     *
     * @param invitation the authenticate member must be receiver of the
     *            invitation.
     * @return true if the invitation is accepted, false if there is an error.
     * @throws UnauthorizedOperationException
     */
    public boolean acceptInvitation(final JoinTeamInvitation invitation) throws UnauthorizedOperationException {
        if (!invitation.getReceiver().getId().equals(AuthToken.getMember().getId())) {
            throw new UnauthorizedOperationException(SpecialCode.INVITATION_RECIEVER_MISMATCH);
        }

        // Accept the invitation
        if (invitation.accept()) {
            // discard all other invitation to join the same team
            final Team team = invitation.getTeam();
            final PageIterable<JoinTeamInvitation> receivedInvitation = this.getReceivedInvitation(State.PENDING,
                    team);
            for (final JoinTeamInvitation invite : receivedInvitation) {
                invite.discard();
            }
            return true;
        }
        return false;
    }

    /**
     * To refuse an invitation you must have the DELETED right on the "invite"
     * property. If the invitation is not in PENDING state then nothing is done.
     *
     * @param invitation the authenticate member must be receiver of the
     *            invitation.
     * @throws UnauthorizedOperationException
     */
    public void refuseInvitation(final JoinTeamInvitation invitation) throws UnauthorizedOperationException {
        if (!invitation.getReceiver().getId().equals(AuthToken.getMember().getId())) {
            throw new UnauthorizedOperationException(SpecialCode.INVITATION_RECIEVER_MISMATCH);
        }
        invitation.refuse();
    }

    /**
     * To remove this member from a team you have to have the DELETED right on
     * the "team" property. If the member is not in the "team", nothing is done.
     * (Although it should be considered as an error and will be logged)
     *
     * @param aTeam is the team from which the user will be removed.
     * @throws UnauthorizedOperationException
     */
    public void kickFromTeam(final Team aTeam, final Member actor) throws UnauthorizedOperationException {
        if (!canBeKickFromTeam(aTeam, actor)) {
            throw new UnauthorizedOperationException(SpecialCode.TEAM_PROMOTE_RIGHT_MISSING);
        }
        getDao().removeFromTeam(aTeam.getDao());
    }

    /**
     * To add a user into a public team, you have to make sure you can access
     * the teams with the {@link Action#WRITE} action.
     *
     * @param team must be a public team.
     * @throws UnauthorizedOperationException if the authenticated member do not
     *             have the right to use this methods.
     */
    public void addToPublicTeam(final Team team) throws UnauthorizedOperationException {
        if (!team.isPublic()) {
            throw new UnauthorizedOperationException(SpecialCode.TEAM_NOT_PUBLIC);
        }
        getDao().addToTeam(team.getDao());
    }

    /**
     * Adds a user to a team without checking if the team is Public or not
     *
     * @param team the team to which the user will be added
     */
    void addToTeamUnprotected(final Team team) {
        getDao().addToTeam(team.getDao());
    }

    // User data modification

    /**
     * Updates user password with right checking
     *
     * @param password the new password
     * @throws UnauthorizedPrivateAccessException when the logged user cannot
     *             modify the password
     */
    public void setPassword(final String password) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Password(), Action.WRITE);
        getDao().setPassword(Hash.calculateHash(password, getDao().getSalt()));
    }

    /**
     * Checks if an inputed password matches the user password
     *
     * @param password the password to match
     * @return <i>true</i> if the inputed password matches the password in the
     *         database, <i>false</i> otherwise
     */
    public boolean checkPassword(final String password) {
        final String digestedPassword = Hash.calculateHash(password, getDao().getSalt());
        return getDao().passwordEquals(digestedPassword);
    }

    public void setFullname(final String fullname) throws UnauthorizedPublicAccessException {
        tryAccess(new RgtMember.FullName(), Action.WRITE);
        getDao().setFullname(fullname);
    }

    public void setAvatar(final FileMetadata fileImage) throws UnauthorizedPublicAccessException {
        tryAccess(new RgtMember.Avatar(), Action.WRITE);
        if (fileImage == null) {
            getDao().setAvatar(null);
        } else {
            getDao().setAvatar(fileImage.getDao());
        }
    }

    public void setDescription(final String userDescription) throws UnauthorizedPublicAccessException {
        tryAccess(new RgtMember.UserDescription(), Action.WRITE);
        getDao().setDescription(userDescription);
    }

    private String libravatar(final String email) {
        final String digest = DigestUtils.md5Hex(email.toLowerCase());
        // return "http://cdn.libravatar.org/avatar/" + digest +
        // "?d=http://elveos.org/resources/commons/img/none.png&s=64";
        return digest;
    }

    public void setEmail(final String email) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Email(), Action.WRITE);
        getDao().setEmail(email);
    }

    public void setLocal(final Locale loacle) throws UnauthorizedPublicAccessException {
        tryAccess(new RgtMember.Locale(), Action.WRITE);
        getDao().setLocale(loacle);
    }

    protected void setRole(final Role role) {
        getDao().setRole(role);
    }

    // Activation
    public boolean isActive() {
        return getDao().getActivationState() == ActivationState.ACTIVE;
    }

    public boolean activate(final String activationKey) {
        if (getDao().getActivationState() != ActivationState.VALIDATING) {
            return false;
        }
        if (getActivationKey().equals(activationKey) || getResetKey().equals(activationKey)) {
            getDao().setActivationState(ActivationState.ACTIVE);
            addToKarma(ModelConfiguration.getKarmaActivationAmount());
            return true;
        }
        if (getResetKey().equals(activationKey)) {
            getDao().setActivationState(ActivationState.ACTIVE);
            addToKarma(ModelConfiguration.getKarmaActivationAmount());
            return true;
        }

        return false;
    }

    public boolean hasEmailToActivate() {
        return getDao().getEmailToActivate() != null;
    }

    public boolean activateEmail(final String activationKey) {
        if (!hasEmailToActivate()) {
            return false;
        }

        if (getEmailActivationKey().equals(activationKey)) {
            getDao().setEmail(getDao().getEmailToActivate());
            getDao().setEmailToActivate(null);
            return true;
        }
        return false;

    }

    public void setEmailToActivate(final String email) {
        getDao().setEmailToActivate(email);
    }

    public void acceptNewsLetter(final boolean newsletter) {
        getDao().acceptNewsLetter(newsletter);
    }

    public FollowFeature followOrGetFeature(final Feature f) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        return FollowFeature.create(getDao().followOrGetFeature(((FeatureImplementation) f).getDao()));
    }

    public boolean isFollowing(final Feature feature) {
        if (feature == null) {
            return false;
        }
        return getDao().isFollowing(((FeatureImplementation) feature).getDao());
    }

    public boolean isFollowing(final Software software) {
        if (software == null) {
            return false;
        }
        return getDao().isFollowing(software.getDao());
    }

    public boolean isFollowing(final Actor<?> actor) {
        if (actor == null) {
            return false;
        }
        return getDao().isFollowing(actor.getDao());
    }

    public void unfollowFeature(final Feature f) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        final FollowFeature followFeature = followOrGetFeature(f);
        followFeature.getDao().unfollow();
    }

    public FollowSoftware followOrGetSoftware(final Software s) throws UnauthorizedOperationException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        if (!isFollowing(s)) {
            // Follow all features of the software
            for (final Feature feature : s.getFeatures()) {
                final FollowFeature followFeature = followOrGetFeature(feature);
                followFeature.setFeatureComment(true);
                followFeature.setBugComment(true);
            }
        }
        return FollowSoftware.create(getDao().followOrGetSoftware(s.getDao()));
    }

    public void unfollowSoftware(final Software s) throws UnauthorizedOperationException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        final FollowSoftware followSoftware = followOrGetSoftware(s);
        followSoftware.getDao().unfollow();

        // Unfollow all features of the software
        for (final FollowFeature followFeature : getFollowedFeatures()) {
            if (s.equals(followFeature.getFollowed().getSoftware())) {
                unfollowFeature(followFeature.getFollowed());
            }
        }
    }

    public FollowActor followOrGetActor(final Actor<?> f) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        return FollowActor.create(getDao().followOrGetActor(f.getDao()));
    }

    public void unfollowActor(final Actor<?> a) throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Follow(), Action.WRITE);
        final FollowActor followActor = followOrGetActor(a);
        followActor.getDao().unfollow();
    }

    public void setLastWatchedEvents(final Date lastWatchedEvents) {
        getDao().setLastWatchedEvents(lastWatchedEvents);
    }

    public void setEmailStrategy(final EmailStrategy emailStrategy) throws UnauthorizedPrivateAccessException {
        tryAccess(new Private(), Action.WRITE);
        getDao().setEmailStrategy(emailStrategy);
    }

    public void setGlobalFollow(final boolean globalFollow) throws UnauthorizedOperationException {
        tryAccess(new Private(), Action.WRITE);

        if (globalFollow && !isGlobalFollow()) {
            // Follow all
            for (final Feature feature : FeatureManager.getAllByCreationDate()) {
                final FollowFeature followFeature = followOrGetFeature(feature);
                followFeature.setFeatureComment(true);
                followFeature.setBugComment(true);
            }
        } else if (!globalFollow && isGlobalFollow()) {
            for (final FollowFeature followFeature : getFollowedFeatures()) {
                Software software = followFeature.getFollowed().getSoftware();
                if (software == null || !isFollowing(software)) {
                    unfollowFeature(followFeature.getFollowed());
                }
            }
        }
        getDao().setGlobalFollow(globalFollow);
    }

    public void setGlobalFollowWithMail(final boolean globalFollow) throws UnauthorizedOperationException {
        tryAccess(new Private(), Action.WRITE);

        for (final FollowFeature followFeature : getFollowedFeatures()) {
            Software software = followFeature.getFollowed().getSoftware();
            if (software == null || !isFollowing(software)) {
                followFeature.setMail(globalFollow);
            }
        }

        getDao().setGlobalFollowWithMail(globalFollow);
    }

    // /////////////////////////////////////////////////////////////////////////////////////////
    // Getters
    // /////////////////////////////////////////////////////////////////////////////////////////

    public Date getLastWatchedEvents() {
        final Date lastWatchedEvents = getDao().getLastWatchedEvents();
        if (lastWatchedEvents == null) {
            return DateUtils.dawnOfTime();
        } else {
            return lastWatchedEvents;
        }
    }

    /*
     * (non-Javadoc)
     * @see com.bloatit.model.User#getActivationState()
     */
    @Override
    public ActivationState getActivationState() {
        return getDao().getActivationState();
    }

    public String getDescription() {
        return getDao().getDescription();
    }

    public EmailStrategy getEmailStrategy() throws UnauthorizedPrivateAccessException {
        tryAccess(new Private(), Action.READ);
        return getDao().getEmailStrategy();
    }

    public FollowFeature getFollowFeature(final Feature feature) {
        return FollowFeature.create(getDao().getFollowFeature(((FeatureImplementation) feature).getDao()));
    }

    public boolean isGlobalFollow() throws UnauthorizedPrivateAccessException {
        tryAccess(new Private(), Action.READ);
        return getDao().isGlobalFollow();
    }

    public boolean isGlobalFollowWithMail() throws UnauthorizedPrivateAccessException {
        tryAccess(new Private(), Action.READ);
        return getDao().isGlobalFollowWithMail();
    }

    public String getActivationKey() {
        final DaoMember m = getDao();
        final String digest = "" + m.getId() + m.getEmail() + m.getFullname() + m.getPassword() + m.getSalt()
                + ACTIVATE_SALT;
        return DigestUtils.sha256Hex(digest);
    }

    public String getEmailActivationKey() {
        final DaoMember m = getDao();
        final String digest = "" + m.getId() + m.getEmail() + m.getEmailToActivate() + m.getFullname()
                + m.getPassword() + m.getSalt() + ACTIVATE_SALT;
        return DigestUtils.sha256Hex(digest);
    }

    /**
     * @param state can be PENDING, ACCEPTED or REFUSED
     * @return all the received invitation with the specified state.
     */
    public PageIterable<JoinTeamInvitation> getReceivedInvitation(final State state) {
        return new JoinTeamInvitationList(getDao().getReceivedInvitation(state));
    }

    /**
     * @param state can be PENDING, ACCEPTED or REFUSED
     * @param team the team invited to join
     * @return all the received invitation with the specified state and team
     */
    private PageIterable<JoinTeamInvitation> getReceivedInvitation(final State state, final Team team) {
        return new JoinTeamInvitationList(getDao().getReceivedInvitation(state, team.getDao()));
    }

    /**
     * @param state can be PENDING, ACCEPTED or REFUSED
     * @return all the sent invitation with the specified state.
     */
    public PageIterable<JoinTeamInvitation> getSentInvitation(final State state) {
        return new JoinTeamInvitationList(getDao().getSentInvitation(state));
    }

    /**
     * To get the teams you have the have the READ right on the "team" property.
     *
     * @return all the team in which this member is.
     * @throws UnauthorizedOperationException
     */
    public PageIterable<Team> getTeams() throws UnauthorizedOperationException {
        tryAccess(new RightManager.Public(), Action.READ);
        return new TeamList(getDao().getTeams());
    }

    public int getKarma() {
        return getDao().getKarma();
    }

    public PageIterable<Milestone> getMilestoneToInvoice() {
        return new MilestoneList(getDao().getMilestoneToInvoice());
    }

    public PageIterable<Milestone> getMilestoneInvoiced() {
        return new MilestoneList(getDao().getMilestoneInvoiced());
    }

    public PageIterable<Milestone> getMilestones() {
        return new MilestoneList(getDao().getMilestones());
    }

    // TODO make right managements
    public PageIterable<UserContent<? extends DaoUserContent>> getHistory() {
        return new UserContentList(getDao().getHistory());
    }

    private static final float INFLUENCE_MULTIPLICATOR = 2;
    private static final float INFLUENCE_GELIFICATOR = 20;
    // Minimum karma
    private static final float INFLUENCE_BASE = 1;

    protected int calculateInfluence() {
        final int karma = getDao().getKarma();
        if (karma >= 0) {
            return (int) (Math.log10((INFLUENCE_GELIFICATOR + karma) / INFLUENCE_GELIFICATOR)
                    * INFLUENCE_MULTIPLICATOR + INFLUENCE_BASE);
        }
        return 0;
    }

    @Override
    public String getDisplayName() {
        if (getDao().getFullname() != null && !getDao().getFullname().isEmpty()) {
            return getFullname();
        }
        return getLogin();
    }

    public String getEmail() throws UnauthorizedOperationException {
        tryAccess(new RgtMember.Email(), Action.READ);
        return getEmailUnprotected();
    }

    public String getEmailToActivate() throws UnauthorizedOperationException {
        tryAccess(new RgtMember.Email(), Action.READ);
        return getDao().getEmailToActivate();
    }

    // TODO: Create a send notification / mail
    public String getEmailUnprotected() {
        return getDao().getEmail();
    }

    public boolean getNewsletterAccept() {
        return getDao().getNewsletterAccept();
    }

    // no right management: this is public data
    public Locale getLocaleUnprotected() {
        return getDao().getLocale();
    }

    // no right management: this is public data
    @Override
    public Locale getLocale() {
        return getDao().getLocale();
    }

    // no right management: this is public data
    public String getFullname() {
        return getDao().getFullname();
    }

    // no right management: this is public data
    @Override
    public Image getAvatar() {
        final DaoFileMetadata avatar = getDao().getAvatar();
        if (avatar != null) {
            return new Image(FileMetadata.create(avatar));
        }
        String libravatar = null;
        libravatar = libravatar(getDao().getEmail().toLowerCase().trim());
        if (libravatar == null) {
            return null;
        }
        return new Image(libravatar);
    }

    public long getInvitationCount() {
        // TODO right management
        return getDao().getInvitationCount();
    }

    // no right management: this is public data
    public PageIterable<Feature> getFeatures(final boolean asMemberOnly) {
        return new FeatureList(getDao().getFeatures(asMemberOnly));
    }

    public PageIterable<Kudos> getKudos() throws UnauthorizedPrivateAccessException {
        tryAccess(new RgtMember.Kudos(), Action.READ);
        return new KudosList(getDao().getKudos());
    }

    @Override
    public PageIterable<Contribution> doGetContributions() {
        return getContributions(true);
    }

    @Override
    public PageIterable<MoneyWithdrawal> doGetMoneyWithdrawals() throws UnauthorizedOperationException {
        return new MoneyWithdrawalList(getDao().getMoneyWithdrawals());
    }

    // no right management: this is public data
    public PageIterable<Contribution> getContributions(final boolean asMemberOnly) {
        return new ContributionList(getDao().getContributions(asMemberOnly));
    }

    // no right management: this is public data
    public PageIterable<Comment> getComments(final boolean asMemberOnly) {
        return new CommentList(getDao().getComments(asMemberOnly));
    }

    // no right management: this is public data
    public PageIterable<Offer> getOffers(final boolean asMemberOnly) {
        return new OfferList(getDao().getOffers(asMemberOnly));
    }

    // no right management: this is public data
    public PageIterable<Translation> getTranslations(final boolean asMemberOnly) {
        return new TranslationList(getDao().getTranslations(asMemberOnly));
    }

    public void addToKarma(final int value) {
        getDao().addToKarma(value);
    }

    // no right management: this is public data
    public Role getRole() {
        return getDao().getRole();
    }

    // TODO make a more integrated method.
    public String getResetKey() {
        final DaoMember m = getDao();
        final String digest = "" + m.getId() + m.getEmail() + m.getFullname() + m.getPassword() + m.getSalt()
                + RESET_SALT;
        return DigestUtils.sha256Hex(digest);
    }

    public PageIterable<ExternalServiceMembership> getExternalServices() {
        return new ListBinder<ExternalServiceMembership, DaoExternalServiceMembership>(
                getDao().getAuthorizedExternalServices());
    }

    public PageIterable<FollowFeature> getFollowedFeatures() {
        return new ListBinder<FollowFeature, DaoFollowFeature>(getDao().getFollowedFeatures());
    }

    public PageIterable<FollowActor> getFollowedActors() {
        return new ListBinder<FollowActor, DaoFollowActor>(getDao().getFollowedActors());
    }

    public PageIterable<FollowSoftware> getFollowedSoftware() {
        return new ListBinder<FollowSoftware, DaoFollowSoftware>(getDao().getFollowedSoftware());
    }

    public void addAuthorizedExternalService(final String clientId, final String token,
            final EnumSet<RightLevel> rights) {
        getDao().addAuthorizedExternalService(clientId, token, rights);
    }

    // /////////////////////////////////////////////////////////////////////////////////////////
    // Accessors
    // /////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Tells if the authenticated user can access the <i>Avatar</i> property.
     *
     * @param action the type of access you want to do on the <i>Avatar</i>
     *            property.
     * @return true if you can access the <i>Avatar</i> property.
     */
    public final boolean canAccessAvatar(final Action action) {
        return canAccess(new RgtMember.Avatar(), action);
    }

    public boolean canGetTeams() {
        return canAccess(new RgtMember.Teams(), Action.READ);
    }

    public boolean canSetPassword() {
        return canAccess(new RgtMember.Password(), Action.WRITE);
    }

    public boolean canAccessEmail(final Action action) {
        return canAccess(new RgtMember.Email(), action);
    }

    /**
     * Indicates whether the logged member can access <code>ANY</code>of the
     * user's information
     *
     * @param action the type of action
     */
    public boolean canAccessUserInformations(final Action action) {
        return canAccess(new RgtMember.UserInformations(), action);
    }

    public boolean canBeKickFromTeam(final Team aTeam, final Member actor) {
        if (actor == null) {
            return false;
        }
        if (this.equals(actor)) {
            if (hasPromoteTeamRight(aTeam)) {
                return false;
            }
            return true;
        }
        if (!actor.hasPromoteTeamRight(aTeam)) {
            return false;
        }
        return true;
    }

    // /////////////////////////////////////////////////////////////////////////////////////////
    // Team Rights
    // /////////////////////////////////////////////////////////////////////////////////////////

    public boolean hasTeamRight(final Team aTeam, final UserTeamRight aRight) {
        if (getTeamRights(aTeam) == null) {
            return false;
        }
        return getTeamRights(aTeam).contains(aRight);
    }

    public Set<UserTeamRight> getTeamRights(final Team g) {
        return getDao().getTeamRights(g.getDao());
    }

    public boolean hasConsultTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.CONSULT);
    }

    public boolean hasTalkTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.TALK);
    }

    public boolean hasInviteTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.INVITE);
    }

    public boolean hasModifyTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.MODIFY);
    }

    public boolean hasPromoteTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.PROMOTE);
    }

    public boolean hasBankTeamRight(final Team aTeam) {
        return hasTeamRight(aTeam, UserTeamRight.BANK);
    }

    public boolean isInTeam(final Team team) {
        return isInTeamUnprotected(team);
    }

    protected boolean isInTeamUnprotected(final Team team) {
        return getDao().isInTeam(team.getDao());
    }

    @Override
    public <ReturnType> ReturnType accept(final ModelClassVisitor<ReturnType> visitor) {
        return visitor.visit(this);
    }

}