Skip to content

Commit

Permalink
"Create forum post" button on a rig page (#551)
Browse files Browse the repository at this point in the history
* Add button for creating forum thread draft from event detail

TODO: Allow RIGS to ingest POST requests sent from the forum on new posts in RIG info to link up the forum thread

RE https://forum.nottinghamtec.co.uk/t/rigs-discourse-integration/15592/21

* Mockup webhook recieving view

* Correct method of CRSF exemption for webhook reciever

* Use f-strings correctly, not like a big dumb

* That was also dumb, fix that too

* Second shot at webhook reciever

* Oops

* >.<

* Third shot

* Try again at signing

* What if I gave it the right arguments. That might be a good start.

* More fiddling with auth

* Add debug print

* Okay, put that back where it was because I inavertently overloaded my import

Flashbacks to my java days...

* Different header access method

* Fix import, again

* Fix ommited json parsing wotsit

* Fix url

* Fix string index

* Correct template logic

* Allow manual adding/editing of URLs

* Filter by right flavour of event

* Amend event str conversion for consistency

* Oops

* Make migration

Will be squashed later

* Fix logic when creating events

* Squash migration

* Implement codedoctor suggestion
  • Loading branch information
FreneticScribbler authored Jun 27, 2023
1 parent d03a4e1 commit 1687407
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 5 deletions.
2 changes: 1 addition & 1 deletion RIGS/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Meta:
fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date',
'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic',
'person', 'organisation', 'dry_hire', 'checked_in_by', 'status',
'purchase_order', 'collector']
'purchase_order', 'collector', 'forum_url']


class BaseClientEventAuthorisationForm(forms.ModelForm):
Expand Down
19 changes: 19 additions & 0 deletions RIGS/migrations/0050_event_forum_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.19 on 2023-06-27 11:28

import RIGS.models
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('RIGS', '0049_auto_20230529_1123'),
]

operations = [
migrations.AddField(
model_name='event',
name='forum_url',
field=models.URLField(blank=True, default='', validators=[RIGS.models.validate_forum_url]),
),
]
12 changes: 11 additions & 1 deletion RIGS/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ def search(self, query=None):
return qs


def validate_forum_url(value):
if not value:
return # Required error is done the field
obj = urlparse(value)
if obj.hostname not in ('forum.nottinghamtec.co.uk'):
raise ValidationError('URL must point to a location on the TEC Forum')


@reversion.register(follow=['items'])
class Event(models.Model, RevisionMixin):
# Done to make it much nicer on the database
Expand Down Expand Up @@ -357,6 +365,8 @@ class Event(models.Model, RevisionMixin):
auth_request_at = models.DateTimeField(null=True, blank=True)
auth_request_to = models.EmailField(blank=True, default='')

forum_url = models.URLField(default='', blank=True, validators=[validate_forum_url])

@property
def display_id(self):
if self.pk:
Expand Down Expand Up @@ -505,7 +515,7 @@ def get_absolute_url(self):
return reverse('event_detail', kwargs={'pk': self.pk})

def __str__(self):
return f"{self.display_id}: {self.name}"
return f"{self.display_id} | {self.name}"

def clean(self):
errdict = {}
Expand Down
20 changes: 17 additions & 3 deletions RIGS/templates/event_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
<label for="{{ form.start_date.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.start_date.label }}</label>

<div class="col-sm-8">
<div class="col-sm-10">
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="Start date for event, required">
{% render_field form.start_date class+="form-control" %}
Expand All @@ -246,7 +246,7 @@
<label for="{{ form.end_date.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.end_date.label }}</label>

<div class="col-sm-8">
<div class="col-sm-10">
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="End date of event, leave blank if unknown or same as start date">
{% render_field form.end_date class+="form-control" %}
Expand Down Expand Up @@ -334,12 +334,26 @@

<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
<label for="{{ form.purchase_order.id_for_label }}"
class="col-sm-4 col-fitem_tableorm-label">{{ form.purchase_order.label }}</label>
class="col-sm-4 col-form-label">{{ form.purchase_order.label }}</label>

<div class="col-sm-8">
{% render_field form.purchase_order class+="form-control" %}
</div>
</div>

<div class="form-group" data-toggle="tooltip" title="The thread for this event on the TEC Forum">
<label for="{{ form.forum_url.id_for_label }}"
class="col-sm-4 col-form-label">Forum Thread</label>
<div class="col-sm-12">
<p class="small mb-0">Paste URL</p>
{% render_field form.forum_url class+="form-control" %}
{% if object.pk %}
<p class="small mb-0">or</p>
<a href="{% url 'event_thread' object.pk %}" class="btn btn-primary" title="Create Forum Thread">
<span class="fas fa-plus"></span> <span class="hidden-xs">Create Forum Thread</span></a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions RIGS/templates/partials/event_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@
<dt class="col-sm-6">PO</dt>
<dd class="col-sm-6">{{ object.purchase_order }}</dd>
{% endif %}

<dt class="col-6">Forum Thread</dt>
{% if object.forum_url %}
<dd class="col-6"><a href="{{object.forum_url}}">{{object.forum_url}}</a></dd>
{% else %}
<a href="{% url 'event_thread' object.pk %}" class="btn btn-primary" title="Create Forum Thread"><span
class="fas fa-plus"></span> <span
class="hidden-xs">Create Forum Thread</span></a>
{% endif %}
</dl>
</div>
</div>
3 changes: 3 additions & 0 deletions RIGS/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@
path('event/<int:pk>/checkin/add/', login_required(views.EventCheckInOverride.as_view()),
name='event_checkin_override'),

path('event/<int:pk>/thread/', permission_required_with_403('RIGS.change_event')(views.CreateForumThread.as_view()), name='event_thread'),
path('event/webhook/', views.RecieveForumWebhook.as_view(), name='webhook_recieve'),

# Finance
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),
name='invoice_list'),
Expand Down
45 changes: 45 additions & 0 deletions RIGS/views/rigboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import re
import premailer
import simplejson
import urllib
import hmac
import hashlib

from envparse import env
from bs4 import BeautifulSoup

from django.conf import settings
from django.contrib import messages
Expand All @@ -19,6 +25,7 @@
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views import generic
from django.views.decorators.csrf import csrf_exempt

from PyRIGS import decorators
from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin, PrintView, get_related
Expand Down Expand Up @@ -377,3 +384,41 @@ def get_context_data(self, **kwargs):
context['to_name'] = self.request.GET.get('to_name', None)
context['target'] = 'event_authorise_form_preview'
return context


class CreateForumThread(generic.base.RedirectView):
permanent = False

def get_redirect_url(self, *args, **kwargs):
event = get_object_or_404(models.Event, pk=kwargs['pk'])

if event.forum_url:
return event.forum_url

params = {
'title': str(event),
'body': f'https://rigs.nottinghamtec.co.uk/event/{event.pk}',
'category': 'rig-info'
}
return f'https://forum.nottinghamtec.co.uk/new-topic?{urllib.parse.urlencode(params)}'


class RecieveForumWebhook(generic.View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

def post(self, request, *args, **kwargs):
computed = f"sha256={hmac.new(env('FORUM_WEBHOOK_SECRET').encode(), request.body, hashlib.sha256).hexdigest()}"
if not hmac.compare_digest(request.headers.get('X-Discourse-Event-Signature'), computed):
return HttpResponseForbidden('Invalid signature header')
# Check if this is the right kind of event. The webhook filters by category on the forum side
if request.headers.get('X-Discourse-Event') == "topic_created":
body = simplejson.loads(request.body.decode('utf-8'))
event_id = int(body['topic']['title'][1:6]) # find the ID, force convert it to an int to eliminate leading zeros
event = models.Event.objects.filter(pk=event_id).first()
if event:
event.forum_url = f"https://forum.nottinghamtec.co.uk/t/{body['topic']['slug']}"
event.save()
return HttpResponse(status=202)
return HttpResponse(status=204)

0 comments on commit 1687407

Please sign in to comment.