Support auto-tweeting when sponsorship benefit is confirmed
authorMagnus Hagander <magnus@hagander.net>
Tue, 9 Oct 2018 20:18:39 +0000 (22:18 +0200)
committerMagnus Hagander <magnus@hagander.net>
Tue, 9 Oct 2018 20:21:43 +0000 (22:21 +0200)
This allows automatic posting of tweets like "welcome xyz as foobar
sponsor awesomeconf". Which will make it less likely to forget...

docs/confreg/sponsors.md
postgresqleu/confreg/jinjafunc.py
postgresqleu/confsponsor/backendforms.py
postgresqleu/confsponsor/migrations/0010_benefit_tweet_template.py [new file with mode: 0644]
postgresqleu/confsponsor/models.py
postgresqleu/confsponsor/views.py

index d78d19ed179b3a9c9b793f1a2f1d7bab08b511dd..b458c41f6f1d12bdde9fd526e8ebea7a1a4b3263 100644 (file)
@@ -179,6 +179,11 @@ Class parameters
    JSON format. Will be automatically populated with a default set of
    parameters when created, but their values have to be set.
 
+Tweet template
+:  A template, in jinja2 format, used to generate tweets when this
+benefit is confirmed. If left empty, no tweet is posted. Can reference
+*sponsor*, *level*, *conference* and *benefit* variables that will be
+filled with information about the current conference.
 
 ### Sponsorship contract <a name="contract"></a>
 
index 0a0aa15998127d3a797ab176331c47e40502ab6c..b14bc17c63d6ae924535d045a7631ae1c53e43ca 100644 (file)
@@ -1,6 +1,7 @@
 from django.http import Http404, HttpResponse
 from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
 from django.template import defaultfilters
+from django.core.exceptions import ValidationError
 from django.contrib.messages.api import get_messages
 from django.utils.text import slugify
 from django.conf import settings
@@ -266,3 +267,25 @@ def render_jinja_conference_response(request, conference, pagemagic, templatenam
        c.update(settings_context_unicode())
 
        return HttpResponse(t.render(**c), content_type='text/html')
+
+
+
+
+
+# Small sandboxed jinja templates that can be configured in system
+def render_sandboxed_template(templatestr, context):
+       env = ConfSandbox(loader=jinja2.DictLoader({'t': templatestr}))
+       t = env.get_template('t')
+       return t.render(context)
+
+class JinjaTemplateValidator(object):
+       def __init__(self, context={}):
+               self.context = context
+
+       def __call__(self, s):
+               try:
+                       render_sandboxed_template(s, self.context)
+               except jinja2.TemplateSyntaxError, e:
+                       raise ValidationError("Template syntax error: %s" % e)
+               except Exception, e:
+                       raise ValidationError("Failed to parse template: %s" % e)
index 9a3dddca22b71d22d1c4233133a8905c11d9aaa9..0f1a1f8c112768a70c0576ebb2513f884e413fb6 100644 (file)
@@ -6,6 +6,7 @@ from postgresqleu.util.magic import magicdb
 from postgresqleu.util.widgets import RequiredFileUploadWidget, PrettyPrintJsonWidget
 from postgresqleu.confreg.backendforms import BackendForm
 from postgresqleu.confreg.backendlookups import GeneralAccountLookup
+from postgresqleu.confreg.jinjafunc import JinjaTemplateValidator, render_sandboxed_template
 
 from models import Sponsor
 from models import SponsorshipLevel, SponsorshipContract, SponsorshipBenefit
@@ -39,14 +40,39 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
        helplink='sponsors#benefit'
        json_fields = ['class_parameters', ]
        markdown_fields = ['benefitdescription', 'claimprompt', ]
+       dynamic_preview_fields = ['tweet_template']
+
        class Meta:
                model = SponsorshipBenefit
                fields = ['benefitname', 'benefitdescription', 'sortkey', 'benefit_class',
-                                 'claimprompt', 'class_parameters', ]
+                                 'claimprompt', 'class_parameters', 'tweet_template']
                widgets = {
                        'class_parameters': PrettyPrintJsonWidget,
                }
 
+       def fix_fields(self):
+               self.fields['tweet_template'].validators = [
+                       JinjaTemplateValidator({
+                               'conference': self.conference,
+                               'benefit': self.instance,
+                               'level': self.instance.level,
+                               'sponsor': Sponsor(name='Test'),
+                       }),
+               ]
+
+       @classmethod
+       def get_dynamic_preview(self, fieldname, s, objid):
+               if fieldname == 'tweet_template':
+                       if objid:
+                               o = self.Meta.model.objects.get(pk=objid)
+                               return render_sandboxed_template(s, {
+                                       'benefit': o,
+                                       'level': o.level,
+                                       'conference': o.level.conference,
+                                       'sponsor': Sponsor(name='Test'),
+                               })
+                       return ''
+
        def clean(self):
                cleaned_data = super(BackendSponsorshipLevelBenefitForm, self).clean()
                if cleaned_data.get('benefit_class') >= 0:
diff --git a/postgresqleu/confsponsor/migrations/0010_benefit_tweet_template.py b/postgresqleu/confsponsor/migrations/0010_benefit_tweet_template.py
new file mode 100644 (file)
index 0000000..8eca775
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2018-10-09 21:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('confsponsor', '0009_vat_allow_null'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='sponsorshipbenefit',
+            name='tweet_template',
+            field=models.TextField(blank=True),
+        ),
+    ]
index 51c3ce1c20fe8c29ee91d50b60c6c0e48c7f48ed..c27ba86fce6a24555977fefff9b6d5d59cb773a7 100644 (file)
@@ -67,6 +67,7 @@ class SponsorshipBenefit(models.Model):
        claimprompt = models.TextField(null=False, blank=True)
        benefit_class = models.IntegerField(null=True, blank=True, default=None, choices=benefit_choices)
        class_parameters = JSONField(blank=True, null=False)
+       tweet_template = models.TextField(null=False, blank=True)
 
        def __unicode__(self):
                return self.benefitname
index fd4a5a814ac67a96caae3a78526553a618d47d3c..ef40739f318e88a36d52ecc1b8972c63ba3875b2 100644 (file)
@@ -11,6 +11,8 @@ from datetime import datetime, timedelta
 from postgresqleu.auth import user_search, user_import
 
 from postgresqleu.confreg.models import Conference, PrepaidVoucher, DiscountCode
+from postgresqleu.confreg.models import ConferenceTweetQueue
+from postgresqleu.confreg.jinjafunc import render_sandboxed_template
 from postgresqleu.mailqueue.util import send_simple_mail
 from postgresqleu.util.storage import InlineEncodedStorage
 from postgresqleu.util.decorators import superuser_required
@@ -483,6 +485,16 @@ def _confirm_benefit(request, benefit):
                                                 sendername=conference.conferencename,
                                                 )
 
+               # Potentially send tweet
+               if benefit.benefit.tweet_template:
+                       ConferenceTweetQueue(conference=conference, datetime=datetime.now(),
+                                                                contents=render_sandboxed_template(benefit.benefit.tweet_template, {
+                                                                        'benefit': benefit.benefit,
+                                                                        'level': benefit.benefit.level,
+                                                                        'conference': conference,
+                                                                        'sponsor': benefit.sponsor
+                                                                })).save()
+
 @login_required
 def sponsor_admin_sponsor(request, confurlname, sponsorid):
        if request.user.is_superuser: