def get_pending_invitations(self):
        portal = getToolByName(self.context, 'portal_url').getPortalObject()
        mtool = getToolByName(self.context, 'portal_membership')
        storage = IInvitationStorage(portal)

        mtool = getToolByName(self.context, 'portal_membership')
        member = mtool.getAuthenticatedMember()

        invitations = []
        for invitation in storage.get_invitations_for_context(self.context):
            inviter = mtool.getMemberById(invitation.inviter)
            if inviter:
                inviter_name = inviter.getProperty('fullname',
                                                   invitation.inviter)
            else:
                inviter_name = invitation.inviter

            item = dict(name=invitation.email,
                        roles=get_friendly_role_names(
                            invitation.roles, self.context, self.request),
                        inherited_roles=[],
                        inviter=inviter_name,
                        readonly=not member.getId() == invitation.inviter,
                        type_='invitations',
                        iid=invitation.iid)
            invitations.append(item)

        return invitations
Example #2
0
    def get_invitations(self, email=None, user=None):
        assert email or user, '"email" or "user" argument required'
        assert not (email and user), 'only one orgument of "email" and "user" allowed'
        if user:
            email = user.getProperty('email')

        self.layer['request'].SESSION = {}
        storage = IInvitationStorage(self.layer['portal'])
        return storage.get_invitations_for_email(email)
Example #3
0
    def allowed_number_of_invitations(self):
        """Returns how many users can be invited on the context at the moment.
        Pending invitations are counted.

        """
        allowed = int(self.get_quota_limit())
        # subtract amount of local roles
        allowed -= len(self.context.get_local_roles())
        # subtract the amount of pending invitations
        storage = IInvitationStorage(self.context)
        allowed -= len(storage.get_invitations_for_context(self.context))
        return allowed
 def __init__(self, target, email, inviter, roles):
     """Creates a new `Invitation` on the `target` for `email`. The
     invitation was created by `inviter` (userid). Roles are the given
     additional roles for the user (email)
     """
     # get the storage
     storage = IInvitationStorage(target)
     # set the attributes
     self.email = email
     self.inviter = inviter
     self.set_target(target)
     self.roles = roles
     # generate the id, which sets it to self.iid
     storage.generate_iid_for(self)
     # register the invitation
     storage.add_invitation(self)
    def remove_invitations(self, iids):
        self.require_manage()

        storage = IInvitationStorage(self.context)

        mtool = getToolByName(self.context, 'portal_membership')
        member = mtool.getAuthenticatedMember()

        for iid in iids:
            invitation = storage.get_invitation_by_iid(iid)
            if invitation is None:
                continue

            if invitation.inviter != member.getId():
                raise Forbidden

            storage.remove_invitation(invitation)
            notify(InvitationRetractedEvent(invitation.get_target(),
                                            invitation))
Example #6
0
    def load(self, iid):
        """Loads storage, invitation, target, etc
        """
        self.storage = IInvitationStorage(self.context)
        self.invitation = self.storage.get_invitation_by_iid(iid)
        self.target = self.invitation.get_target()

        # sanity check
        mtool = getToolByName(self.context, 'portal_membership')
        self.member = mtool.getAuthenticatedMember()
        if not self.invitation \
                or self.invitation.inviter != self.member.getId():
            msg = _(u'error_invitation_not_found',
                    default=u'Could not find the invitation.')
            IStatusMessage(self.request).addStatusMessage(msg, type='error')
            raise Redirect(self.context.portal_url())
Example #7
0
    def load(self, iid):
        """Loads the storage, the invitation and the target and does some
        more things.

        """
        self.storage = IInvitationStorage(self.context)
        self.invitation = self.storage.get_invitation_by_iid(iid)
        self.target = self.invitation.get_target()

        # sanity check
        mtool = getToolByName(self.context, 'portal_membership')
        self.member = mtool.getAuthenticatedMember()
        valid_emails = (self.member.getId(),
                        self.member.getProperty('email', object()))
        if not self.invitation or self.invitation.email not in valid_emails:
            msg = _(u'error_invitation_not_found',
                    default=u'Could not find the invitation.')
            IStatusMessage(self.request).addStatusMessage(msg, type='error')
            raise Redirect(self.context.portal_url())
