diff --git a/PyRIGS/decorators.py b/PyRIGS/decorators.py index 67d8964c..055901ca 100644 --- a/PyRIGS/decorators.py +++ b/PyRIGS/decorators.py @@ -2,23 +2,37 @@ from django.contrib.auth import REDIRECT_FIELD_NAME from django.shortcuts import render_to_response from django.template import RequestContext from django.http import HttpResponseRedirect +from django.core.urlresolvers import reverse -def user_passes_test_with_403(test_func, login_url=None): +from RIGS import models + + +def user_passes_test_with_403(test_func, login_url=None, oembed_view=None): """ Decorator for views that checks that the user passes the given test. - Anonymous users will be redirected to login_url, while users that fail the test will be given a 403 error. + If embed_view is set, then a JS redirect will be used, and a application/json+oembed + meta tag set with the url of oembed_view + (oembed_view will be passed the kwargs from the main function) """ if not login_url: from django.conf import settings login_url = settings.LOGIN_URL + def _dec(view_func): def _checklogin(request, *args, **kwargs): if test_func(request.user): return view_func(request, *args, **kwargs) elif not request.user.is_authenticated(): - return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path())) + if oembed_view is not None: + extra_context = {} + extra_context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs)) + extra_context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path()) + resp = render_to_response('login_redirect.html', extra_context, context_instance=RequestContext(request)) + return resp + else: + return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path())) else: resp = render_to_response('403.html', context_instance=RequestContext(request)) resp.status_code = 403 @@ -28,14 +42,14 @@ def user_passes_test_with_403(test_func, login_url=None): return _checklogin return _dec -def permission_required_with_403(perm, login_url=None): + +def permission_required_with_403(perm, login_url=None, oembed_view=None): """ Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page or rendering a 403 as necessary. """ - return user_passes_test_with_403(lambda u: u.has_perm(perm), login_url=login_url) + return user_passes_test_with_403(lambda u: u.has_perm(perm), login_url=login_url, oembed_view=oembed_view) -from RIGS import models def api_key_required(function): """ @@ -64,4 +78,4 @@ def api_key_required(function): if user_object.api_key != key: return error_resp return function(request, *args, **kwargs) - return wrap \ No newline at end of file + return wrap diff --git a/RIGS/rigboard.py b/RIGS/rigboard.py index a6ccde09..fc14a89e 100644 --- a/RIGS/rigboard.py +++ b/RIGS/rigboard.py @@ -9,11 +9,13 @@ from django.shortcuts import get_object_or_404 from django.template import RequestContext from django.template.loader import get_template from django.conf import settings +from django.core.urlresolvers import reverse from django.http import HttpResponse from django.db.models import Q from django.contrib import messages from z3c.rml import rml2pdf from PyPDF2 import PdfFileMerger, PdfFileReader +import simplejson from RIGS import models, forms import datetime @@ -47,6 +49,28 @@ class EventDetail(generic.DetailView): model = models.Event +class EventOembed(generic.View): + model = models.Event + + def get(self, request, pk=None): + + embed_url = reverse('event_embed', args=[pk]) + full_url = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], embed_url) + + data = { + 'html': ''.format(full_url), + 'version': '1.0', + 'type': 'rich', + } + + json = simplejson.JSONEncoderForHTML().encode(data) + return HttpResponse(json, content_type="application/json") + + +class EventEmbed(EventDetail): + template_name = 'RIGS/event_embed.html' + + class EventCreate(generic.CreateView): model = models.Event form_class = forms.EventForm @@ -59,7 +83,7 @@ class EventCreate(generic.CreateView): form = context['form'] if re.search('"-\d+"', form['items_json'].value()): messages.info(self.request, "Your item changes have been saved. Please fix the errors and save the event.") - + # Get some other objects to include in the form. Used when there are errors but also nice and quick. for field, model in form.related_models.iteritems(): diff --git a/RIGS/static/css/screen.css b/RIGS/static/css/screen.css index d7ed2f52..41bd5f39 100644 --- a/RIGS/static/css/screen.css +++ b/RIGS/static/css/screen.css @@ -16,4 +16,4 @@ * Portions copyright Addy Osmani, jQuery UI & Twitter Bootstrap * Created the LESS version by $dharapvj * Released under MIT - */.ui-tooltip{display:block;font-size:11px;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:0.8;position:absolute;visibility:visible;z-index:1070;max-width:200px;background:#000;border:1px solid #000;color:#fff;padding:3px 8px;text-align:center;text-decoration:none;-moz-box-shadow:inset 0 1px 0 #000;-webkit-box-shadow:inset 0 1px 0 #000;box-shadow:inset 0 1px 0 #000;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;border-width:1px}.ui-tooltip .arrow{overflow:hidden;position:absolute;margin-left:0;height:20px;width:20px}.ui-tooltip .arrow.bottom{top:100%;left:38%}.ui-tooltip .arrow.bottom:after{border-top:8px solid #000;border-right:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid transparent}.ui-tooltip .arrow.top{top:-50%;bottom:22px;left:42%}.ui-tooltip .arrow.top:after{border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #000;border-left:6px solid transparent}.ui-tooltip .arrow.left{top:25%;left:-15%;right:0;bottom:-16px}.ui-tooltip .arrow.left:after{width:0;border-top:6px solid transparent;border-right:6px solid #000;border-bottom:6px solid transparent;border-left:6px solid transparent}.ui-tooltip .arrow.right{top:26%;left:100%;right:0;bottom:-16px;margin-left:1px}.ui-tooltip .arrow.right:after{width:0;border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #000}.ui-tooltip .arrow:after{content:" ";position:absolute;height:0;left:0;top:0;width:0;margin-left:0;bottom:12px;box-shadow:6px 5px 9px -9px #000}body,.pad-top{padding-top:50px}#content{padding:40px 15px}#userdropdown>li{padding:0 0.3em}#userdropdown>li .media-object,#activity .media-object{max-width:3em}.table tbody>tr>td.vert-align{vertical-align:middle}.align-right{text-align:right}textarea{width:100%;resize:vertical}.btn-page,.btn-pad{margin:0 0 0.5em}.custom-combobox{display:block}.event-mic-photo{max-width:2em}.item-description{margin-left:1em}.overflow-ellipsis{text-overflow:ellipsis;display:inline-block;max-width:100%;overflow:hidden}.modal-dialog{z-index:inherit}.panel-default .default{background-color:#f5f5f5}del{background-color:#f2dede;border-radius:3px}ins{background-color:#dff0d8;border-radius:3px}.loading-animation{position:relative;margin:30px auto 0}.loading-animation .circle{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-right:5px solid transparent;border-left:5px solid transparent;border-radius:50px;box-shadow:0 0 35px #2187e7;width:50px;height:50px;margin:0 auto;-moz-animation:spinPulse 1s infinite ease-in-out;-webkit-animation:spinPulse 1s infinite ease-in-out;animation:spinPulse 1s infinite ease-in-out}.loading-animation .circle1{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-left:5px solid transparent;border-right:5px solid transparent;border-radius:50px;box-shadow:0 0 15px #2187e7;width:30px;height:30px;margin:0 auto;position:relative;top:-40px;-moz-animation:spinoffPulse 1s infinite linear;-webkit-animation:spinoffPulse 1s infinite linear;animation:spinoffPulse 1s infinite linear}@-moz-keyframes spinPulse{0%{-moz-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-webkit-keyframes spinPulse{0%{-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@keyframes spinPulse{0%{-moz-transform:rotate(160deg);-ms-transform:rotate(160deg);-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);-ms-transform:rotate(145deg);-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);-ms-transform:rotate(-320deg);-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-moz-keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinoffPulse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)}} + */.ui-tooltip{display:block;font-size:11px;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:0.8;position:absolute;visibility:visible;z-index:1070;max-width:200px;background:#000;border:1px solid #000;color:#fff;padding:3px 8px;text-align:center;text-decoration:none;-moz-box-shadow:inset 0 1px 0 #000;-webkit-box-shadow:inset 0 1px 0 #000;box-shadow:inset 0 1px 0 #000;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;border-width:1px}.ui-tooltip .arrow{overflow:hidden;position:absolute;margin-left:0;height:20px;width:20px}.ui-tooltip .arrow.bottom{top:100%;left:38%}.ui-tooltip .arrow.bottom:after{border-top:8px solid #000;border-right:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid transparent}.ui-tooltip .arrow.top{top:-50%;bottom:22px;left:42%}.ui-tooltip .arrow.top:after{border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #000;border-left:6px solid transparent}.ui-tooltip .arrow.left{top:25%;left:-15%;right:0;bottom:-16px}.ui-tooltip .arrow.left:after{width:0;border-top:6px solid transparent;border-right:6px solid #000;border-bottom:6px solid transparent;border-left:6px solid transparent}.ui-tooltip .arrow.right{top:26%;left:100%;right:0;bottom:-16px;margin-left:1px}.ui-tooltip .arrow.right:after{width:0;border-top:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #000}.ui-tooltip .arrow:after{content:" ";position:absolute;height:0;left:0;top:0;width:0;margin-left:0;bottom:12px;box-shadow:6px 5px 9px -9px #000}body,.pad-top{padding-top:50px}#content{padding:40px 15px}#userdropdown>li{padding:0 0.3em}#userdropdown>li .media-object,#activity .media-object{max-width:3em}.table tbody>tr>td.vert-align{vertical-align:middle}.align-right{text-align:right}textarea{width:100%;resize:vertical}.btn-page,.btn-pad{margin:0 0 0.5em}.custom-combobox{display:block}.event-mic-photo{max-width:2em}.item-description{margin-left:1em}.overflow-ellipsis{text-overflow:ellipsis;display:inline-block;max-width:100%;overflow:hidden}.modal-dialog{z-index:inherit}.panel-default .default{background-color:#f5f5f5}del{background-color:#f2dede;border-radius:3px}ins{background-color:#dff0d8;border-radius:3px}.loading-animation{position:relative;margin:30px auto 0}.loading-animation .circle{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-right:5px solid transparent;border-left:5px solid transparent;border-radius:50px;box-shadow:0 0 35px #2187e7;width:50px;height:50px;margin:0 auto;-moz-animation:spinPulse 1s infinite ease-in-out;-webkit-animation:spinPulse 1s infinite ease-in-out;animation:spinPulse 1s infinite ease-in-out}.loading-animation .circle1{background-color:transparent;border:5px solid rgba(0,183,229,0.9);opacity:.9;border-left:5px solid transparent;border-right:5px solid transparent;border-radius:50px;box-shadow:0 0 15px #2187e7;width:30px;height:30px;margin:0 auto;position:relative;top:-40px;-moz-animation:spinoffPulse 1s infinite linear;-webkit-animation:spinoffPulse 1s infinite linear;animation:spinoffPulse 1s infinite linear}@-moz-keyframes spinPulse{0%{-moz-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-webkit-keyframes spinPulse{0%{-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@keyframes spinPulse{0%{-moz-transform:rotate(160deg);-ms-transform:rotate(160deg);-webkit-transform:rotate(160deg);transform:rotate(160deg);opacity:0;box-shadow:0 0 1px #2187e7}50%{-moz-transform:rotate(145deg);-ms-transform:rotate(145deg);-webkit-transform:rotate(145deg);transform:rotate(145deg);opacity:1}100%{-moz-transform:rotate(-320deg);-ms-transform:rotate(-320deg);-webkit-transform:rotate(-320deg);transform:rotate(-320deg);opacity:0}}@-moz-keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinoffPulse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinoffPulse{0%{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)}}html.embedded{min-height:100%;display:table;width:100%}html.embedded body{padding:0;display:table-cell;vertical-align:middle;width:100%;background:none}html.embedded .embed_container{border:5px solid #e9e9e9;padding:12px 0px;min-height:100%;width:100%}html.embedded .source{background:url("/static/imgs/pyrigs-avatar.png") no-repeat;background-size:16px 16px;padding-left:20px;color:#000}html.embedded h3{margin-top:10px;margin-bottom:5px}html.embedded p{margin-bottom:2px;font-size:11px}html.embedded .event-mic-photo{max-width:3em} diff --git a/RIGS/static/scss/screen.scss b/RIGS/static/scss/screen.scss index be36ebbb..953027a9 100644 --- a/RIGS/static/scss/screen.scss +++ b/RIGS/static/scss/screen.scss @@ -147,3 +147,45 @@ ins { }; } } + +html.embedded{ + min-height:100%; + display: table; + width: 100%; + + body{ + padding:0; + display: table-cell; + vertical-align: middle; + width:100%; + background:none; + } + + .embed_container{ + border:5px solid #e9e9e9; + padding:12px 0px; + min-height:100%; + width:100%; + } + + .source{ + background: url('/static/imgs/pyrigs-avatar.png') no-repeat; + background-size: 16px 16px; + padding-left: 20px; + color: #000; + } + + h3{ + margin-top:10px; + margin-bottom:5px; + } + + p{ + margin-bottom:2px; + font-size: 11px; + } + + .event-mic-photo{ + max-width: 3em; + } +} diff --git a/RIGS/templates/RIGS/event_embed.html b/RIGS/templates/RIGS/event_embed.html new file mode 100644 index 00000000..a6e3e586 --- /dev/null +++ b/RIGS/templates/RIGS/event_embed.html @@ -0,0 +1,106 @@ +{% extends 'base_embed.html' %} +{% load static from staticfiles %} + +{% block content %} + +
+
+ + Rig Information Gathering System + +
+ +
+ + {% if object.mic %} +
+ +
+ {% elif object.is_rig %} + + {% endif %} +
+ +

+ + {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %} + | {{ object.name }} + {% if object.venue %} + at {{ object.venue }} + {% endif %} +
+ {{ object.start_date|date:"D d/m/Y" }} + {% if object.has_start_time %} + {{ object.start_time|date:"H:i" }} + {% endif %} + {% if object.end_date or object.has_end_time %} + – + {% endif %} + {% if object.end_date and object.end_date != object.start_date %} + {{ object.end_date|date:"D d/m/Y" }} + {% endif %} + {% if object.has_end_time %} + {{ object.end_time|date:"H:i" }} + {% endif %} + +

+ +
+
+

+ Status: + {{ object.get_status_display }} +

+

+ {% if object.is_rig %} + Client: {{ object.person.name }} + {% if object.organisation %} + for {{ object.organisation.name }} + {% endif %} + {% if object.dry_hire %}(Dry Hire){% endif %} + {% else %} + Non-Rig + {% endif %} +

+

+ MIC: + {% if object.mic %} + {{object.mic.name}} + {% else %} + None + {% endif %} +

+
+
+ + {% if object.meet_at %} +

+ Crew meet: + {{ object.meet_at|date:"H:i" }} {{ object.meet_at|date:"(Y-m-d)" }} +

+ {% endif %} + {% if object.access_at %} +

+ Access at: + {{ object.access_at|date:"H:i" }} {{ object.access_at|date:"(Y-m-d)" }} +

+ {% endif %} +

+ Last updated: + {{ object.last_edited_at }} by "{{ object.last_edited_by.initials }}" +

+
+
+ {% if object.description %} +

+ Description: + {{ object.description|linebreaksbr }} +

+ {% endif %} + + +
+
+ + +{% endblock %} diff --git a/RIGS/test_unit.py b/RIGS/test_unit.py index f0c778b3..82a7acca 100644 --- a/RIGS/test_unit.py +++ b/RIGS/test_unit.py @@ -213,6 +213,88 @@ class TestInvoiceDelete(TestCase): # Check this didn't work self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk)) + +class TestEmbeddedViews(TestCase): + @classmethod + def setUpTestData(cls): + cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True) + + cls.events = { + 1: models.Event.objects.create(name="TE E1", start_date=date.today()), + 2: models.Event.objects.create(name="TE E2", start_date=date.today()) + } + + cls.invoices = { + 1: models.Invoice.objects.create(event=cls.events[1]), + 2: models.Invoice.objects.create(event=cls.events[2]) + } + + cls.payments = { + 1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34, method=models.Payment.CASH) + } + + def setUp(self): + self.profile.set_password('testuser') + self.profile.save() + + def testLoginRedirect(self): + request_url = reverse('event_embed', kwargs={'pk': 1}) + expected_url = "{0}?next={1}".format(reverse('login_embed'), request_url) + + # Request the page and check it redirects + response = self.client.get(request_url, follow=True) + self.assertRedirects(response, expected_url, status_code=302, target_status_code=200) + + # Now login + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + # And check that it no longer redirects + response = self.client.get(request_url, follow=True) + self.assertEqual(len(response.redirect_chain), 0) + + def testLoginCookieWarning(self): + login_url = reverse('login_embed') + response = self.client.post(login_url, follow=True) + self.assertContains(response, "Cookies do not seem to be enabled") + + def testXFrameHeaders(self): + event_url = reverse('event_embed', kwargs={'pk': 1}) + login_url = reverse('login_embed') + + self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) + + response = self.client.get(event_url, follow=True) + with self.assertRaises(KeyError): + response._headers["X-Frame-Options"] + + response = self.client.get(login_url, follow=True) + with self.assertRaises(KeyError): + response._headers["X-Frame-Options"] + + def testOEmbed(self): + event_url = reverse('event_detail', kwargs={'pk': 1}) + event_embed_url = reverse('event_embed', kwargs={'pk': 1}) + oembed_url = reverse('event_oembed', kwargs={'pk': 1}) + + alt_oembed_url = reverse('event_oembed', kwargs={'pk': 999}) + alt_event_embed_url = reverse('event_embed', kwargs={'pk': 999}) + + # Test the meta tag is in place + response = self.client.get(event_url, follow=True, HTTP_HOST='example.com') + self.assertContains(response, '(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), url(r'^rigboard/calendar/(?P(month|week|day))/(?P(\d{4}-\d{2}-\d{2}))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')), + url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')), url(r'^rigboard/activity/$', permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()), name='activity_table'), @@ -80,8 +82,14 @@ urlpatterns = patterns('', name='activity_feed'), url(r'^event/(?P\d+)/$', - permission_required_with_403('RIGS.view_event')(rigboard.EventDetail.as_view()), + permission_required_with_403('RIGS.view_event', oembed_view="event_oembed")(rigboard.EventDetail.as_view()), name='event_detail'), + url(r'^event/(?P\d+)/embed/$', + xframe_options_exempt(login_required(login_url='/user/login/embed/')(rigboard.EventEmbed.as_view())), + name='event_embed'), + url(r'^event/(?P\d+)/oembed_json/$', + rigboard.EventOembed.as_view(), + name='event_oembed'), url(r'^event/(?P\d+)/print/$', permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()), name='event_print'), @@ -101,7 +109,7 @@ urlpatterns = patterns('', permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()), name='event_history', kwargs={'model': models.Event}), - + # Finance url(r'^invoice/$', @@ -140,10 +148,10 @@ urlpatterns = patterns('', # User editing url(r'^user/$', login_required(views.ProfileDetail.as_view()), name='profile_detail'), url(r'^user/(?P\d+)/$', - permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), - name='profile_detail'), + permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), + name='profile_detail'), url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()), - name='profile_update_self'), + name='profile_update_self'), url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'), # ICS Calendar - API key authentication @@ -154,8 +162,7 @@ urlpatterns = patterns('', url(r'^api/(?P\w+)/(?P\d+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), # Legacy URL's - url(r'^rig/show/(?P\d+)/$', RedirectView.as_view(permanent=True,pattern_name='event_detail')), - url(r'^bookings/$', RedirectView.as_view(permanent=True,pattern_name='rigboard')), - url(r'^bookings/past/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')), -) - + url(r'^rig/show/(?P\d+)/$', RedirectView.as_view(permanent=True, pattern_name='event_detail')), + url(r'^bookings/$', RedirectView.as_view(permanent=True, pattern_name='rigboard')), + url(r'^bookings/past/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')), + ) diff --git a/RIGS/views.py b/RIGS/views.py index c013ce1d..c0186bed 100644 --- a/RIGS/views.py +++ b/RIGS/views.py @@ -12,6 +12,8 @@ from django.contrib import messages import datetime, pytz import operator from registration.views import RegistrationView +from django.views.decorators.csrf import csrf_exempt + from RIGS import models, forms @@ -29,12 +31,37 @@ class Index(generic.TemplateView): def login(request, **kwargs): if request.user.is_authenticated(): next = request.REQUEST.get('next', '/') - return HttpResponseRedirect(request.REQUEST.get('next', '/')) + return HttpResponseRedirect(next) else: from django.contrib.auth.views import login return login(request) + +# This view should be exempt from requiring CSRF token. +# Then we can check for it and show a nice error +# Don't worry, django.contrib.auth.views.login will +# check for it before logging the user in +@csrf_exempt +def login_embed(request, **kwargs): + print("Running LOGIN") + if request.user.is_authenticated(): + next = request.REQUEST.get('next', '/') + return HttpResponseRedirect(next) + else: + from django.contrib.auth.views import login + + if request.method == "POST": + csrf_cookie = request.COOKIES.get('csrftoken', None) + + if csrf_cookie is None: + messages.warning(request, 'Cookies do not seem to be enabled. Try logging in using a new tab.') + request.method = 'GET' # Render the page without trying to login + + return login(request, template_name="registration/login_embed.html") + + + """ Called from a modal window (e.g. when an item is submitted to an event/invoice). May optionally also include some javascript in a success message to cause a load of diff --git a/templates/base_embed.html b/templates/base_embed.html new file mode 100644 index 00000000..6a7a8741 --- /dev/null +++ b/templates/base_embed.html @@ -0,0 +1,49 @@ +{% load static from staticfiles %} +{% load raven %} + + + + + + + + + + + + + + + + + + +{% include "analytics.html" %} + +
+
+ {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + + {% block content %} + {% endblock %} +
+
+ +{% block js %} +{% endblock %} + + diff --git a/templates/login_redirect.html b/templates/login_redirect.html new file mode 100644 index 00000000..d2dcb0b2 --- /dev/null +++ b/templates/login_redirect.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% block title %}Login Required{% endblock %} + +{% block js %} + +{% endblock %} + +{% block extra-head %} + {% if oembed_url %} + + {% endif %} +{% endblock %} + +{% block content %} +
+

