def validate(self, data): errors = () invitations_nr = (data.get('users') and len(data.get('users')) or 0) +\ (data.get('addresses') and len(data.get('addresses')) or 0) if invitations_nr == 0: # at least one invitation required registry = getUtility(IRegistry) config = registry.forInterface(IParticipationRegistry) if config.allow_invite_email and config.allow_invite_users: errors += (Invalid(_(u'Select at least one user or enter at ' 'least one e-mail address')),) elif config.allow_invite_email: errors += (Invalid(_(u'Enter at least one e-mail address.')),) elif config.allow_invite_users: errors += (Invalid(_(u'Select at least one user.')),) if IParticipationQuotaSupport.providedBy(self.context): # check maximum participants when quota enabled quota_support = IParticipationQuotaHelper(self.context) allowed = quota_support.allowed_number_of_invitations() if invitations_nr > allowed: if allowed <= 0: msg = _(u'error_participation_quota_reached', default=u'The participation quota is reached, ' 'you cannot add any further participants.') else: msg = _('error_too_many_participants', default=u'You cannot invite so many participants ' 'any more. Can only add ${num} more participants.', mapping={'num': allowed}) raise Invalid(msg) return errors
def prepare_comment(self, activity, obj): action = activity.attrs['action'] actor_info = activity.get_actor_info() actor_fullname = actor_info.get('fullname').decode('utf-8') if action == 'participation:invitation_created': actor_email = activity.attrs['invitation:email'].decode('utf-8') roles = translate_and_join_roles( activity.attrs['invitation:roles'], obj, self.request) return _(u'activity_invitation_created_body', default=u'${inviter} has invited ${mail} as ${roles}.', mapping={'inviter': actor_fullname, 'mail': actor_email, 'roles': roles}) if action == 'participation:invitation_retracted': actor_email = activity.attrs['invitation:email'].decode('utf-8') roles = translate_and_join_roles( activity.attrs['invitation:roles'], obj, self.request) return _(u'activity_invitation_retracted_body', default=u'${actor} has retracted the invitation' u' for ${mail}.', mapping={'actor': actor_fullname, 'mail': actor_email}) if action == 'participation:role_changed': subject_fullname = self.get_fullname_of( activity.attrs['roles:userid']) removed_roles = translate_and_join_roles( activity.attrs['roles:removed'], obj, self.request) added_roles = translate_and_join_roles( activity.attrs['roles:added'], obj, self.request) return _(u'activity_role_changed_body', default=u'${actor} has changed the role of ${subject}' u' from ${removed_roles} to ${added_roles}.', mapping={'actor': actor_fullname, 'subject': subject_fullname, 'removed_roles': removed_roles, 'added_roles': added_roles}) if action == 'participation:role_removed': subject_fullname = self.get_fullname_of( activity.attrs['roles:userid']) return _(u'activity_participant_removed_body', default=u'${actor} has removed the' u' participant ${subject}.', mapping={'actor': actor_fullname, 'subject': subject_fullname}) return None
def validate(self, roles): registry = getUtility(IRegistry) config = registry.forInterface(IParticipationRegistry) if not config.allow_multiple_roles and not roles: msg = _(u'error_no_roles', default=u'Please select a role') raise Invalid(msg)
def __call__(self, iid=None): """ """ if not iid: iid = self.request.get('iid') self.load(iid) notify(InvitationRejectedEvent(self.target, self.invitation)) self.send_email() # add status message msg = _(u'info_invitation_rejected', default=u'You have rejected the invitation.') IStatusMessage(self.request).addStatusMessage(msg, type='info') # 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 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 get_subject(self): """Returns the translated subject of the email. """ context_title = self.context.pretty_title_or_id().decode('utf-8') # -- i18ndude hint -- if 0: _(u'mail_invitation_subject', default=u'Invitation for paticipating in ${title}', mapping=dict(title=context_title)) # / -- i18ndude hint -- # do not use translation messages - translate directly return translate(u'mail_invitation_subject', domain='ftw.participation', context=self.request, default=u'Invitation for paticipating in ${title}', mapping=dict(title=context_title))
def _validate_addresses(self, addresses): """E-Mail address validation """ for addr in addresses: addr = addr.strip() if not self.email_expression.match(addr): msg = _(u'error_invalid_addresses', default=u'At least one of the defined addresses ' 'are not valid.') raise Invalid(msg)
def save_local_roles(self, data): # Remove all managed roles user_roles = get_user_local_roles(data['memberid'], self.context) managed_roles = extract_roles(self.context) filtered = filter(lambda role: role not in managed_roles, user_roles) # Append new roles filtered.extend(data['roles']) self.context.manage_setLocalRoles(data['memberid'], filtered) self.context.reindexObjectSecurity() msg = _(u'message_roles_changes', default=u'Changed roles') IStatusMessage(self.request).addStatusMessage(msg, type='info')
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('utf8') context_title = self.context.pretty_title_or_id().decode('utf-8') # -- i18ndude hint -- if 0: _(u'mail_invitation_rejected_subject', default=u'The Invitation to participate in ${title} ' u'was rejected by ${user}', mapping=dict(title=context_title, user=fullname)) # / -- i18ndude hint -- # do not use translation messages - translate directly return translate(u'mail_invitation_rejected_subject', domain='ftw.participation', context=self.request, default=u'The Invitation to participate in ${title} ' u'was rejected by ${user}', mapping=dict(title=context_title, user=fullname))
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 handle_invite(self, action): """Invite the users: create Invitations and send email """ registry = getUtility(IRegistry) config = registry.forInterface(IParticipationRegistry) data, errors = self.extractData() if len(errors) == 0: # get inviter mtool = getToolByName(self.context, 'portal_membership') inviter = mtool.getAuthenticatedMember() # get all addresses addresses = [] if config.allow_invite_email and \ data.get('addresses') and len(data.get('addresses')): for addr in data.get('addresses').split('\n'): addresses.append(addr.strip()) if config.allow_invite_users and data.get('users'): for username in data['users']: member = api.user.get(username=username) addresses.append(member.getProperty('email')) # get roles roles = data.get('roles', []) # handle every email seperate emails = [] # used for info msg for email in addresses: email = email.strip() if not email: continue inv = Invitation(self.context, email, inviter.getId(), roles) self.send_invitation(inv, email, inviter, data.get('comment')) notify(InvitationCreatedEvent(self.context, inv, data.get('comment'))) emails.append(email) # notify user emails.sort() msg = _(u'info_invitations_sent', default=u'The invitation mails were sent to ${emails}.', mapping={'emails': ', '.join(emails), }) IStatusMessage(self.request).addStatusMessage(msg, type='info') return self.redirect()
def __call__(self): form = self.request.form del_userids = form.get('userids', []) del_invitations = form.get('invitations', []) if form.get('form.delete') and (del_userids or del_invitations): self.remove_users(del_userids) self.remove_invitations(del_invitations) IStatusMessage(self.request).addStatusMessage(_(u"Changes saved."), type='info') elif form.get('form.cancel'): return self.request.RESPONSE.redirect(self.cancel_url()) return self.template()
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 __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 translate_and_join_roles(roles, context, request): roles = get_friendly_role_names(roles, context, request) and_ = translate(_(' and '), context=request) return ', '.join(roles[:-2] + [and_.join(roles[-2:])])
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)