Example #8
0
class RetractInvitation(AcceptInvitation):
    """The inviter retracts the invitation.
    """

    def __call__(self, iid=None):
        if not iid:
            iid = self.request.get('iid')

        self.load(iid)
        notify(InvitationRetractedEvent(self.target, self.invitation))

        msg = _(u'info_invitation_retracted',
                default=u'You have retracted the invitation.')
        IStatusMessage(self.request).addStatusMessage(msg)

        # destroy the invitation
        self.storage.remove_invitation(self.invitation)
        del self.invitation

        # redirect back
        url = self.request.get('HTTP_REFERER',
                               os.path.join(self.context.portal_url(),
                                            '@@invitations'))
        return self.request.RESPONSE.redirect(url)

    def load(self, iid):
        """Loads storage, invitation, target, etc
        """
        self.storage = IInvitationStorage(self.context)
        self.invitation = self.storage.get_invitation_by_iid(iid)
        self.target = self.invitation.get_target()

        # sanity check
        mtool = getToolByName(self.context, 'portal_membership')
        self.member = mtool.getAuthenticatedMember()
        if not self.invitation \
                or self.invitation.inviter != self.member.getId():
            msg = _(u'error_invitation_not_found',
                    default=u'Could not find the invitation.')
            IStatusMessage(self.request).addStatusMessage(msg, type='error')
            raise Redirect(self.context.portal_url())
    def __call__(self):
        self.storage = IInvitationStorage(self.context)
        self.handle_pending_invitations()
        authenticator = self.context.restrictedTraverse('@@authenticator',
                                                        None)
        if not self.is_logged_in():
            # redirect to home when no
            pending_iids = self.storage.get_pending_session_invitations()
            if not len(pending_iids):
                # no pending sessions and not logged in -> go to home
                url = self.context.portal_url()
                return self.request.RESPONSE.redirect(url)

            else:
                # check if the email of at least one pending invitation is
                # already used by any user -> then the current user may be
                # this user, so we redirect to the login form
                passearch = self.context.unrestrictedTraverse('@@pas_search')
                for iid in pending_iids:
                    invitation = self.storage.get_invitation_by_iid(iid)
                    if not invitation:
                        # Maybe a deleted Invitation is still in the session.
                        continue
                    if len(passearch.searchUsers(email=invitation.email)) > 0:
                        url = os.path.join(
                            self.context.portal_url(),
                            'login_form?came_from=@@invitations')
                        return self.request.RESPONSE.redirect(url)
                # if we cannot find it we redirect to the invite_join_form
                url = os.path.join(self.context.portal_url(),
                                   'welcome_invited')
                # store one of the email addresses to the session. it can be
                # used later while registering as proposal
                if invitation:
                    self.request.SESSION['invite_email'] = invitation.email
                return self.request.RESPONSE.redirect(url)

        # not logged in -> show the view...
        else:
            # if we have pending session invitations, we should assign them to
            # the current user now.
            self.storage.assign_pending_session_invitations()
            if self.request.get('form.submitted', False):
                # sanity check with authenticator
                if not authenticator.verify():
                    raise Forbidden

                # handle accepted invitations
                if self.request.get('accept', False):
                    for iid in self.request.get('received_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@accept_invitation')(iid)

                # handle rejected invitations
                if self.request.get('reject', False):
                    for iid in self.request.get('received_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@reject_invitation')(iid)

                # hande reetracted invitations
                if self.request.get('retract', False):
                    for iid in self.request.get('sent_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@retract_invitation')(iid)

        return self.template()