Login is required for this page

+ Login +
+{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html index ace80d16..5dd85fe7 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -3,5 +3,8 @@ {% block title %}Login{% endblock %} {% block content %} +
+

Rig Information Gathering System

+
{% include 'registration/loginform.html' %} {% endblock %} \ No newline at end of file diff --git a/templates/registration/login_embed.html b/templates/registration/login_embed.html new file mode 100644 index 00000000..bddb7333 --- /dev/null +++ b/templates/registration/login_embed.html @@ -0,0 +1,34 @@ +{% extends 'base_embed.html' %} +{% load widget_tweaks %} + +{% block title %}Login{% endblock %} + +{% block content %} +
+

Rig Information Gathering System

+
+ + +{% include 'form_errors.html' %} + + +
+ +
{% csrf_token %} +
+ + {% render_field form.username class+="form-control" placeholder=form.username.label %} +
+
+ + {% render_field form.password class+="form-control" placeholder=form.password.label %} +
+
+ +
+ + +
+
+ +{% endblock %} diff --git a/templates/registration/loginform.html b/templates/registration/loginform.html index 74a3787b..ad7e33d3 100644 --- a/templates/registration/loginform.html +++ b/templates/registration/loginform.html @@ -3,7 +3,7 @@ {% include 'form_errors.html' %}
-
{% csrf_token %} + {% csrf_token %}
{% render_field form.username class+="form-control" placeholder=form.username.label autofocus="" %} @@ -12,9 +12,11 @@ {% render_field form.password class+="form-control" placeholder=form.password.label %}
- Register - Forgotten Password - - +
-
\ No newline at end of file +