Esempio n. 1
0
    def __call__(self, data, encoding):
        all_errors = []
        dialect = csv.Sniffer().sniff(data[:512])
        reader = csv.reader(StringIO(data), dialect)

        adder = SubscriberAdder(self.context, self.request, self.add_form)

        try:
            for idx, row in enumerate(reader):
                if not 2 <= len(row) <= 4:
                    all_errors.append(dict(number=idx + 1, email='',
                                           error=_(u'Invalid line format.')))
                    continue

                row = list(row) + (4 - len(row)) * [None]
                email, format, deactivation, activation = row
                email = email.decode(encoding)
                format = format.decode(encoding)

                error = adder.add(email, format, activation, deactivation)
                if error:
                    all_errors.append(dict(number=idx + 1, email=email,
                                           error=error))
                    continue
                self.all_failed = False

        except csv.Error:
            all_errors.append(dict(number=reader.line_num, email='',
                                   error=_(u'File is not a proper CSV.')))

        return all_errors
Esempio n. 2
0
class IImportFormSchema(model.Schema):

    file = NamedFile(title=_(u'File'))

    encoding = schema.Choice(
        title=_(u'Encoding'),
        vocabulary=encodings,
        default='utf-8',
    )
def addIndexesAndMetadataToTopics(portal, logger):
    atcttool = getToolByName(portal, 'portal_atct')
    title = _(u'Issue date')
    desc = _(u'The time and date an item was last sent')
    atcttool.addIndex('sortable_last_sent',
                      title,
                      description=desc,
                      enabled=True)
    atcttool.addMetadata('last_sent', title, description=desc, enabled=True)
def addIndexesAndMetadataToTopics(portal, logger):
    atcttool = getToolByName(portal, 'portal_atct')
    title = _(u'Issue date')
    desc  = _(u'The time and date an item was last sent')
    atcttool.addIndex('sortable_last_sent',
                      title,
                      description=desc,
                      enabled=True)
    atcttool.addMetadata('last_sent',
                         title,
                         description=desc,
                         enabled=True)
    def handleActivate(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        activateSubscriber(self.context)

        IStatusMessage(self.request).add(
            _(u'subscription_confirmed',
              default=_(u'Subscription of ${email} has been confirmed.'),
              mapping=dict(email=self.context.title)))
        self.request.response.redirect(getSite().absolute_url())
    def handleActivate(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        activateSubscriber(self.context)

        IStatusMessage(self.request).add(_(
            u'subscription_confirmed',
            default=_(u'Subscription of ${email} has been confirmed.'),
            mapping=dict(email=self.context.title)
        ))
        self.request.response.redirect(getSite().absolute_url())
Esempio n. 7
0
class ISubscriberListSchema(model.Schema,
                            IHasRelations,
                            interfaces.IPossibleSubscriberProvider):

    title = schema.TextLine(title=_(u'Name'))

    description = schema.Text(
        title=_(u'A short summary'),
        required=False,
    )

    def __setitem__(name, subscriber):
        """Add a subscriber.
        """
    __setitem__.precondition = precondition
Esempio n. 8
0
class ControlPanelView(ControlPanelFormWrapper, grok.View):
    grok.context(IPloneSiteRoot)
    grok.require('cmf.ManagePortal')
    grok.name('plonemailing-controlpanel')

    label = _(u'TN Plone Mailing settings')
    form = ControlPanelForm
def validate_html(value):
    if not value:
        raise zope.interface.Invalid(_(u'Cannot be empty'))
    try:
        lxml.html.fragment_fromstring(value)
    except lxml.etree.LxmlError, e:
        raise zope.interface.Invalid(unicode(e))
    def handleDeactivate(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        deactivateSubscriber(self.context)

        IStatusMessage(self.request).add(
            _(u'subscription_removal_confirmed',
              default=_(
                  u'Removal of ${email} from list ${list} has been confirmed.'
              ),
              mapping=dict(email=self.context.title,
                           list=self.context.__parent__.title)))
        self.request.response.redirect(getSite().absolute_url())
    def handleDeactivate(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        deactivateSubscriber(self.context)

        IStatusMessage(self.request).add(_(
            u'subscription_removal_confirmed',
            default=_(
                u'Removal of ${email} from list ${list} has been confirmed.'
            ),
            mapping=dict(email=self.context.title,
                         list=self.context.__parent__.title)
        ))
        self.request.response.redirect(getSite().absolute_url())
Esempio n. 12
0
    def handleImport(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        file = data['file']
        importer = CSVImporter(self.context, self.request)
        errors = importer(file.data, data['encoding'])

        if errors:
            self.status = _(u'One or more subscribers could not be imported.')
            self.invalid_subscribers = errors
            return

        IStatusMessage(self.request).add(_(u'All subscribers imported.'),
                                         type=u'info')
        self.redirect_to_context()
Esempio n. 13
0
class CSVImportForm(form.SchemaForm):
    grok.name('import')
    grok.context(ISubscriberListSchema)
    grok.require('cmf.ModifyPortalContent')

    schema = IImportFormSchema
    label = _(u'CSV import')
    description = _(u"Use this form to input a batch of subscribers (without "
                    u"confirmation). Provide a CSV table with columns email "
                    u"and format (\"html\" or \"text\"), in this order, "
                    u"without headers.  Optionally, you can add one more "
                    u'column to inform an deactivation date for the '
                    u'subscriber.  Also, a fourth column may be added to '
                    u'indicate an activation date.')
    ignoreContext = True
    invalid_subscribers = []
    all_failed = True

    @button.buttonAndHandler(_(u'Import'))
    def handleImport(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        file = data['file']
        importer = CSVImporter(self.context, self.request)
        errors = importer(file.data, data['encoding'])

        if errors:
            self.status = _(u'One or more subscribers could not be imported.')
            self.invalid_subscribers = errors
            return

        IStatusMessage(self.request).add(_(u'All subscribers imported.'),
                                         type=u'info')
        self.redirect_to_context()

    @button.buttonAndHandler(_(u"Cancel"))
    def handleCancel(self, action):
        self.redirect_to_context()

    def redirect_to_context(self):
        self.request.response.redirect(self.context.absolute_url())
 def check_removal_html(obj):
     # Sometimes attributes which are not required are not even set in the
     # received object (when using z3c.form).
     if not hasattr(obj, 'subscriber_removal_html'):
         return
     if obj.add_subscriber_removal and not obj.subscriber_removal_html:
         raise zope.interface.Invalid(_(
             u'Must provide a removal HTML if a link for this is to be '
             u'added.'
         ))
def checkEmailUniqueness(container, name, subscriber):
    email = subscriber.title
    portal = getSite()
    path = '/'.join(container.getPhysicalPath())
    catalog = getToolByName(portal, 'portal_catalog')
    items = catalog(object_provides=ISubscriberSchema.__identifier__,
                    path=path,
                    Title=email)

    if not items:
        return

    if name and len(items) == 1 and items[0].getId == name:
        return

    raise Invalid(_(u'E-mail address is not unique in this folder.'))
def checkEmailUniqueness(container, name, subscriber):
    email = subscriber.title
    portal = getSite()
    path = '/'.join(container.getPhysicalPath())
    catalog = getToolByName(portal, 'portal_catalog')
    items = catalog(object_provides=ISubscriberSchema.__identifier__,
                    path=path,
                    Title=email)

    if not items:
        return

    if name and len(items) == 1 and items[0].getId == name:
        return

    raise Invalid(_(u'E-mail address is not unique in this folder.'))
class Deactivate(form.SchemaForm):
    grok.context(ISubscriberSchema)
    grok.require('zope2.View')

    schema = Interface
    ignoreContext = True

    @property
    def label(self):
        return _(u'deactivate_subscriber_title_message',
                 default=u'Removal confirmation for ${email}',
                 mapping=dict(email=self.context.title))

    @property
    def description(self):
        return _(u'deactivate_subscriber_description_message',
                 default=u'Please confirm removal from ${list_title} list.',
                 mapping=dict(list_title=self.context.__parent__.title))

    def update(self):
        self.request.set('disable_border', True)
        return super(Deactivate, self).update()

    @button.buttonAndHandler(_(u'Deactivate'))
    def handleDeactivate(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        deactivateSubscriber(self.context)

        IStatusMessage(self.request).add(
            _(u'subscription_removal_confirmed',
              default=_(
                  u'Removal of ${email} from list ${list} has been confirmed.'
              ),
              mapping=dict(email=self.context.title,
                           list=self.context.__parent__.title)))
        self.request.response.redirect(getSite().absolute_url())
Esempio n. 18
0
    def parse_datetime(self, datestr):
        if not datestr:
            return None
        locale = self.request.locale

        parsers = [
            locale.dates.getFormatter('date', 'short').parse,
            locale.dates.getFormatter('date', 'medium').parse,
            locale.dates.getFormatter('dateTime', 'short').parse,
            locale.dates.getFormatter('dateTime', 'medium').parse,
        ]

        def to_datetime(date_or_datetime):
            if isinstance(date_or_datetime, datetime.datetime):
                return date_or_datetime
            date = date_or_datetime
            return datetime.datetime(date.year, date.month, date.day)

        for parser in parsers:
            try:
                return to_datetime(parser(datestr))
            except zope.i18n.format.DateTimeParseError:
                continue
        raise Invalid(_(u'Invalid date format.'))
 def label(self):
     return _(u'deactivate_subscriber_title_message',
              default=u'Removal confirmation for ${email}',
              mapping=dict(email=self.context.title))
class ISubscriberSchema(model.Schema):

    title = schema.TextLine(title=_(u'E-mail'), constraint=is_email)
    format = schema.Choice(title=_(u'Format'), vocabulary=formats)
 def description(self):
     return _(u'deactivate_subscriber_description_message',
              default=u'Please confirm removal from ${list_title} list.',
              mapping=dict(list_title=self.context.__parent__.title))
 def label(self):
     return _(u'deactivate_subscriber_title_message',
              default=u'Removal confirmation for ${email}',
              mapping=dict(email=self.context.title))
import datetime
import re


is_email_re = re.compile(r'^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$',
                         re.IGNORECASE)


def is_email(value):
    if is_email_re.match(value):
        return True
    raise Invalid(u'E-mail address is not valid.')

formats = schema.vocabulary.SimpleVocabulary([
    schema.vocabulary.SimpleTerm(value=u'html', title=_(u'HTML')),
    schema.vocabulary.SimpleTerm(value=u'text', title=_(u'Text')),
])


class ISubscriberSchema(model.Schema):

    title = schema.TextLine(title=_(u'E-mail'), constraint=is_email)
    format = schema.Choice(title=_(u'Format'), vocabulary=formats)


class EmailValidator(validator.SimpleFieldValidator, grok.MultiAdapter):
    grok.adapts(Interface, Interface, Interface,
                util.getSpecification(ISubscriberSchema['title']), Interface)
    grok.provides(IValidator)
import datetime
import re

is_email_re = re.compile(r'^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$',
                         re.IGNORECASE)


def is_email(value):
    if is_email_re.match(value):
        return True
    raise Invalid(u'E-mail address is not valid.')


formats = schema.vocabulary.SimpleVocabulary([
    schema.vocabulary.SimpleTerm(value=u'html', title=_(u'HTML')),
    schema.vocabulary.SimpleTerm(value=u'text', title=_(u'Text')),
])


class ISubscriberSchema(model.Schema):

    title = schema.TextLine(title=_(u'E-mail'), constraint=is_email)
    format = schema.Choice(title=_(u'Format'), vocabulary=formats)


class EmailValidator(validator.SimpleFieldValidator, grok.MultiAdapter):
    grok.adapts(Interface, Interface, Interface,
                util.getSpecification(ISubscriberSchema['title']), Interface)
    grok.provides(IValidator)
class INewsletterFromContent(model.Schema, interfaces.ISubscriberProvider):
    """Transform a content into a newsletter.
    """

    form.fieldset(
        'newsletter',
        label=_(u'Newsletter'),
        fields=('author_address', 'author_name', 'sender_address',
                'sender_name', 'reply_to_address', 'reply_to_name', 'subject'),
    )

    possible_subscriber_providers = z3c.relationfield.RelationList(
        title=_(u'Subscriber providers'),
        description=_(u'The subscriber providers to which this content '
                      u'should be sent.'),
        required=False,
        value_type=z3c.relationfield.RelationChoice(
            source=possiblePossibleSubscriberProviders))

    author_address = zope.schema.TextLine(
        title=_(u'Author e-mail address'),
        description=_(u'The e-mail address of the author of the newsletter.  '
                      u'If none is provided, the address '
                      u'set as the site\'s contact form address will be '
                      u'used.'),
        required=False,
    )

    author_name = zope.schema.TextLine(
        title=_(u'Author name'),
        description=_(u'The name of the author of the newsletter.  '
                      u'This is the name which will appear in the in '
                      u'the mailbox of the subscriber.'),
        required=False,
    )

    sender_address = zope.schema.TextLine(
        title=_(u'Sender e-mail address'),
        description=_(u'The e-mail address of the sender of the newsletter.  '
                      u'This '
                      u'should match the authentication address used to send '
                      u'the newsletter (otherwise you may have delivery '
                      u'problems for some subscribers).  If none is provided, '
                      u'the "Author e-mail address" is used.'),
        required=False,
    )

    sender_name = zope.schema.TextLine(
        title=_(u'Sender name'),
        description=_(u'The name of the sender of the newsletter.  If none is '
                      u'provided, the "Author name" is used.'),
        required=False)

    reply_to_address = zope.schema.TextLine(
        title=_("Reply e-mail address"),
        description=_(u'The e-mail address where replies should go to.  '
                      u'Leave blank '
                      u'to not set a reply address.'),
        required=base_newsletter['reply_to_address'].required,
    )

    reply_to_name = zope.schema.TextLine(
        title=_("Reply person name"),
        description=_(u'The name of the person whom replies should go to.'),
        required=base_newsletter['reply_to_name'].required,
    )

    subject = zope.schema.TextLine(
        title=_(u'Subject'),
        description=_(u'The subject of the newsletter.  If none is provided '
                      u'the title of the content is used.'),
        required=False,
    )
 def description(self):
     return _(u'deactivate_subscriber_description_message',
              default=u'Please confirm removal from ${list_title} list.',
              mapping=dict(list_title=self.context.__parent__.title))