Exemplo n.º 1
0
    def test_send_subscriber(self):
        self.gfolder.invokeFactory('gazette.GazetteIssue', 'issue')
        issue = self.gfolder['issue']
        issue.title = u'Test issue'
        issue.text = RichTextValue('Hello world!')

        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)
        adapter.subscribe('*****@*****.**', '')

        # activation message
        self.failUnless(len(self.mailhost.messages) == 1)

        # Activate subsriber
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        s = soup.query(email='*****@*****.**').next()
        s.active = True
        s.key = u''
        soup.reindex([s])

        transaction.commit()
        browser = Browser(self.layer['app'])
        browser.handleErrors = False
        browser.addHeader('Authorization', 'Basic %s:%s' % (TEST_USER_NAME, TEST_USER_PASSWORD,))

        browser.open(issue.absolute_url() + '/send')
        self.failUnless('Number of subscribers: 1' in browser.contents)
        browser.getControl(name='submit').click()
        self.failUnless('Gazette has been sent to 1 recipients' in browser.contents)
        # activation + issue
        self.failUnless(len(self.mailhost.messages) == 2)
Exemplo n.º 2
0
    def send_gazette(self):
        context = aq_inner(self.context)
        parent = aq_parent(context)
        now = datetime.now()
        CheckAuthenticator(self.request)
        ptool = getToolByName(self.context, 'plone_utils')
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        providers = self._providers()
        subject = context.Title()

        url = parent.absolute_url() + '/subscription?uuid=%(uuid)s'
        footer_text = parent.footer.output.replace('${url}', '$url')
        footer_text = footer_text.replace('$url', url)
        count = 0
        text = context.text.output + '\n'
        for p in providers:
            text += p.get_gazette_text(parent, context)

        for s in soup.query(active=True):
            # returns email and fullname taken from memberdata if s.username is set and member exists
            subscriber_info = s.get_info(context)
            footer = footer_text % subscriber_info
            mail_text = ""
            if subscriber_info['salutation']:
                mail_text += "%s<br /><br />" % subscriber_info['salutation']
            mail_text += "%s------------<br />%s" % (text, footer)
            try:
                if utils.send_mail(context, None, subscriber_info['email'], subscriber_info['fullname'], subject, mail_text):
                    count += 1
            except (SMTPException, SMTPRecipientsRefused):
                pass
        context.sent_at = now
        parent.most_recent_issue = context
        ptool.addPortalMessage(_(u'Gazette has been sent to $count recipients', mapping={'count': count}))
        self.request.response.redirect(context.absolute_url())
