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
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 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())
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
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())
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()
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.'))
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())
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))
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, )