def handle_migrate_action(self, action, data):

        # permission checking needed since setting the permission
        # in zcml doesn't seem to work
        mtool = getToolByName(self.context, 'portal_membership')
        if not mtool.checkPermission('Manage Portal', self.context):
            return _(u'permission denied')

        ll = getUtility(IListLookup)
        mappings = ll.showAddressMapping()
        self.nlists = len(mappings)
        self.results = []
        for mapping in mappings:
            path = mapping['path']
            try:
                ml = self.context.unrestrictedTraverse(path)
                migrator = IMigrateList(ml)
                return_msg = migrator.migrate()
                absolute_url = ml.absolute_url()
            except AttributeError:
                return_msg = _(u'Error: List not found')
                absolute_url = ''

            self.results.append({
                'url': absolute_url,
                'title': path,
                'msg': return_msg
            })
        return self.result_template.render()
Example #2
0
    def chooseName(self, name, object):
        if not name:
            title = getattr(object, 'title', '')
            name = self.getIdFromTitle(title)
            if not name:
                name = object.__class__.__name__
        try:
            name = name.encode('ascii')
        except UnicodeDecodeError:
            raise UserError, _("Id must contain only ASCII characters.")

        dot = name.rfind('.')
        if dot >= 0:
            suffix = name[dot:]
            name = name[:dot]
        else:
            suffix = ''

        n = name + suffix
        i = 0
        while True:
            i += 1
            try:
                self.context._getOb(n)
            except AttributeError:
                break
            n = name + '-' + str(i) + suffix

        # Make sure the name is valid.  We may have started with
        # something bad.
        self.checkName(n, object)

        return n
Example #3
0
    def checkName(self, name, object):
        # ObjectManager can only deal with ASCII names. Specially
        # ObjectManager._checkId can only deal with strings.
        try:
            name = name.encode('ascii')
        except UnicodeDecodeError:
            raise UserError, _("Id must contain only ASCII characters.")

        context = self.context
        # XXX: Try Plone check_id script this should become a view/adapter
        try:
            check_id = getattr(object.__of__(context), 'check_id', None)
        except AttributeError:
            # We may not have acquisition available
            check_id = None
        if check_id is not None:
            invalid = check_id(name, required=1)
            if invalid:
                raise UserError, invalid
        # Otherwise fallback on _checkId
        else:
            try:
                self.context._checkId(name, allow_dup=False)
            except BadRequest, e:
                msg = ' '.join(e.args) or _("Id is in use or invalid")
                raise UserError, msg
Example #4
0
    def pending_status(self, user):
        annot = IAnnotations(self.context)
        listen_annot = annot.setdefault(PROJECTNAME, OOBTree())

        subscribe_pending_list = getAdapter(self.context,
                                            IMembershipPendingList,
                                            'pending_sub_email')
        unsubscribe_pending_list = getAdapter(self.context,
                                              IMembershipPendingList,
                                              'pending_unsub_email')
        sub_mod_pending_list = getAdapter(self.context, IMembershipPendingList,
                                          'pending_sub_mod_email')

        email_address = is_email(user) and user or lookup_email(
            user, self.context)

        inlist = lambda lst: lst.is_pending(email_address)
        status = lambda msg, lst: msg + lst.get_pending_time(email_address)

        status_msg = ''
        if inlist(subscribe_pending_list):
            status_msg += status(
                _(u'subscription pending user confirmation: '),
                subscribe_pending_list)
        if inlist(unsubscribe_pending_list):
            status_msg += status(
                _(u'unsubscription pending user confirmation: '),
                unsubscribe_pending_list)
        if inlist(sub_mod_pending_list):
            status_msg += status(
                _(u'subscription pending manager moderation: '),
                sub_mod_pending_list)

        return status_msg
Example #5
0
class MailingListEditForm(EditForm):
    """A form for editing MailingList objects.
    """
    form_fields = listen_form_fields

    label = _(u"Edit Mailing List")

    def _assign_local_roles_to_managers(self):
        ml = self.context
        assign_local_role('Owner', ml.managers, IRoleManager(ml))

    @form.action(_('label_save', u'Save'), condition=form.haveInputWidgets)
    def handle_save_action(self, action, data):
        old_list_type = self.context.list_type.list_marker
        new_list_type = data.get('list_type').list_marker

        if form.applyChanges(self.context, self.form_fields, data,
                             self.adapters):
            # ensure correct role is set for users
            self._assign_local_roles_to_managers()
            notify(zope.app.event.objectevent.ObjectModifiedEvent(self.context))
            notify(ListTypeChanged(self.context, old_list_type, new_list_type))
            self.status = _(u"Your changes have been saved.")
        else:
            self.status = _(u"No changes need to be saved.")
        return ""

    @form.action(_('label_cancel', u'Cancel'), validator=lambda *a: ())
    def handle_cancel_action(self, action, data):
        self.status = _(u"Edit cancelled.")
        return ""
 def _searchArchive(self, text=None):
     messages = []
     batch = self.request.get('batch', True)
     batch_size = int(self.request.get('b_size', 25))
     batch_start = int(self.request.get('b_start', 0))
     text = text or decode(self.request.get('search_text'), '')
     context = self.context
     subscription = IMembershipList(self.getMailingList())
     if text:
         text = text.strip()
         try:
             messages = self.search.search(text)
         except ParseTree.ParseError, inst:
             if "Token 'ATOM' required, 'EOF' found" in str(inst) or "Token 'EOF' required" in str(inst):
                 self.request.set('portal_status_message', _(u'Invalid Search: Search phrase cannot end with \'and\', \'or\', or \'not\''))
             elif "Token 'ATOM' required" in str(inst):
                 self.request.set('portal_status_message', _(u'Invalid Search: Search phrase cannot begin with \'and\', \'or\', or \'not\''))
             elif "a term must have at least one positive word" in str(inst):
                 self.request.set('portal_status_message', _(u'Invalid Search: Search phrase cannot begin with \'not\''))
             elif "Query contains only common words" in str(inst):
                 self.request.set('portal_status_message', _(u'Invalid Search: Search phrase must contain words other than \'and\', \'or\', and \'not\''))
         else:
             messages = catalogMessageIterator(messages, sub_mgr=subscription)
             if len(messages) == 0:
                 self.request.set('portal_status_message', _(u'There were no messages found'))
Example #7
0
    def manage_afterAdd(self, item, container, **kw):
        """Setup properties and sub-objects"""
        # Only run on add, not rename, etc.
        if not base_hasattr(self, 'mqueue'):
            setMailBoxerProperties(self, self.REQUEST, kw)
            # Setup the default checkMail validator chain
            setDefaultValidatorChain(self)

            # Add Archive
            archive = zapi.createObject('listen.ArchiveFactory', self.storage,
                                        title=_(u'List Archive'))
            item._setObject(self.storage, archive)

            # Add moderation queue
            mqueue = zapi.createObject('listen.QueueFactory', self.mailqueue,
                                       title=_(u'Moderation queue'))
            item._setObject(self.mailqueue, mqueue)

            ttool = getToolByName(self, 'portal_types', None)
            if ttool is not None:
                # If the archive/queue are CMF types then we must finish
                # constructing them.
                fti = ttool.getTypeInfo(mqueue)
                if fti is not None:
                    fti._finishConstruction(mqueue)
                fti = ttool.getTypeInfo(archive)
                if fti is not None:
                    fti._finishConstruction(archive)
        MailBoxer.manage_afterAdd(self, self.REQUEST, kw)
Example #8
0
    def __call__(self):
        if not self.request.get('save', None): return self.index()
        d = self.request.form
        self.errors = ''
        to_remove = []
        subscribed_list = set()
        wassubscribed_list = set()
        for name, value in d.items():
            if name.lower() == 'save' and value.lower() == 'save changes':
                continue
            valuetype, name = name.split('_', 1)
            if valuetype == 'remove':
                to_remove.append(name.decode('utf-8'))
            elif valuetype == 'subscribed':
                subscribed_list.add(name.decode('utf-8'))
            elif valuetype == 'wassubscribed':
                wassubscribed_list.add(name.decode('utf-8'))

        to_subscribe = subscribed_list - wassubscribed_list
        to_unsubscribe = wassubscribed_list - subscribed_list

        self._remove(to_remove)
        self._subscribe(to_subscribe)
        self._unsubscribe(to_unsubscribe)

        psm = ""
        to_add = d.get('add_email', None).strip()
        if to_add:
            subscribed = d.get('add_subscribed', None)
            if self._add(to_add, subscribed):
                psm += _(u'add_subscribed_portal_msg',
                         u'Added: ${to_add}.  ',
                         mapping={'to_add': to_add})
            else:
                psm += _(u'bad_user_email_portal_msg',
                         u'Bad user or email address: ${to_add}.  ',
                         mapping={'to_add': to_add})

        if to_remove:
            psm += _(u'removed_portal_msg',
                     u'Removed: ${to_remove}.  ',
                     mapping={'to_remove': to_remove})
        if to_subscribe:
            psm += _(u'subscribed_portal_msg',
                     u'Subscribed: ${to_subscribe}.  ',
                     mapping={'to_subscribe': to_subscribe})
        if to_unsubscribe:
            psm += _(u'unsubscribed_portal_msg',
                     u'Unsubscribed: ${to_unsubscribe}.  ',
                     mapping={'to_unsubscribe': to_unsubscribe})

        if psm:
            context = aq_inner(self.context)
            plone_utils = getToolByName(context, 'plone_utils')
            plone_utils.addPortalMessage(psm)

        # since this means that we've been posted to
        # we should redirect
        self.request.response.redirect(self.nextURL())
Example #9
0
    def __call__(self):
        # Make sure that the current user is allowed to reply:
        if not self.canReply():
            raise Unauthorized, \
                _(u"You do not have permission to respond to this message.")
        # Save the referring URL, either from the template form, or the
        # HTTP_REFERER, otherwise just use the message url.
        referring_url = (self.request.get('referring_url', None)
                         or self.request.get('HTTP_REFERER', None)
                         or self.context.absolute_url())
        self.referring_url = urllib.splitquery(referring_url)[0]
        submitted = self.request.get('submit', None)
        cancelled = self.request.get('cancel', None)
        if cancelled:
            portal_status_msg = _(u"Reply Cancelled")
            return self.request.response.redirect(self.referring_url +
                                                  '?portal_status_message=%s' %
                                                  (portal_status_msg))
        if submitted:
            self.errors = {}
            ml = self.getMailingList()
            body = decode(self.request.get('body', None), ml)
            subject = decode(self.request.get('subject', None), ml)
            if not body or body == self.reply_body(use_empty=True):
                self.errors['body'] = _(u'You must enter a message')
            if not subject:
                self.errors['subject'] = _(
                    u'You must enter a subject for your message.')
            if not self.member_address():
                self.errors['subject'] = _(u'The current user has no address.')
            if not self.errors:
                message = self.createReply()
                self.request.set('Mail', message)
                result = ml.processMail(self.request)
                if result == POST_ALLOWED:
                    portal_status_msg = _(u"Post Sent")
                    return self.request.response.redirect(
                        self.referring_url + '?portal_status_message=%s' %
                        (portal_status_msg))
                elif result == POST_DEFERRED:
                    portal_status_msg = _(u"Post Pending Moderation")
                    return self.request.response.redirect(
                        self.referring_url + '?portal_status_message=%s' %
                        (portal_status_msg))
                elif result == POST_DENIED:
                    portal_status_msg = _(
                        u"Post Rejected: You already have a post pending moderation."
                    )
                    return self.request.response.redirect(
                        self.referring_url + '?portal_status_message=%s' %
                        (portal_status_msg))
                else:
                    portal_status_msg = _(u"Post Error")
                    return self.request.response.redirect(
                        self.referring_url + '?portal_status_message=%s' %
                        (portal_status_msg))

        return self.index()
    def __call__(self):
        sub_action = self.request.get('subscribe_member', None)
        unsub_action = self.request.get('unsubscribe_member', None)
        email_action = self.request.get('subscribe_email', None)
        self.request.set('enable_border', True)
        self.errors = errors = {}

        logged_in_mem = self._get_logged_in_user()
        self.user_logged_in = False
        if logged_in_mem:
            self.user_email = lookup_email(logged_in_mem.getId(), self.context)
            self.user_logged_in = True
        else:
            #XXX what should this be?
            self.user_email = ''

        self.mem_list = IWriteMembershipList(self.context)

        # the appropriate sub_policy needs to be instantiated
        # depending on list type
        self.sub_policy = getAdapter(self.context, IUserTTWMembershipPolicy)

        if sub_action:
            self.subscribe()
        elif unsub_action:
            self.unsubscribe()
        elif email_action:
            address = self.request.get('email_address', None)
            if not address:
                errors['email_address'] = _('An email address is required')
            elif EMAIL_RE.match(address) is None:
                errors['email_address'] = _('This email address is invalid')
            elif self.mem_list.is_subscribed(address):
                errors['email_address'] = \
                                 _('This email address is already subscribed')
            else:
                # everything is OK, send a request mail the
                # appropriate sub_policy needs to be instantiated
                # depending on list type
                sub_policy_for_email = getAdapter(self.context, IUserEmailMembershipPolicy)

                ret = sub_policy_for_email.enforce({'email':address,
                                                    'subject':'subscribe'})
                if ret == MEMBERSHIP_ALLOWED:
                    # make user a subscriber
                    self.mem_list.subscribe(address)
                    self.request.set('portal_status_message', 'Email subscribed')
                elif ret == MEMBERSHIP_DEFERRED:
                    self.request.set('portal_status_message',
                                     'Subscription request sent')
                else:
                    self.request.set('portal_status_message', 'Bad email address')
                    
                # Blank the email field to avoid the postback
                self.request.set('email_address', '')
                self.request.set('subscribe_email', '')

        return self.index()
Example #11
0
class IListTypeDefinition(Interface):
    """used for indicating the type of mailing list"""
    
    title = Attribute("displayable title of list type")
    description = Attribute("displayable description of list type")

    list_marker = InterfaceField(title=_(u'list marker'),
                                 description=_(u'marked interface used to indicate the list type'),
                                 constraint=IListType.providedBy)

    index = Int(title=_(u'sort index'))
 def __call__(self):
     ll = getUtility(IListLookup, context=self.context)
     try:
         return ll.deliverMessage(self.request)
     except ListDoesNotExist:
         raise NotFound, _(
             "The message address does not correspond to a known mailing list"
         )
     except:
         message = str(self.request.get(MAIL_PARAMETER_NAME, None))
         logger.error("Listen delivery failed for message: \n%s" % message)
         raise
Example #13
0
    def addMail(self, mailString, force_id=False):
        """ Store mail in date based folder archive.
            Returns created mail.  See IMailingList interface.
        """
        archive = aq_get(self, self.getValueFor('storage'), None)

        # no archive available? then return immediately
        if archive is None:
            return None

        (header, body) = splitMail(mailString)

        # if 'keepdate' is set, get date from mail,
        if self.getValueFor('keepdate'):
            assert header.get("date") is not None
            time = DateTime(header.get("date"))
        # ... take our own date, clients are always lying!
        else:
            time = DateTime()

        # now let's create the date-path (yyyy/mm)
        year  = str(time.year()) # yyyy
        month = str(time.mm())   # mm
        title = "%s %s"%(time.Month(), year)

        # do we have a year folder already?
        if not base_hasattr(archive, year):
            self.addMailBoxerFolder(archive, year, year, btree=False)
        yearFolder=getattr(archive, year)

        # do we have a month folder already?
        if not base_hasattr(yearFolder, month):
            self.addMailBoxerFolder(yearFolder, month, title)
        mailFolder=getattr(yearFolder, month)

        subject = header.get('subject', _('No Subject'))
        sender = header.get('from',_('Unknown'))

        # search a free id for the mailobject
        id = time.millis()
        while base_hasattr(mailFolder, str(id)):
            if force_id:
                raise AssertionError("ID %s already exists on folder %s" % (id, mailFolder))
            id = id + 1
        id = str(id)

        self.addMailBoxerMail(mailFolder, id, sender, subject, time,
                              mailString)
        mailObject = getattr(mailFolder, id)

        return mailObject
Example #14
0
    def handle_save_action(self, action, data):
        old_list_type = self.context.list_type.list_marker
        new_list_type = data.get('list_type').list_marker

        if form.applyChanges(self.context, self.form_fields, data,
                             self.adapters):
            # ensure correct role is set for users
            self._assign_local_roles_to_managers()
            notify(zope.app.event.objectevent.ObjectModifiedEvent(self.context))
            notify(ListTypeChanged(self.context, old_list_type, new_list_type))
            self.status = _(u"Your changes have been saved.")
        else:
            self.status = _(u"No changes need to be saved.")
        return ""
    def handle_save_action(self, action, data):
        old_list_type = self.context.list_type.list_marker
        new_list_type = data.get('list_type').list_marker

        if form.applyChanges(self.context, self.form_fields, data,
                             self.adapters):
            # ensure correct role is set for users
            self._assign_local_roles_to_managers()
            notify(zope.app.event.objectevent.ObjectModifiedEvent(self.context))
            notify(ListTypeChanged(self.context, old_list_type, new_list_type))
            self.status = _(u"Your changes have been saved.")
        else:
            self.status = _(u"No changes need to be saved.")
        return ""
    def __call__(self):
        if not self.request.get('save', None): return self.index()
        d = self.request.form
        self.errors = ''
        to_remove = []
        subscribed_list = set()
        wassubscribed_list = set()
        for name, value in d.items():
            if name.lower() == 'save' and value.lower() == 'save changes': continue
            valuetype, name = name.split('_', 1)
            if valuetype == 'remove':
                to_remove.append(name.decode('utf-8'))
            elif valuetype == 'subscribed':
                subscribed_list.add(name.decode('utf-8'))
            elif valuetype == 'wassubscribed':
                wassubscribed_list.add(name.decode('utf-8'))
        
        to_subscribe = subscribed_list - wassubscribed_list
        to_unsubscribe = wassubscribed_list - subscribed_list

        self._remove(to_remove)
        self._subscribe(to_subscribe)
        self._unsubscribe(to_unsubscribe)

        psm = ""
        to_add = d.get('add_email', None).strip()
        if to_add:
            subscribed = d.get('add_subscribed', None)
            if self._add(to_add, subscribed):
                psm += 'Added: %s.  ' % to_add
            else:
                psm += 'Bad user or email address: %s.  ' % to_add
            
        if to_remove:
            psm += _(u'Removed: %s.  ') % ', '.join(to_remove)
        if to_subscribe:
            psm += _(u'Subscribed: %s.  ') % ', '.join(to_subscribe)
        if to_unsubscribe:
            psm += 'Unsubscribed: %s.  ' % ', '.join(to_unsubscribe)


        if psm:
            context = aq_inner(self.context)
            plone_utils = getToolByName(context, 'plone_utils')
            plone_utils.addPortalMessage(psm)

        # since this means that we've been posted to
        # we should redirect
        self.request.response.redirect(self.nextURL())
    def __call__(self):
        # Make sure that the current user is allowed to reply:
        if not self.canReply():
            raise Unauthorized, \
                "You do not have permission to respond to this message."
        # Save the referring URL, either from the template form, or the
        # HTTP_REFERER, otherwise just use the message url.
        referring_url = (self.request.get('referring_url', None) or
                              self.request.get('HTTP_REFERER', None) or
                              self.context.absolute_url())
        self.referring_url = urllib.splitquery(referring_url)[0]
        submitted = self.request.get('submit', None)
        cancelled = self.request.get('cancel', None)
        if cancelled:
            return self.request.response.redirect(self.referring_url+
                                    '?portal_status_message=Reply%20Cancelled')
        if submitted:
            self.errors = {}
            ml = self.getMailingList()
            body = decode(self.request.get('body', None), ml)
            subject = decode(self.request.get('subject', None), ml)
            if not body or body == self.reply_body(use_empty=True):
                self.errors['body'] = _(u'You must enter a message')
            if not subject:
                self.errors['subject'] = _(
                    u'You must enter a subject for your message.')
            if not self.member_address():
                self.errors['subject'] = _(u'The current user has no address.')
            if not self.errors:
                message = self.createReply()
                self.request.set('Mail', message)
                result = ml.processMail(self.request)
                if result == POST_ALLOWED:
                    return self.request.response.redirect(self.referring_url+
                                    '?portal_status_message=Post%20Sent')
                elif result == POST_DEFERRED:
                    return self.request.response.redirect(self.referring_url+
                                    '?portal_status_message=Post%20Pending%20Moderation')
                elif result == POST_DENIED:
                    return self.request.response.redirect(self.referring_url+
                                    '?portal_status_message=Post%20Rejected:%20You%20already%20have%20a%20post%20pending%20moderation.')
                else:
                    return self.request.response.redirect(self.referring_url+
                                    '?portal_status_message=Post%20Error')
                    


        return self.index()
Example #18
0
    def createAndAdd(self, data):
        # use aq_inner, or else the obj will be wrapped in this view,
        # will screw up acquired security settings (esp. local roles)
        context = aq_inner(self.context)
        plone_utils = getToolByName(context, 'plone_utils')
        list_id = plone_utils.normalizeString(data['title'])

        context.invokeFactory(self.portal_type, list_id)
        list_ob = context._getOb(list_id)

        old_list_type = list_ob.list_type.list_marker
        new_list_type = data.get('list_type').list_marker

        form.applyChanges(list_ob, self.form_fields, data)

        # ensure correct role is set for users
        self._assign_local_roles_to_managers(list_ob)
        
        # XXX this ObjectCreatedEvent event would normally come before
        # the ObjectAddedEvent
        notify(zope.app.event.objectevent.ObjectCreatedEvent(list_ob))
        notify(ListTypeChanged(list_ob, old_list_type, new_list_type))
        self._finished_add = True

        status = IStatusMessage(self.request)
        status.addStatusMessage(_('Mailing list added.'), type=u'info')

        self._next_url = list_ob.absolute_url()

        return list_ob