Exemplo n.º 3
0
    def subscribe(self, email, fullname, username=u'', send_activation_mail=True, wait_for_confirmation=True, **kwargs):
        # find if there is existing subscription
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        email = email.strip()
        if username:
            # ignore everything
            user = api.user.get(username='******')
            if user:
                email = user.getProperty('email', '')
                if email:
                    # subscribe as portal user
                    # is username is set, subscription is considered as subscription of portal member
                    # override fullname, if any
                    fullname = user.getProperty('fullname', fullname)

        if not email:
            return config.INVALID_DATA
        results = [r for r in soup.query(email=email)]
        _marker = object()
        if results:
            s = results[0]
            s.email = email
            s.fullname = fullname
            if s.active:
                send_activation_mail = False
                if wait_for_confirmation:
                    s.key = GenerateSecret()
                    result = config.WAITING_FOR_CONFIRMATION
                else:
                    result = config.ALREADY_SUBSCRIBED
            else:
                s.active = True
                result = config.SUBSCRIPTION_SUCCESSFULL
            for k, v in kwargs.items():
                has_attr = getattr(s, k, _marker)
                if has_attr is not _marker:
                    setattr(s, k, v)
            soup.reindex([s])
            if send_activation_mail:
                # Do not send if user is editor and has different email address
                self.activation_mail(s)
            return result
        else:
            # new subscriber
            s = Subscriber(email=email, fullname=fullname, active=False, username=username)
            if wait_for_confirmation:
                s.key = GenerateSecret()
                result = config.WAITING_FOR_CONFIRMATION
            else:
                s.active = True
                result = config.SUBSCRIPTION_SUCCESSFULL
            for k, v in kwargs.items():
                has_attr = getattr(s, k, _marker)
                if has_attr is not _marker:
                    setattr(s, k, v)
            soup.add(s)
            if send_activation_mail:
                self.activation_mail(s)
            return result
    def test_subscribe_anon(self):

        logout()
        provideAdapter(
            adapts=(Interface, IBrowserRequest), provides=Interface, factory=SubscriberForm, name=u"subscriber-form"
        )

        # empty form
        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)
        self.gfolder.subscription_require_tos = True

        subscriberForm = getMultiAdapter((self.gfolder, request), name=u"subscriber-form")
        subscriberForm.update()
        data, errors = subscriberForm.extractData()
        self.assertEquals(len(errors), 1)
        self.assertEquals(adapter.subscribe("", ""), INVALID_DATA)

        self.gfolder.subscription_require_tos = False
        subscriberForm = getMultiAdapter((self.gfolder, request), name=u"subscriber-form")
        subscriberForm.update()
        data, errors = subscriberForm.extractData()

        self.assertEquals(len(errors), 1)  # FIXME - there should be 1 in this test - TOS is disabled
        self.assertEquals(adapter.subscribe("", ""), INVALID_DATA)

        # fill email only
        request.form = {"form.widgets.email": u"*****@*****.**", "form.widgets.tos:list": u"selected"}
        subscriberForm = getMultiAdapter((self.context, request), name=u"subscriber-form")
        subscriberForm.update()
        data, errors = subscriberForm.extractData()

        self.assertEquals(len(errors), 0)
        self.assertEquals(adapter.subscribe("*****@*****.**", ""), WAITING_FOR_CONFIRMATION)
        self.assertEquals(len(self.mailhost.messages), 1)

        # second subscription of inactive user resends activation email
        self.assertEquals(adapter.subscribe("*****@*****.**", ""), WAITING_FOR_CONFIRMATION)
        self.assertEquals(len(self.mailhost.messages), 2)
        msg = _parseMessage(self.mailhost.messages[1])
        self.assertEquals(msg["To"], "*****@*****.**")
        msgtext = msg.get_payload(decode=True)
        # let's retrieve the key from the message
        groups = re.search("\?key=([a-f0-9]+)", msgtext).groups()
        self.assertEquals(len(groups), 1)
        key = groups[0]

        activationView = ActivationView(self.context, request)
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        subscriber = soup.data.values()[0]

        self.assertEquals(subscriber.email, u"*****@*****.**")
        self.assertEquals(subscriber.active, False)
        self.assertEquals(subscriber.key, key)
        self.assertEquals(subscriber.username, "")
        activationView.activate(key)
        self.assertEquals(subscriber.key, u"")
        self.assertEquals(subscriber.active, True)
        self.assertEquals(subscriber.username, "")
Exemplo n.º 5
0
 def reindex_soup(self):
     id = self.request.form.get('id')
     if not id:
         return self.redirect_base('No id')
     soup = getSoup(self.context, id)
     soup.reindex()
     msg = '%s reindexed.' % id
     return self.redirect_base(msg)
Exemplo n.º 6
0
 def rebuild_soup(self):
     id = self.request.form.get('id')
     if not id:
         return self.redirect_base(msg)
     soup = getSoup(self.context, id)
     soup.rebuild()
     msg = '%s rebuilt.' % id
     return self.redirect_base(msg)
Exemplo n.º 7
0
 def rebuild_length(self):
     id = self.request.form.get('id')
     if not id:
         return self.redirect_base(msg)
     soup = getSoup(self.context, id)
     newlen = len(soup.storage.data)
     soup.storage.length.set(newlen)
     transaction.commit()
     return self.redirect_base(u'Length of storage %s is %s' % (id, newlen))        
Exemplo n.º 8
0
 def subscriber(self):
     uuid = self.request.form.get("uuid")
     value = None
     if uuid:
         value = getattr(self, "_v_subscriber", None)
         soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
         for s in soup.query(uuid=uuid):
             self._v_subscriber = value = s
             break
     return value
Exemplo n.º 9
0
 def update(self, uuid, **kwargs):
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     results = [r for r in soup.query(uuid=uuid)]
     _marker = object()
     if results:
         s = results[0]
         for k, v in kwargs.items():
             if k == 'uuid':
                 continue
             has_attr = getattr(s, k, _marker)
             if has_attr is not _marker:
                 setattr(s, k, v)
     else:
         return config.NO_SUCH_SUBSCRIPTION
    def test_subscribe_1000users(self):
        logout()

        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)

        for i in range(1, 1001):
            self.assertEquals(adapter.subscribe("bleh@blah-%d.com" % i, "Dummy %d" % i), WAITING_FOR_CONFIRMATION)

        subscribers = soup.data.values()
        self.assertEquals(len(subscribers), 1000)

        subscriber = soup.query(email="*****@*****.**").next()
        self.assertEquals(subscriber.fullname, "Dummy 149")
    def setUp(self):
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]

        setRoles(self.portal, TEST_USER_ID, ["Manager"])
        login(self.portal, TEST_USER_NAME)
        _createObjectByType("gazette.GazetteFolder", self.portal, "gazettefolder")
        self.gfolder = self.portal.gazettefolder
        self.context = self.gfolder
        self.soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        self.soup.add(Subscriber(email="*****@*****.**", fullname="Tester One", active=True))
        self.soup.add(Subscriber(email="*****@*****.**", fullname="Tester Two", active=False))
        self.soup.add(Subscriber(email="*****@*****.**", fullname="Tester Three", active=True))
        self.soup.add(Subscriber(email="*****@*****.**", fullname="Tester Four", active=False))
        self.soup.add(Subscriber(email="*****@*****.**", fullname="Tester Five", active=True))
Exemplo n.º 12
0
    def __call__(self):
        result = []
        if self.request.get('form.submitted') == '1':
            query = {}
            fulltext = self.request.get('fulltext', '')
            if fulltext:
                query['SearchableText'] = fulltext
            active = self.request.get('active', '')
            if active == '1':
                query['active'] = True
            elif active == '0':
                query['active'] = False
            result = self.search(**query)
        if self.request.get('form.import.submitted') == '1':
            fp = self.request.get('subscribers')
            putil = getToolByName(self.context, 'plone_utils')
            book = xlrd.open_workbook(filename=fp.filename, file_contents=fp.read())
            sh = book.sheet_by_index(0)
            valid = 0
            invalid = 0
            duplicates = 0
            if sh.nrows > 0:
                email = sh.row(0)[0].value
                if putil.validateSingleEmailAddress(email):
                    start = 0
                else:
                    start = 1
                soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
                for rx in range(start, sh.nrows):
                    # [text:u'*****@*****.**', text:u'Test 2', empty:'', empty:'', empty:'']
                    row = sh.row(rx)
                    email = row[0].value.strip()
                    fullname = row[1].value.strip()
                    if putil.validateSingleEmailAddress(email):
                        if not [r for r in soup.lazy(email=email)]:
                            # subscribe (don't try to pair with portal users)
                            s = Subscriber(email=email, fullname=fullname, active=True)
                            soup.add(s)
                            valid += 1
                        else:
                            duplicates += 1
                    else:
                        invalid += 1
                putil.addPortalMessage(_("Imported subscribers: ${imported}, Invalid: ${invalid}, Duplicates: ${duplicates}", mapping={'imported': valid, 'duplicates': duplicates, 'invalid': invalid}))
            else:
                putil.addPortalMessage(_("XLS file is empty"))

        return self.template(searchresults=result)
Exemplo n.º 13
0
 def deactivate(self, key):
     """ """
     if not key:
         raise Forbidden("Authenticator is invalid.")
     ptool = getToolByName(self.context, "plone_utils")
     # get subscriber by key
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     results = [r for r in soup.query(key=key)]
     if len(results) != 1:
         ptool.addPortalMessage(_(u"Invalid key"))
     else:
         s = results[0]
         s.active = False
         s.key = u""
         soup.reindex([s])
         ptool.addPortalMessage(_(u"Your subscription has been successfully deactivated."))
     self.request.response.redirect(self.context.absolute_url())
Exemplo n.º 14
0
 def search(self, **query):
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     result = []
     ACTIVE = _(u'label_yes', default=u'Yes')
     INACTIVE = _(u'label_no', default=u'No')
     if query:
         rows = soup.query(**query)
     else:
         # all records
         rows = soup.data.values()
     for row in rows:
         result.append(dict(
             fullname=row.fullname,
             email=row.email,
             active=row.active and ACTIVE or INACTIVE,
             username=row.username,
             uuid=row.uuid,
         ))
     return result
