summaryrefslogtreecommitdiff
path: root/postgresqleu/util/backendviews.py
diff options
context:
space:
mode:
authorMagnus Hagander2020-05-18 14:37:06 +0000
committerMagnus Hagander2020-05-18 14:37:06 +0000
commitda947f59e4b30671da0fd666cdf503b40c1fcf5c (patch)
tree3f8bb1ba55f90300328f9be62ecadde2c720ae05 /postgresqleu/util/backendviews.py
parentab6723636735c1fa8be9dfc07c56410a3ac0e644 (diff)
Complete overhaul of social media integrations
* Remove the hard-coded twitter implementation and replace it with an infrastructure for pluggable implementations. * Separate out "social media broadcasting" (public twitter posts) from "private notifications" (DMs) and invent "private broadcasting" (notifications sent only to attendees of a conference) and "organisation notifications" (sent to organisers). * Add the concept of a Messaging Provider that's configured on a Conference Series, which maps to a twitter account (or similar in other providers). Replace "incoming twitter active" flag on a conference with a setting on this messaging provider for "route messages to conference". This way the messaging doesn't have to be reconfigured for each new conference in a series, and we also automatically avoid the risk of having two conferences getting the same input. * For each conference in a series, the individual Messaging Providers can be enabled or disabled for the different functionality, and individual channels be configured when applicable. * Add implementations of Twitter (updated, social broadcasting and private messaging support), Mastodon (social broadcasting and private messaging) and Telegram (attendee boadcasts, private notifications, and organisation broadcasts) * Add webhook support for Twitter and Telegram, making for much faster reactions to incoming messages. * Hardcoded news twitter post accounts, and replaced with MessagingProviders per above that are not attached to a conference. * Add a daemon that listens to PostgreSQL notifications and sends out broadcasts and notifications for quicker action (if not enabled, a scheduled task will send them out every 10 minutes like before) * In making broadcast posts, add support for the fact that different providers have different max length of posts (e.g. Twitter currently has 280 and Mastodon 500), and also roughly account for the effects of URL shorterners on posts. * Add a button to registration dashboards to send DMs to attendees that have configured notification. * Send "private broadcasts" ahead of any talks to keep people posted of talks. For now this is always enabled if a channel is set up for private broadcasts, we may want to consider making it more configurable in the future. There are still a lot of tables and files referring Twitter in the tree, and some of those will be renamed in a future commit to make tracking of changes easier. Fixes #29 Fixes #13
Diffstat (limited to 'postgresqleu/util/backendviews.py')
-rw-r--r--postgresqleu/util/backendviews.py83
1 files changed, 81 insertions, 2 deletions
diff --git a/postgresqleu/util/backendviews.py b/postgresqleu/util/backendviews.py
index 9fad8e38..33e07356 100644
--- a/postgresqleu/util/backendviews.py
+++ b/postgresqleu/util/backendviews.py
@@ -11,6 +11,11 @@ from postgresqleu.util.lists import flatten_list
from postgresqleu.confreg.util import get_authenticated_conference
from postgresqleu.confreg.backendforms import BackendCopySelectConferenceForm
+from .models import OAuthApplication
+from .backendforms import BackendForm
+from .forms import SelectSetValueField
+from .oauthapps import oauth_application_choices, oauth_application_create
+
def backend_process_form(request, urlname, formclass, id, cancel_url='../', saved_url='../', allow_new=True, allow_delete=True, breadcrumbs=None, permissions_already_checked=False, conference=None, bypass_conference_filter=False, instancemaker=None, deleted_url=None, topadmin=None):
if not conference and not bypass_conference_filter:
@@ -160,7 +165,7 @@ def backend_process_form(request, urlname, formclass, id, cancel_url='../', save
form.pre_create_item()
form.save()
form._save_m2m()
- all_excludes = ['_validator', '_newformdata'] + form.readonly_fields
+ all_excludes = ['_validator', '_newformdata'] + list(form.readonly_fields) + form.nosave_fields
if form.json_form_fields:
for fn, ffields in form.json_form_fields.items():
all_excludes.extend(ffields)
@@ -170,7 +175,9 @@ def backend_process_form(request, urlname, formclass, id, cancel_url='../', save
# Merge fields stored in json
if form.json_form_fields:
for fn, ffields in form.json_form_fields.items():
- setattr(form.instance, fn, {fld: form.cleaned_data[fld] for fld in ffields})
+ d = getattr(form.instance, fn, {})
+ d.update({fld: form.cleaned_data[fld] for fld in ffields})
+ setattr(form.instance, fn, d)
form.instance.save(update_fields=form.json_form_fields.keys())
return HttpResponseRedirect(saved_url)
@@ -471,3 +478,75 @@ def backend_handle_copy_previous(request, formclass, restpieces, conference):
],
'helplink': formclass.helplink,
})
+
+
+#
+# Special direct views
+#
+class BackendOAuthappNewForm(forms.Form):
+ helplink = 'oauth'
+ apptype = forms.CharField() # Field type will be changed dynamically
+ baseurl = forms.URLField(label='Base URL')
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ # We have to set this dynamically, otherwise the system won't start up enough to
+ # run the migrations, for some reason.
+ self.fields['apptype'] = SelectSetValueField(choices=oauth_application_choices,
+ setvaluefield='baseurl', label='Type of application')
+
+ def get_newform_data(self):
+ return "{}:{}".format(self.cleaned_data['apptype'], self.cleaned_data['baseurl'])
+
+ def clean_baseurl(self):
+ b = self.cleaned_data['baseurl'].rstrip('/')
+ if OAuthApplication.objects.filter(baseurl=b).exists():
+ raise ValidationError("An OAuth provider with this base URL is already configured!")
+ return b
+
+
+class BackendOAuthappForm(BackendForm):
+ helplink = 'oauth'
+ list_fields = ['name', 'baseurl']
+ readonly_fields = ['name', 'baseurl']
+ form_before_new = BackendOAuthappNewForm
+
+ class Meta:
+ model = OAuthApplication
+ fields = ['name', 'baseurl', 'client', 'secret']
+
+ def fix_fields(self):
+ super().fix_fields()
+ if self.newformdata:
+ (name, baseurl) = self.newformdata.split(':', 1)
+ self.instance.name = name
+ self.initial['name'] = name
+ self.instance.baseurl = baseurl
+ self.initial['baseurl'] = baseurl
+ if self.request.method == 'POST' and '_validator' not in self.request.POST:
+ try:
+ (client, secret) = oauth_application_create(name, baseurl)
+ except Exception as e:
+ messages.error(self.request, str(e))
+ return
+ if client:
+ self.instance.client = client
+ self.initial['client'] = client
+ self.instance.secret = secret
+ self.initial['secret'] = secret
+ messages.info(self.request, "OAuth client and secret automaticaly created, just hit save!")
+
+
+def edit_oauthapps(request, rest):
+ if not request.user.is_superuser:
+ raise PermissionDenied("Access denied")
+
+ return backend_list_editor(request,
+ None,
+ BackendOAuthappForm,
+ rest,
+ bypass_conference_filter=True,
+ topadmin='OAuth',
+ return_url='/admin/',
+ )