diff --git a/src/bornhack/settings.py b/src/bornhack/settings.py index 91590962..67359fed 100644 --- a/src/bornhack/settings.py +++ b/src/bornhack/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = [ 'backoffice', 'events', 'rideshare', + 'tokens', 'allauth', 'allauth.account', diff --git a/src/bornhack/urls.py b/src/bornhack/urls.py index fb665d83..cd2907c6 100644 --- a/src/bornhack/urls.py +++ b/src/bornhack/urls.py @@ -70,6 +70,11 @@ urlpatterns = [ name='camp_list' ), + path( + 'token/', + include('tokens.urls', namespace='tokens'), + ), + # camp redirect views here path( diff --git a/src/profiles/templates/profile_base_buttons.html b/src/profiles/templates/profile_base_buttons.html index 47f2a697..75e184e9 100644 --- a/src/profiles/templates/profile_base_buttons.html +++ b/src/profiles/templates/profile_base_buttons.html @@ -7,7 +7,7 @@ Manage emails - {% if user.is_authenticated and user.orders.exists %} + {% if user.orders.exists %} Orders @@ -20,6 +20,9 @@ {% endif %} {% endif %} + + Secret Tokens + Logout diff --git a/src/tokens/__init__.py b/src/tokens/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tokens/admin.py b/src/tokens/admin.py new file mode 100644 index 00000000..a13a67a4 --- /dev/null +++ b/src/tokens/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin +from .models import Token, TokenFind + +@admin.register(Token) +class InfoCategorydmin(admin.ModelAdmin): + list_filter = ['camp',] + list_display = ['token', 'description', 'camp'] + search_fields = ['token', 'description'] + + +@admin.register(TokenFind) +class InfoCategorydmin(admin.ModelAdmin): + list_filter = ['token__camp', 'user'] + list_display = ['token', 'user', 'created'] + search_fields = ['user', 'token'] + diff --git a/src/tokens/apps.py b/src/tokens/apps.py new file mode 100644 index 00000000..440d98a8 --- /dev/null +++ b/src/tokens/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TokensConfig(AppConfig): + name = 'tokens' diff --git a/src/tokens/migrations/0001_initial.py b/src/tokens/migrations/0001_initial.py new file mode 100644 index 00000000..52954c3c --- /dev/null +++ b/src/tokens/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 2.0.4 on 2018-08-17 16:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('camps', '0029_auto_20180815_2018'), + ] + + operations = [ + migrations.CreateModel( + name='Token', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('token', models.CharField(help_text='The secret token', max_length=32)), + ('description', models.TextField(help_text='The description of the token')), + ('camp', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='camps.Camp')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/src/tokens/migrations/0002_tokenfind.py b/src/tokens/migrations/0002_tokenfind.py new file mode 100644 index 00000000..ea7f6fa3 --- /dev/null +++ b/src/tokens/migrations/0002_tokenfind.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.4 on 2018-08-18 12:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('tokens', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='TokenFind', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('token', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tokens.Token')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/src/tokens/migrations/__init__.py b/src/tokens/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tokens/models.py b/src/tokens/models.py new file mode 100644 index 00000000..c496be63 --- /dev/null +++ b/src/tokens/models.py @@ -0,0 +1,45 @@ +from django.db import models +from utils.models import CampRelatedModel + + +class Token(CampRelatedModel): + camp = models.ForeignKey( + 'camps.Camp', + on_delete=models.PROTECT + ) + + token = models.CharField( + max_length=32, + help_text="The secret token" + ) + + description = models.TextField( + help_text="The description of the token" + ) + + camp_filter = 'camp' + + def __str__(self): + return '%s (%s)' % (self.description, self.camp) + + +class TokenFind(CampRelatedModel): + token = models.ForeignKey( + 'tokens.Token', + on_delete=models.PROTECT + ) + + user = models.ForeignKey( + 'auth.User', + on_delete=models.PROTECT, + ) + + camp_filter = 'token__camp' + + def __str__(self): + return '%s found by %s' % (self.token, self.user) + + @property + def camp(self): + return self.token.camp + diff --git a/src/tokens/templates/token_detail.html b/src/tokens/templates/token_detail.html new file mode 100644 index 00000000..bec2867a --- /dev/null +++ b/src/tokens/templates/token_detail.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load static from staticfiles %} +{% load commonmark %}$ + +{% block title %} +Secret Token Found! | {{ block.super }} +{% endblock %} + +{% block content %} +

Secret Token Found!

+

You found a secret token:

+

{{ token.description }}

+

Your visit has been registered! Keep hunting, there might be more tokens out there.

+

List All Tokens

+{% endblock %} + diff --git a/src/tokens/templates/tokenfind_list.html b/src/tokens/templates/tokenfind_list.html new file mode 100644 index 00000000..67a3019d --- /dev/null +++ b/src/tokens/templates/tokenfind_list.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} +{% load static from staticfiles %} +{% load commonmark %}$ + +{% block title %} +Your Secret Tokens | {{ block.super }} +{% endblock %} + +{% block content %} +

Your Secret Tokens

+{% if object_list %} +

You have found the following secret tokens in the BornHack Secret Token Game:

+ + + + + + + + + + + {% for tokenfind in object_list %} + + + + + + + {% endfor %} + +
CampTokenDescriptionFound
{{ tokenfind.token.camp.title }}{{ tokenfind.token.token }}{{ tokenfind.token.description }}{{ tokenfind.created }}
+{% else %} +

You haven't found any secret tokens yet. Happy hunting!

+{% endif %} +{% endblock %} + diff --git a/src/tokens/urls.py b/src/tokens/urls.py new file mode 100644 index 00000000..1f06ff6d --- /dev/null +++ b/src/tokens/urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path, include +from .views import TokenDetailView, TokenFindListView + +app_name = 'tokens' + +urlpatterns = [ + path( + '', + TokenFindListView.as_view(), + name='tokenfind_list' + ), + re_path( + '(?P[0-9a-zA-Z\.@]+)/$', + TokenDetailView.as_view(), + name='details' + ), +] + diff --git a/src/tokens/views.py b/src/tokens/views.py new file mode 100644 index 00000000..0723c4b3 --- /dev/null +++ b/src/tokens/views.py @@ -0,0 +1,27 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.views.generic import ListView, DetailView + +from .models import Token, TokenFind + +class TokenDetailView(LoginRequiredMixin, DetailView): + template_name = "token_detail.html" + model = Token + slug_field = 'token' + slug_url_kwarg = 'token' + + def get(self, request, *args, **kwargs): + # register this tokenview if it isn't already + token, created = TokenFind.objects.get_or_create( + token=self.get_object(), + user=request.user + ) + return super().get(request, *args, **kwargs) + + +class TokenFindListView(LoginRequiredMixin, ListView): + template_name = "tokenfind_list.html" + model = TokenFind + + def get_queryset(self): + return TokenFind.objects.filter(user=self.request.user) +