class InvitationsView(BrowserView):
    """The invitations shows a listing of invitations the user got (where he
    was invited by another user) and a listing of invitations where he invited
    someone else.

    """

    template = ViewPageTemplateFile('invitations.pt')

    def __call__(self):
        self.storage = IInvitationStorage(self.context)
        self.handle_pending_invitations()
        authenticator = self.context.restrictedTraverse('@@authenticator',
                                                        None)
        if not self.is_logged_in():
            # redirect to home when no
            pending_iids = self.storage.get_pending_session_invitations()
            if not len(pending_iids):
                # no pending sessions and not logged in -> go to home
                url = self.context.portal_url()
                return self.request.RESPONSE.redirect(url)

            else:
                # check if the email of at least one pending invitation is
                # already used by any user -> then the current user may be
                # this user, so we redirect to the login form
                passearch = self.context.unrestrictedTraverse('@@pas_search')
                for iid in pending_iids:
                    invitation = self.storage.get_invitation_by_iid(iid)
                    if not invitation:
                        # Maybe a deleted Invitation is still in the session.
                        continue
                    if len(passearch.searchUsers(email=invitation.email)) > 0:
                        url = os.path.join(
                            self.context.portal_url(),
                            'login_form?came_from=@@invitations')
                        return self.request.RESPONSE.redirect(url)
                # if we cannot find it we redirect to the invite_join_form
                url = os.path.join(self.context.portal_url(),
                                   'welcome_invited')
                # store one of the email addresses to the session. it can be
                # used later while registering as proposal
                if invitation:
                    self.request.SESSION['invite_email'] = invitation.email
                return self.request.RESPONSE.redirect(url)

        # not logged in -> show the view...
        else:
            # if we have pending session invitations, we should assign them to
            # the current user now.
            self.storage.assign_pending_session_invitations()
            if self.request.get('form.submitted', False):
                # sanity check with authenticator
                if not authenticator.verify():
                    raise Forbidden

                # handle accepted invitations
                if self.request.get('accept', False):
                    for iid in self.request.get('received_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@accept_invitation')(iid)

                # handle rejected invitations
                if self.request.get('reject', False):
                    for iid in self.request.get('received_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@reject_invitation')(iid)

                # hande reetracted invitations
                if self.request.get('retract', False):
                    for iid in self.request.get('sent_invitations', []):
                        self.context.unrestrictedTraverse(
                            '@@retract_invitation')(iid)

        return self.template()

    def get_received_invitations(self):
        """Returns all invitations which the user has received and open.
        """
        return self.storage.get_invitations_for_email()

    def get_sent_invitations(self):
        """Returns all open invitations created by the current user.
        """
        return self.storage.get_invitations_invited_by()

    def handle_pending_invitations(self):
        """When the `iid` of a invitation is in the request
        (@@invitaitons?iid=bla) and the user is not authenticated, we mark this
        invitation in the session. If there is a `iid` but the user is
        authenticated we can reassign the invitation to this user.

        """
        iid = self.request.get('iid', False)
        if not iid:
            return
        invitation = self.storage.get_invitation_by_iid(iid)
        if not invitation:
            # the invitation is expired or answered
            msg = _(u'warning_invitaiton_expired',
                    default=u'The invitation has expired.')
            IStatusMessage(self.request).addStatusMessage(msg, type='warning')
            return

        if self.is_logged_in():
            # reassign to current user
            mtool = getToolByName(self.context, 'portal_membership')
            member = mtool.getAuthenticatedMember()
            new_email = member.getProperty('email', False) or member.getId()
            self.storage.reassign_invitation(invitation, new_email)
        else:
            # set pending for session
            self.storage.set_invitation_pending_for_session(invitation)

    def is_logged_in(self):
        """Returns wheather the user is logged in or not.
        """
        mtool = getToolByName(self.context, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        return member != SpecialUsers.nobody
Example #11
0
class AcceptInvitation(BrowserView):
    """Accept a invitation. This either called directly from the browser (with
    a url like .../@@accept_invitations?iid=XXX) or through the invitations
    view (with unrestrictedTravers('@@accept_invitations')(iid) ).

    """

    def __call__(self, iid=None, redirect=True):
        if not iid:
            iid = self.request.get('iid')

        self.load(iid)

        self.set_participation()
        notify(InvitationAcceptedEvent(self.target, self.invitation))
        self.send_email()

        # add status message
        msg = _(u'info_invitation_accepted',
                default=u'You have accepted the invitation.')
        IStatusMessage(self.request).addStatusMessage(msg, type='info')

        # destroy the invitation
        self.storage.remove_invitation(self.invitation)
        del self.invitation

        # redirect context (where the user now has access)
        if redirect:
            return self.request.RESPONSE.redirect(self.target.absolute_url())

    def load(self, iid):
        """Loads the storage, the invitation and the target and does some
        more things.

        """
        self.storage = IInvitationStorage(self.context)
        self.invitation = self.storage.get_invitation_by_iid(iid)
        self.target = self.invitation.get_target()

        # sanity check
        mtool = getToolByName(self.context, 'portal_membership')
        self.member = mtool.getAuthenticatedMember()
        valid_emails = (self.member.getId(),
                        self.member.getProperty('email', object()))
        if not self.invitation or self.invitation.email not in valid_emails:
            msg = _(u'error_invitation_not_found',
                    default=u'Could not find the invitation.')
            IStatusMessage(self.request).addStatusMessage(msg, type='error')
            raise Redirect(self.context.portal_url())

    def set_participation(self):
        """Sets up participation up.
        """
        # participate the user (using the adapter)
        setter = getMultiAdapter((self.target, self.request,
                                  self.invitation, self.member),
                                 IParticipationSetter)
        setter()

    def send_email(self):
        """Sends the notification email to the invitor.
        """
        properties = getUtility(IPropertiesTool)
        mtool = getToolByName(self.context, 'portal_membership')
        # prepare from address for header
        from_str = Header(properties.email_from_name, 'utf-8')
        from_str.append(u'<%s>' % properties.email_from_address.decode('utf-8'))

        # To
        to_member = mtool.getMemberById(self.invitation.inviter)
        if not to_member.getProperty('email'):
            msg = _(u'error_inviter_email_missing',
                    default=u'Invitor could not be notified: he has '
                    'no email address defined.')
            IStatusMessage(self.request).addStatusMessage(msg, type='error')
            return
        to_str = to_member.getProperty('fullname', to_member.getId())
        if isinstance(to_str, unicode):
            to_str = to_str.encode('utf8')
        header_to = Header(to_str, 'utf-8')
        header_to.append(u'<%s>' % to_member.getProperty(
                'email').decode('utf-8'))
        # Subject
        subject = self.get_subject()
        if isinstance(subject, unicode):
            subject = subject.encode('utf8')
        header_subject = Header(subject, 'utf8')

        # Options for templated
        options = {
            'invitation': self.invitation,
            'inviter': to_member,
            'inviter_name': to_member.getProperty('fullname',
                                                  to_member.getId()),
            'invited': self.member,
            'invited_name': self.member.getProperty('fullname',
                                                    self.member.getId()),
            'invited_email': self.member.getProperty('email',
                                                     self.member.getId()),
            'target': self.target,
            }

        # make the mail
        msg = MIMEMultipart('alternative')
        msg['Subject'] = header_subject
        msg['From'] = str(from_str)
        msg['To'] = str(header_to)
        # get the body views
        html_view = self.get_mail_body_html_view()
        text_view = self.get_mail_body_text_view()

        # render and emmbed body
        text_body = text_view(**options).encode('utf-8')
        msg.attach(MIMEText(text_body, 'plain', 'utf-8'))
        html_body = html_view(**options).encode('utf-8')
        msg.attach(MIMEText(html_body, 'html', 'utf-8'))

        # send the email
        mh = getToolByName(self.context, 'MailHost')
        mh.send(msg)

    def get_mail_body_html_view(self):
        return self.context.unrestrictedTraverse(
            '@@invitation_accepted_mail_html')

    def get_mail_body_text_view(self):
        return self.context.unrestrictedTraverse(
            '@@invitation_accepted_mail_text')

    def get_subject(self):
        """Returns the translated subject of the email.
        """
        member = getToolByName(self.context,
                               'portal_membership').getAuthenticatedMember()
        fullname = member.getProperty('fullname',
                                      member.getId()).decode('utf-8')
        context_title = self.context.pretty_title_or_id().decode('utf-8')
        # -- i18ndude hint --
        if 0:
            _(u'mail_invitation_accepted_subject',
              default=u'The Invitation to participate in ${title} '
              u'was accepted by ${user}',
              mapping=dict(title=context_title,
                           user=fullname))
        # / -- i18ndude hint --
        # do not use translation messages - translate directly
        return translate(u'mail_invitation_accepted_subject',
                         domain='ftw.participation',
                         context=self.request,
                         default=u'The Invitation to participate in ${title} '
                         u'was accepted by ${user}',
                         mapping=dict(title=context_title,
                                      user=fullname))