Exemplo n.º 15
0
 def unsubscribe(self, email, send_deactivation_mail=True, wait_for_confirmation=True):
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     results = [r for r in soup.query(email=email)]
     if results:
         if not results[0].active:
             return config.ALREADY_UNSUBSCRIBED
         else:
             s = results[0]
             if wait_for_confirmation:
                 s.key = GenerateSecret()
                 result = config.WAITING_FOR_CONFIRMATION
             else:
                 s.active = False
                 result = config.UNSUBSCRIBED
             soup.reindex([s])
             if send_deactivation_mail:
                 self.deactivation_mail(s)
             return result
     else:
         return config.NO_SUCH_SUBSCRIPTION
    def test_subscribe_user(self):
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        user = self.portal.acl_users.getUserById("user1")
        user.setProperties(email="*****@*****.**", fullname="Joe User")
        login(self.portal, "user1")

        self.gfolder.subscription_require_tos = False

        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)
        subscriberForm = getMultiAdapter((self.context, request), name=u"subscriber-form")
        subscriberForm.update()
        data, errors = subscriberForm.extractData()
        # email and fullname are prefilled
        self.assertEquals(len(errors), 0)
        # even if I provide different email, it is ignored.
        self.assertEquals(adapter.subscribe("*****@*****.**", "Dummy", "user1"), WAITING_FOR_CONFIRMATION)

        subscriber = soup.data.values()[0]
        self.assertEquals(subscriber.active, False)
        self.assertEquals(subscriber.email, "*****@*****.**")
        self.assertEquals(subscriber.username, "user1")
Exemplo n.º 17
0
    def test_autoissue(self):
        view = self.gfolder.restrictedTraverse('auto-issue')
        result = view.render(test_mode=True, html_only=True)
        self.assertEqual(result, u"""<!doctype html>\n<html>\n<head>\n<meta http-equiv="content-type" content="text/html; charset=utf-8" />\n</head>\n<body>\n<p>Hello world</p>\n</body>\n</html>\n""")
        # this was a test mode - no recent issue created/set
        self.failIf(self.gfolder.objectIds())
        self.failIf(self.gfolder.most_recent_issue)
        result = view.render(test_mode=False, html_only=True)
        # no subscribers, no email
        self.failIf(len(self.mailhost.messages) != 0)
        self.failUnless(len(self.gfolder.objectIds()) == 1)
        self.failUnless(self.gfolder.objectIds()[0] == self.gfolder.most_recent_issue.getId())

        # subscibe a subscriber
        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)
        adapter.subscribe('*****@*****.**', '')
        result = view.render(test_mode=False, html_only=True)
        # subscription request only -- user is not active yet
        self.failUnless(len(self.mailhost.messages) == 1)

        # Activate subsriber
        soup = getSoup(self.gfolder, config.SUBSCRIBERS_SOUP_ID)
        s = soup.query(email='*****@*****.**').next()
        s.active = True
        s.key = u''
        soup.reindex([s])

        result = view.render(test_mode=False, html_only=True)
        # subscription request and issue emails
        self.failUnless(len(self.mailhost.messages) == 2)
        # but all issues are stored as sub objects
        self.failUnless(len(self.gfolder.objectIds()) == 3)

        # finally try to make a PDF
        result = view.render(test_mode=True, html_only=False)
        self.failUnless(result.startswith(r'%PDF-'), msg='Please check wkhtmltopdf is installed in your system.')
    def test_unsubscribe_anon(self):
        logout()
        provideAdapter(
            adapts=(Interface, IBrowserRequest), provides=Interface, factory=SubscriberForm, name=u"subscriber-form"
        )

        # subscribe user
        request = self.request
        adapter = getMultiAdapter((self.gfolder, request), IGazetteSubscription)
        request.form = {"form.widgets.email": u"*****@*****.**"}
        subscriberForm = getMultiAdapter((self.context, request), name=u"subscriber-form")
        subscriberForm.update()
        data, errors = subscriberForm.extractData()
        self.assertEquals(adapter.subscribe("*****@*****.**", ""), WAITING_FOR_CONFIRMATION)

        msg = _parseMessage(self.mailhost.messages[0])
        self.assertEquals(msg["To"], "*****@*****.**")
        msgtext = msg.get_payload(decode=True)

        groups = re.search("\?key=([a-f0-9]+)", msgtext).groups()
        self.assertEquals(len(groups), 1)
        key = groups[0]

        # activate
        activationView = ActivationView(self.context, request)
        activationView.activate(key)

        self.assertEquals(adapter.unsubscribe("*****@*****.**"), WAITING_FOR_CONFIRMATION)
        msg = _parseMessage(self.mailhost.messages[1])
        msgtext = msg.get_payload(decode=True)
        key = re.search("\?key=([a-f0-9]+)", msgtext).groups()[0]
        # deactivate
        activationView.deactivate(key)
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        subscriber = soup.data.values()[0]
        self.assertEquals(subscriber.active, False)