Example #19
0
 def subscribe(self):
     req = {'action':'subscribe', 'email':self.user_email}
     if self.user_logged_in:
         req['use_logged_in_user'] = True
     ret = self.sub_policy.enforce(req)
                                    
     if ret == MEMBERSHIP_ALLOWED:
         self.mem_list.subscribe(self.user_email)
         self.request.set('portal_status_message',
                          _(u'list_have_been_subscribed_status_msg',
                            u'You have been subscribed'))
         pass
     elif ret == MEMBERSHIP_DEFERRED:
         self.request.set('portal_status_message',
                          _(u'list_subscription_pending_moderation_status_msg',
                            u"Your subscription request is pending moderation by the list manager."))
    def createAndAdd(self, data):
        # use aq_inner, or else the obj will be wrapped in this view,
        # will screw up acquired security settings (esp. local roles)
        context = aq_inner(self.context)
        plone_utils = getToolByName(context, 'plone_utils')
        list_id = plone_utils.normalizeString(data['title'])

        context.invokeFactory(self.portal_type, list_id)
        list_ob = context._getOb(list_id)

        old_list_type = list_ob.list_type.list_marker
        new_list_type = data.get('list_type').list_marker

        form.applyChanges(list_ob, self.form_fields, data)

        # ensure correct role is set for users
        self._assign_local_roles_to_managers(list_ob)
        
        # XXX this ObjectCreatedEvent event would normally come before
        # the ObjectAddedEvent
        notify(zope.app.event.objectevent.ObjectCreatedEvent(list_ob))
        notify(ListTypeChanged(list_ob, old_list_type, new_list_type))
        self._finished_add = True

        status = IStatusMessage(self.request)
        status.addStatusMessage(_('Mailing list added.'), type=u'info')

        self._next_url = list_ob.absolute_url()

        return list_ob
 def user_mem_mod_already_pending(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership approval already pending")
     body = default_email_text.user_mem_mod_already_pending
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
 def user_unsubscribe_confirm(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Unsubscription confirmed")
     body = default_email_text.user_unsubscribe_confirm
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
 def user_denied(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership denied")
     body = default_email_text.user_denied
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
 def manager_mod(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership awaiting approval")
     body = default_email_text.manager_mod
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'mod_url': self.context.absolute_url() + '/moderation',}
     body = Message(body, mapping=mapping)
     self.send_to_managers(translate(subject), translate(body))
 def Title(self):
     title = _('label_archive_list_archive_view',
               u'${list} archive for ${date}')
     title = Message(title,
                     mapping={
                         u'list': self.listTitle(),
                         u'date': self.context.title,
                     })
     return title
 def user_post_mod_notification(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Message pending moderation")
     body = default_email_text.user_post_mod_notification
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
 def user_welcome(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("welcome_to_list", "Welcome to ${title}", mapping={'title' : self.context.title})
     body = default_email_text.user_welcome
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'listaddress': self.context.mailto,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
 def user_sub_rejected(self, user_email, user_name, reject_reason):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Subscription request rejected")
     body = default_email_text.user_sub_rejected
     mapping = { 'fullname': user_name,
                 'listname': self.context.title,
                 'reject_reason': reject_reason,
                 'listmanager': self.context.manager_email, }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject), translate(body))
class MigrationView(Form):
    """view to migrate mailing lists"""

    form_fields = Fields()
    result_template = ZopeTwoPageTemplateFile('browser/migration.pt')

    @action(_('label_migrate', _(u'Migrate')))
    def handle_migrate_action(self, action, data):

        # permission checking needed since setting the permission
        # in zcml doesn't seem to work
        mtool = getToolByName(self.context, 'portal_membership')
        if not mtool.checkPermission('Manage Portal', self.context):
            return _(u'permission denied')

        ll = getUtility(IListLookup)
        mappings = ll.showAddressMapping()
        self.nlists = len(mappings)
        self.results = []
        for mapping in mappings:
            path = mapping['path']
            try:
                ml = self.context.unrestrictedTraverse(path)
                migrator = IMigrateList(ml)
                return_msg = migrator.migrate()
                absolute_url = ml.absolute_url()
            except AttributeError:
                return_msg = _(u'Error: List not found')
                absolute_url = ''

            self.results.append({
                'url': absolute_url,
                'title': path,
                'msg': return_msg
            })
        return self.result_template.render()

    def results(self):
        return self.results

    def num_lists(self):
        return self.nlists
Example #30
0
 def manager_mod(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership awaiting approval")
     body = default_email_text.manager_mod
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'mod_url': self.context.absolute_url() + '/moderation',
     }
     body = Message(body, mapping=mapping)
     self.send_to_managers(translate(subject), translate(body))
 def registerList(self, ml):
     """See IListLookup interface documentation"""
     address = ml.mailto
     # normalize case
     if not address:
         # Our list does not have an address yet, this only happens when
         # the add form wasn't used.
         return
     address = address.lower()
     path = '/'.join(ml.getPhysicalPath())
     current_addr = self._reverse.get(path, None)
     current_path = self._mapping.get(address, None)
     if current_addr is not None:
         raise UserError, _("This list is already registered, use "\
                          "updateList to change the address.")
     if current_path is not None:
         raise UserError, _("A list is already registered for this address,"\
                          " you must unregister it first.")
     self._mapping[address] = path
     self._reverse[path] = address
 def _searchArchive(self, text=None):
     messages = []
     batch = self.request.get('batch', True)
     batch_size = int(self.request.get('b_size', 25))
     batch_start = int(self.request.get('b_start', 0))
     text = text or decode(self.request.get('search_text'), '')
     context = self.context
     subscription = IMembershipList(self.getMailingList())
     if text:
         text = text.strip()
         try:
             messages = self.search.search(text)
         except ParseTree.ParseError, inst:
             if "Token 'ATOM' required, 'EOF' found" in str(
                     inst) or "Token 'EOF' required" in str(inst):
                 self.request.set(
                     'portal_status_message',
                     _(u'Invalid Search: Search phrase cannot end with \'and\', \'or\', or \'not\''
                       ))
             elif "Token 'ATOM' required" in str(inst):
                 self.request.set(
                     'portal_status_message',
                     _(u'Invalid Search: Search phrase cannot begin with \'and\', \'or\', or \'not\''
                       ))
             elif "a term must have at least one positive word" in str(
                     inst):
                 self.request.set(
                     'portal_status_message',
                     _(u'Invalid Search: Search phrase cannot begin with \'not\''
                       ))
             elif "Query contains only common words" in str(inst):
                 self.request.set(
                     'portal_status_message',
                     _(u'Invalid Search: Search phrase must contain words other than \'and\', \'or\', and \'not\''
                       ))
         else:
             messages = catalogMessageIterator(messages,
                                               sub_mgr=subscription)
             if len(messages) == 0:
                 self.request.set('portal_status_message',
                                  _(u'There were no messages found'))
Example #33
0
 def user_unsubscribe_confirm(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Unsubscription confirmed")
     body = default_email_text.user_unsubscribe_confirm
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
Example #34
0
 def user_mem_mod_already_pending(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership approval already pending")
     body = default_email_text.user_mem_mod_already_pending
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
Example #35
0
 def user_post_mod_notification(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Message pending moderation")
     body = default_email_text.user_post_mod_notification
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
Example #36
0
 def user_denied(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Membership denied")
     body = default_email_text.user_denied
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
Example #37
0
 def user_sub_rejected(self, user_email, user_name, reject_reason):
     user_name = nonascii_username(user_email, user_name)
     subject = _("Subscription request rejected")
     body = default_email_text.user_sub_rejected
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'reject_reason': reject_reason,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
Example #38
0
 def user_welcome(self, user_email, user_name):
     user_name = nonascii_username(user_email, user_name)
     subject = _("welcome_to_list",
                 "Welcome to ${title}",
                 mapping={'title': self.context.title})
     body = default_email_text.user_welcome
     mapping = {
         'fullname': user_name,
         'listname': self.context.title,
         'listaddress': self.context.mailto,
         'listmanager': self.context.manager_email,
     }
     body = Message(body, mapping=mapping)
     self.context.sendCommandRequestMail(user_email, translate(subject),
                                         translate(body))
 def updateList(self, ml):
     """See IListLookup interface documentation"""
     address = ml.mailto or ''
     # normalize case
     address = address.lower()
     path = '/'.join(ml.getPhysicalPath())
     current_addr = self._reverse.get(path, None)
     current_path = self._mapping.get(address, None)
     if (current_path is None and current_addr is not None
             and current_addr != address):
         # The mailing list address has changed to one which is unknown
         del self._mapping[current_addr]
         self._reverse[path] = address
         self._mapping[address] = path
     elif current_addr == address and current_path == path:
         # Nothing has changed, do nothing
         pass
     elif current_addr is None and current_path is None:
         # The list is not registered at all, this happens when the addform
         # was not used, stupid CMF
         self.registerList(ml)
     else:
         # The new address is already registered
         raise UserError, _("A list is already registered for this address")
    def reply_body(self, use_empty=False):
        """Add quote characters and attribution to original body."""
        req_body = self.request.get('body', None)
        if req_body and not use_empty:
            return req_body

        # Remove message signature
        stripped_body = stripSignature(self.context.body)
        quoted_body = rewrapMessage(stripped_body, add_quote=True)

        attribution = _("reply-attribution", u"On ${date}, ${author} wrote:")
        attribution = Message(attribution, mapping={
            u'date': self.date(),
            u'author': self.mail_from_name(do_encode=False)})
        return translate(attribution) + '\r\n' + quoted_body + '\r\n'
 def manager_mod_post_request(self, user_email, user_name, post):
     user_name = nonascii_username(user_email, user_name)
     subject = _(u"Post requiring moderation")
     body = default_email_text.manager_mod_post_request
     boundary = u'Boundary-%s-%s' % (random(), random())
     post_msg = create_request_from(post)['Mail'].decode('utf-8', 'replace')
     mapping = { 'fullname': user_name,
                 'mod_url': self.context.absolute_url() + '/moderation',
                 'listname': self.context.title,
                 'post': post_msg,
                 'boundary': boundary,}
     body = Message(body, mapping=mapping)
     extra_headers = {'Content-Type': u'multipart/mixed; boundary=%s' % boundary,
                      'Mime-Version': u'1.0',}
     self.send_to_managers(translate(subject), translate(body), extra_headers)
Example #42
0
    def reply_body(self, use_empty=False):
        """Add quote characters and attribution to original body."""
        req_body = self.request.get('body', None)
        if req_body and not use_empty:
            return req_body

        # Remove message signature
        stripped_body = stripSignature(self.context.body)
        quoted_body = rewrapMessage(stripped_body, add_quote=True)

        attribution = _("reply-attribution", u"On ${date}, ${author} wrote:")
        attribution = Message(attribution,
                              mapping={
                                  u'date': self.date(),
                                  u'author':
                                  self.mail_from_name(do_encode=False)
                              })
        return translate(attribution) + '\r\n' + quoted_body + '\r\n'
Example #43
0
 def manager_mod_post_request(self, user_email, user_name, post):
     user_name = nonascii_username(user_email, user_name)
     subject = _(u"Post requiring moderation")
     body = default_email_text.manager_mod_post_request
     boundary = u'Boundary-%s-%s' % (random(), random())
     post_msg = create_request_from(post)['Mail'].decode('utf-8', 'replace')
     mapping = {
         'fullname': user_name,
         'mod_url': self.context.absolute_url() + '/moderation',
         'listname': self.context.title,
         'post': post_msg,
         'boundary': boundary,
     }
     body = Message(body, mapping=mapping)
     extra_headers = {
         'Content-Type': u'multipart/mixed; boundary=%s' % boundary,
         'Mime-Version': u'1.0',
     }
     self.send_to_managers(translate(subject), translate(body),
                           extra_headers)
 def deliverMessage(self, request):
     """See IListLookup interface documentation"""
     # XXX raising NotFound annoyingly hides the real problem so
     # I've added a bunch of logging.  I propose in the future we
     # change NotFound to something that actually gets logged.  But
     # I'm afraid to do that now because I don't know if we somehow
     # depend on getting a 404 here.
     message = str(request.get(MAIL_PARAMETER_NAME, None))
     if message is not None:
         message = message_from_string(message)
     else:
         logger.error("request.get(%s) returned None" % MAIL_PARAMETER_NAME)
         raise NotFound, _("The message destination cannot be deterimined.")
     # preferentially use the x-original-to header (is this postfix only?),
     # so that mails to multiple lists are handled properly
     address = message.get('x-original-to', None)
     if not address:
         address = message['to']
         cc = message['cc']
         if address and cc:
             address = address + ', ' + cc
         elif cc:
             address = cc
     # normalize case
     if not address:
         import pprint
         logger.warn("No destination found in headers:\n%s" %
                     pprint.pformat(message))
         raise NotFound, _("The message destination cannot be deterimined.")
     address = address.lower()
     if '-manager@' in address:
         address = address.replace('-manager@', '@')
     address_list = AddressList(address)
     for ml_address in address_list:
         ml = self.getListForAddress(ml_address[1])
         if ml is not None:
             break
     else:
         # raise an error on bad requests, so that the SMTP server can
         # send a proper failure message.
         logger.warn("no list found for any of %r" % str(address_list))
         raise ListDoesNotExist, _("The message address does not correspond to a "\
                          "known mailing list.")
     setSite(ml)
     return ml.manage_mailboxer(request)
 def Title(self):
     title = _('label_archive_views_forum', u'Forum view for ${list}')
     title = Message(title, mapping={
         u'list': self.listTitle(),
     })
     return title
from Products.listen.i18n import _


user_pin_mismatch = _(u'user_pin_mismatch_body',u'''
Hello ${fullname},

Your request to join the ${listname} mailing list has been denied
because the pin you provided does not match the one initially sent.
Any questions regarding this should be sent to ${listmanager}.

Yours,
List Manager
''')

user_denied = _(u'user_denied_body',u'''
Hello ${fullname},

Your request to join the ${listname} mailing list has been denied
because there was no record of a request.
Any questions regarding this should be sent to ${listmanager}.

Yours,
List Manager
''')


user_mod = _(u'user_mod_body',u'''
Hello ${fullname},

We have received a message from you to the ${listname} mailing 
list. Permission for you to post messages to this list is 
 def list_type(self):
     list_type = self.context.list_type
     if list_type is None:
         return _(u'List Type not set')
     return '%s. %s' % (list_type.title, list_type.description)
 def handle_cancel_action(self, action, data):
     self.status = _(u"Edit cancelled.")
     return ""
 def Description(self):
     return _(u'Manage Allowed Senders')
 def Title(self):
     return _(u'Manage Allowed Senders')
 def listAddress(self):
     """The list address"""
     ml = self.getMailingList()
     if ml is not None:
         return obfct_de(encode(ml.mailto, ml))
     return _(u'No address')
 def Title(self):
     title = _('label_archive_views_date', u'Archive by date for ${title}')
     title = Message(title, mapping={ u'title': self.listTitle(), })
     return title
 def Title(self):
     title = _('label_archive_list_archive_view', u'${list} archive for ${date}')
     title = Message(title, mapping={ u'list': self.listTitle(),
                                      u'date':  self.context.title, })
     return title
 def Title(self):
     title = _('label_archive_search', u'Search messages in ${title}')
     title = Message(title, mapping={ u'title': self.listTitle(), })
     return title
 def listTitle(self):
     """The title of the list"""
     ml = self.getMailingList()
     if ml is not None:
         return ml.Title().decode('utf8')
     return _(u'No Title')
    def __call__(self):
        d = self.request.form
        post = email = None
        action = ''
        postid = None
        reject_reason = ''
        plone_utils = getToolByName(self.context, 'plone_utils')

        # first check if mass moderating all posts
        if d.get('discard_all_posts', False):
            action = 'discard'
            policy = getAdapter(self.context, IEmailPostPolicy)
            for post in self.get_pending_lists():
                postid = post['postid']
                email = post['user']
                req = dict(action=action, email=email, postid=postid)
                policy_result = policy.enforce(req)
                if policy_result == MODERATION_FAILED:
                    plone_utils.addPortalMessage(_(u'Could not moderate!'),
                                                 type='error')
                    break
                else:
                    plone_utils.addPortalMessage(_(u'All posts discarded.'),
                                                 type='info')
            self.request.response.redirect(self.nextURL())
            return

        for name, value in d.items():
            if name.endswith('_approve') or \
               name.endswith('_discard') or \
               name.endswith('_reject'):
                action = name.split('_')[-1]
            elif name == 'postid':
                postid = int(value)
            elif name == 'email':
                email = value
            elif name == 'reject_reason':
                reject_reason = value            

        # we only need to check moderation if we have an action specified
        if action:
            # having a post id specified means that we need to moderate posts
            if postid is not None:
                # using email post policy
                # may have to try enforcing the ITTWPostPolicy as well on failure
                policy = getAdapter(self.context, IEmailPostPolicy)
                req = dict(action=action, email=email, postid=postid, reject_reason=reject_reason)
                policy_result = policy.enforce(req)
                if policy_result == MODERATION_FAILED:
                    plone_utils.addPortalMessage(_(u'Could not moderate!'),
                                                 type='error')
                else:
                    plone_utils.addPortalMessage(_(u'Post moderated.'),
                                                 type='info')
            else:
                # same idea between membership policy
                # may have to try the IUserTTWMembershipPolicy if the email policy fails
                policy = getAdapter(self.context, IUserEmailMembershipPolicy)
                req = dict(action=action, email=email, reject_reason=reject_reason)
                policy_result = policy.enforce(req)
                if policy_result == MODERATION_FAILED:
                    plone_utils.addPortalMessage(_(u'Could not moderate!'),
                                                 type='error')
                else:
                    plone_utils.addPortalMessage(_(u'Member moderated.'),
                                                 type='info')
            self.request.response.redirect(self.nextURL())
            return

        return self.index()
                                 description=_(u'marked interface used to indicate the list type'),
                                 constraint=IListType.providedBy)

    index = Int(title=_(u'sort index'))

class ListDefinitionFactory(object):
    implements(IListTypeDefinition)
    
    def __init__(self, title, description, list_marker, index=100):
        self.title = title
        self.description = description
        self.list_marker = list_marker
        self.index = index

PublicListTypeDefinition = ListDefinitionFactory(
    title=_(u'Public List'),
    description=_(u'Anyone who confirms their email address is valid can post and receive messages.'),
    list_marker=IPublicList,
    index=100)

MembershipModeratedListTypeDefinition = ListDefinitionFactory(
    title=_(u'Members List'),
    description=_(u'Only those approved by the list managers can post and receive messages.'),
    list_marker=IMembershipModeratedList,
    index=300)

PostModeratedListTypeDefinition = ListDefinitionFactory(
    title=_(u'Announce List'),
    description=_(u'Anyone can receive messages, but each posted message has to be approved by the list managers first.'),
    list_marker=IPostModeratedList,
    index=200)
    def __call__(self):

        plone_utils = getToolByName(self.context, 'plone_utils')

        # handle export actions
        export_messages = self.request.form.get('export_messages')
        export_addresses = self.request.form.get('export_addresses')

        if export_messages or export_addresses:
            filename = file_data = ""
            if export_addresses:
                if not self.can(ExportMailingListSubscribers):
                    raise Unauthorized(_(u'Cannot export mailing list subscribers'))
                filename = "%s-subscribers.csv" % self.context.getId()
                es = getAdapter(self.context, IMailingListSubscriberExport, name='csv')
                file_data = es.export_subscribers()
            elif export_messages:
                if not self.can(ExportMailingListArchives):
                    raise Unauthorized(_(u'Cannot export mailing list archives'))
                filename = "%s.mbox" % self.context.getId()
                em = getAdapter(self.context, IMailingListMessageExport, name='mbox')
                file_data = em.export_messages()

            # both export actions result in a file to download
            self.request.response.setHeader("Content-type", "text/plain")
            self.request.response.setHeader("Content-disposition", "attachment; filename=%s" % filename)
            
            # XXX don't know why i have to do this
            import transaction
            transaction.abort()

            return file_data

        # import actions
        psm_text = psm_type = None
        # This should be moved elsewhere
        self.UNDO_SUBMIT = UNDO_SUBMIT = "undu_"
        for key in self.request.form:
            if key.startswith(UNDO_SUBMIT):
                # reuse the import permission here
                if not self.can(ImportMailingListArchives):
                    raise Unauthorized(_(u'Cannot import mailing list archives'))
                key = key[len(UNDO_SUBMIT):]
                msg_count = self.undo_import(key)
                if msg_count:
                    txt = (_(u"Removed %s message%s from the list archive.")
                           % (msg_count, self._plural(msg_count)))
                    plone_utils.addPortalMessage(txt, type='info')
                    self.request.response.redirect(self.nextURL())
                    return

        if self.request.form.get("import_messages"):
            if not self.can(ImportMailingListArchives):
                raise Unauthorized(_(u'Cannot import mailing list archives'))
            file = self.request.form.get("import_file")
            if file:
                im = getAdapter(self.context, IMailingListMessageImport, name='mbox')
                self.msgids = im.import_messages(file)
                self.filename = file.filename
                self.save_import_history()
                msg_count = len(self.msgids)
                psm_text = (_(u'Imported %s message%s from \'%s\'')
                    % (msg_count, self._plural(msg_count), file.filename))
                psm_type = 'info'
            else:
                psm_text = _(u"No file selected. Please select an mbox file before importing.")
                psm_type = 'error'

            plone_utils.addPortalMessage(psm_text, type=psm_type)
            self.request.response.redirect(self.nextURL())
            return

        if self.request.form.get('import_subscribers'):
            if not self.can(ImportMailingListSubscribers):
                raise Unauthorized(_(u'Cannot import mailing list subscribers'))
            file = self.request.form.get("import_subscribers_file")
            if file:
                emails = [s.strip().split(',')[-2:] for s in file]
                # quick sanity check
                emails = [e for e in emails 
                          if '@' in e[0]
                          and e[1].strip() in ("subscribed", "allowed")]
                subscriber_importer = IMailingListSubscriberImport(self.context)
                if emails:
                    subscriber_importer.import_subscribers(emails)
                    n_emails = len(emails)
                    if n_emails != 1:
                        psm_text = _(u'%s email addresses imported') % n_emails
                    else:
                        psm_text = _(u'1 email address imported')
                    psm_type = 'info'
                else:
                    psm_text = _(u'No emails found')
                    psm_type = 'error'
            else:
                psm_text = _(u"No file selected. Please select a csv file before importing.")
                psm_type = 'error'
            plone_utils.addPortalMessage(psm_text, type=psm_type)
            self.request.response.redirect(self.nextURL())
            return

        return self.index()
 def Title(self):
     title = _('label_archive_views_forum', u'Forum view for ${list}')
     title = Message(title, mapping={ u'list': self.listTitle(), })
     return title