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
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)
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))
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 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())
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
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))