Exemplo n.º 19
0
    def render(self, test_mode=False, html_only=False):
        """ This method does all the hard work """
        context = aq_inner(self.context)
        if not context.auto_enabled:
            return 'N/A'

        now = datetime.now()
        wtool = getToolByName(context, 'portal_workflow')
        soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
        # strftime accepts any text, not only strftime characters
        subject = now.strftime(context.auto_subject.encode('utf-8'))
        url = context.absolute_url() + '/subscription?uuid=%(uuid)s'
        footer_text = context.footer.output.replace('${url}', '$url')
        footer_text = footer_text.replace('$url', url)
        count = 0
        base_text = ''
        if context.auto_text:
            base_text += now.strftime(context.auto_text.output.encode('utf-8')) + '\n'
        providers = self._providers()
        gid = 'issue-%s' % now.strftime("%Y-%m-%d-%H-%M-%S.%f")
        idx = 0
        while context.check_id(gid):  # python script in skins
            idx += 1
            gid = 'issue-%s-%d' % (now.strftime("%Y-%m-%d-%H-%M-%S.%f"), idx)
        # create anonymous issue text to be stored to portal
        text = safe_unicode(base_text)
        auto_text = u''
        provider_names = []

        for p in providers:
            auto_text += safe_unicode(p.get_gazette_text(context, None))
            provider_names.append(repr(p))

        if not auto_text:
            # There is no automatically geenrated text. Discard sending of newsletter.
            return 'Nothing to send'

        text = text + auto_text
        # Create PDF version of the newsletter using wkhtml2pdf as archive of the issue
        pdf_raw = self.make_pdf(text, html_only)
        if not pdf_raw:
            logger.warning('Unable to create PDF of automatically issued gazette.')
        if not test_mode:
            # create Gazette object representing this issue
            gid = context.invokeFactory('gazette.GazetteIssue', gid)
            gazette = context[gid]
            # Fill the newly create Gazette object with generated data
            gazette.title = subject
            gazette.text = RichTextValue(text, mimeType='text/html', outputMimeType='text/html')
            gazette.providers = provider_names
            gazette.sent_at = now
            try:
                # ignore if there is no publish option for now
                wtool.doActionFor(gazette, 'publish')
            except:
                pass
            # Attach PDF to gazette but only if it is not HTML only mode
            if pdf_raw and not html_only:
                fid = gazette.invokeFactory('File', gid + '.pdf')
                file_pdf = gazette[fid]
                file_pdf.setTitle(gazette.title)
                file_pdf.setFile(pdf_raw, mimetype='application/pdf')
                file_pdf.processForm()

            for s in soup.query(active=True):
                # returns email and fullname taken from memberdata if s.username is set and member exists
                subscriber_info = s.get_info(context)
                footer = footer_text % subscriber_info
                mail_text = ""
                if subscriber_info['salutation']:
                    mail_text += "%s<br /><br />" % subscriber_info['salutation']
                mail_text += "%s------------<br />%s" % (text, footer)
                try:
                    if utils.send_mail(context, None, subscriber_info['email'], subscriber_info['fullname'], subject, mail_text):
                        count += 1
                except (SMTPException, SMTPRecipientsRefused):
                    pass
            context.most_recent_issue = gazette
        else:
            if html_only:
                self.request.response.setHeader('Content-Type', 'text/html;charset=utf-8')
            else:
                self.request.response.setHeader('Content-Type', 'application/pdf')
            return pdf_raw

        return str(count)
Exemplo n.º 20
0
 def count(self, soup):
     soup = getSoup(aq_inner(self.context), soup)
     return len(soup.storage)
Exemplo n.º 21
0
 def count(self):
     """ Number of subscribers """
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     return len([x for x in soup.lazy(active=True)])
Exemplo n.º 22
0
 def count(self):
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     result = dict()
     result['active'] = len([x for x in soup.lazy(active=True)])
     result['inactive'] = len([x for x in soup.lazy(active=False)])
     return result
Exemplo n.º 23
0
 def status(self, email):
     soup = getSoup(self.context, config.SUBSCRIBERS_SOUP_ID)
     results = [r for r in soup.query(email=email)]
     if results:
         return results[0].active
     return False