public List<Presence> addParticipant(@Nonnull final JID targetUserAddress, @Nullable final String reason,
                                         @Nonnull final Affiliation actorAffiliation, @Nullable final Role actorRole) throws ForbiddenException, NotAllowedException
    {
        // Moderator grants voice, or Admin or owner changes role to participant or revokes moderator status
        if (Affiliation.admin != actorAffiliation && Affiliation.owner != actorAffiliation && Role.moderator != actorRole) {
            throw new ForbiddenException();
        }

        // A moderator SHOULD NOT be allowed to revoke moderation privileges from someone with a higher affiliation than
        // themselves (i.e., an unaffiliated moderator SHOULD NOT be allowed to revoke moderation privileges from an
        // admin or an owner, and an admin SHOULD NOT be allowed to revoke moderation privileges from an owner).
        final Affiliation targetAffiliation = getAffiliation(targetUserAddress);
        if (targetAffiliation.getValue() < actorAffiliation.getValue()) { // note that Openfire's values are ordered the other way around.
            throw new NotAllowedException();
        }

        // An admin might want to revoke a user's moderator status. An admin MAY revoke moderator status only from a
        // user whose affiliation is "member" or "none" (i.e., not from an owner or admin).
        if (actorAffiliation == Affiliation.admin && (targetAffiliation == Affiliation.owner || targetAffiliation == Affiliation.admin)) {
            throw new NotAllowedException();
        }

        // Update the presence with the new role and inform all occupants
        final List<Presence> updatedPresences = applyRoleChange(targetUserAddress, Role.participant);
        for (final Presence updatedPresence : updatedPresences)
        {
            final Element frag = updatedPresence.getChildElement("x", "http://jabber.org/protocol/muc#user");
            // Add the reason why the user was granted voice
            if (reason != null && !reason.trim().isEmpty()) {
                frag.element("item").addElement("reason").setText(reason);
            }
        }
        return updatedPresences;
    }
Changes the role of the user within the room to participant. A participant is allowed to send
messages to the room (i.e. has voice) and may change the room's subject.

@param targetUserAddress the (real) JID of the occupant to give participant privileges.
@param reason The reason why participant privileges were given to the user.
@param actorAffiliation The room affiliation of the user that is granting participant privileges to an occupant.
@param actorRole The room role of the user that is granting participant privileges to an occupant (can be null when the user is currently not an occupant).
@return the list of updated presences of all the client resources that the occupant has used to join the room.
@throws ForbiddenException If the user is not allowed to grant participant privileges.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoom.java