proof that I did something during 33c3
|
@ -72,7 +72,7 @@ TEMPLATES = [
|
|||
'django.contrib.messages.context_processors.messages',
|
||||
'shop.context_processors.current_order',
|
||||
'shop.context_processors.user_has_tickets',
|
||||
'camps.context_processors.camps',
|
||||
'camps.context_processors.camp',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
132
bornhack/urls.py
|
@ -11,8 +11,10 @@ from django.contrib import admin
|
|||
from django.views.generic import TemplateView, RedirectView
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from camps.views import *
|
||||
from info.views import CampInfoView
|
||||
|
||||
from info.views import *
|
||||
from villages.views import *
|
||||
from program.views import *
|
||||
from sponsors.views import *
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
|
@ -27,24 +29,11 @@ urlpatterns = [
|
|||
r'^news/',
|
||||
include('news.urls', namespace='news')
|
||||
),
|
||||
url(
|
||||
r'^villages/',
|
||||
include('villages.urls', namespace='villages')
|
||||
),
|
||||
url(
|
||||
r'^schedule/',
|
||||
include('program.urls', namespace='schedule')
|
||||
),
|
||||
url(
|
||||
r'^$',
|
||||
TemplateView.as_view(template_name='frontpage.html'),
|
||||
name='frontpage'
|
||||
),
|
||||
url(
|
||||
r'^info/',
|
||||
TemplateView.as_view(template_name='info.html'),
|
||||
name='info'
|
||||
),
|
||||
url(
|
||||
r'^contact/',
|
||||
TemplateView.as_view(template_name='contact.html'),
|
||||
|
@ -90,54 +79,91 @@ urlpatterns = [
|
|||
|
||||
# camp specific urls below here
|
||||
|
||||
url(r'(?P<camp_slug>[-_\w+]+)/', include([
|
||||
url(
|
||||
r'^$',
|
||||
CampDetailView.as_view(),
|
||||
name='camp_detail'
|
||||
),
|
||||
url(
|
||||
r'^info/$',
|
||||
CampInfoView.as_view(),
|
||||
name='info'
|
||||
),
|
||||
url(
|
||||
r'^schedule/$',
|
||||
CampScheduleView.as_view(),
|
||||
name='schedule'
|
||||
),
|
||||
url(
|
||||
r'^sponsors/$',
|
||||
CampSponsorView.as_view(),
|
||||
name='camp_sponsors'
|
||||
),
|
||||
url(r'^villages/$', include([
|
||||
url(
|
||||
r'(?P<camp_slug>[-_\w+]+)/', include([
|
||||
url(
|
||||
r'^$',
|
||||
VillageListView.as_view(),
|
||||
name='village_list'
|
||||
CampDetailView.as_view(),
|
||||
name='camp_detail'
|
||||
),
|
||||
|
||||
url(
|
||||
r'create/$',
|
||||
VillageCreateView.as_view(),
|
||||
name='village_create'
|
||||
r'^info/$',
|
||||
CampInfoView.as_view(),
|
||||
name='info'
|
||||
),
|
||||
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/delete/$',
|
||||
VillageDeleteView.as_view(),
|
||||
name='village_delete'
|
||||
r'^schedule/', include([
|
||||
url(
|
||||
r'^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/$',
|
||||
ProgramDayView.as_view(),
|
||||
name='schedule_day'
|
||||
),
|
||||
url(
|
||||
r'^$',
|
||||
ProgramOverviewView.as_view(),
|
||||
name='schedule_index'
|
||||
),
|
||||
url(
|
||||
r'^speakers/$',
|
||||
SpeakerListView.as_view(),
|
||||
name='speaker_index'
|
||||
),
|
||||
url(
|
||||
r'^speakers/(?P<slug>[-_\w+]+)/$',
|
||||
SpeakerDetailView.as_view(),
|
||||
name='speaker_detail'
|
||||
),
|
||||
url(
|
||||
r'^events/$',
|
||||
EventListView.as_view(),
|
||||
name='event_index'
|
||||
),
|
||||
url(
|
||||
r'^(?P<slug>[-_\w+]+)/$',
|
||||
EventDetailView.as_view(),
|
||||
name='event'
|
||||
),
|
||||
])
|
||||
),
|
||||
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/edit/$',
|
||||
VillageUpdateView.as_view(),
|
||||
name='village_update'
|
||||
r'^sponsors/$',
|
||||
SponsorIndexView.as_view(),
|
||||
name='sponsors'
|
||||
),
|
||||
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/$',
|
||||
VillageDetailView.as_view(),
|
||||
name='village_detail'
|
||||
r'^villages/', include([
|
||||
url(
|
||||
r'^$',
|
||||
VillageListView.as_view(),
|
||||
name='village_list'
|
||||
),
|
||||
url(
|
||||
r'create/$',
|
||||
VillageCreateView.as_view(),
|
||||
name='village_create'
|
||||
),
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/delete/$',
|
||||
VillageDeleteView.as_view(),
|
||||
name='village_delete'
|
||||
),
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/edit/$',
|
||||
VillageUpdateView.as_view(),
|
||||
name='village_update'
|
||||
),
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/$',
|
||||
VillageDetailView.as_view(),
|
||||
name='village_detail'
|
||||
),
|
||||
])
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -3,9 +3,16 @@ from .models import Camp
|
|||
from django.utils import timezone
|
||||
|
||||
|
||||
def camps(request):
|
||||
def camp(request):
|
||||
if 'camp_slug' in request.resolver_match.kwargs:
|
||||
camp = Camp.objects.get(slug=request.resolver_match.kwargs['camp_slug'])
|
||||
request.session['campslug'] = camp.slug
|
||||
else:
|
||||
request.session['campslug'] = None
|
||||
camp = None
|
||||
|
||||
return {
|
||||
'upcoming_camps': Camp.objects.filter(camp_start__gt=timezone.now()),
|
||||
'previous_camps': Camp.objects.filter(camp_start__lt=timezone.now()),
|
||||
'camps': Camp.objects.all().order_by('-camp_start'),
|
||||
'camp': camp
|
||||
}
|
||||
|
||||
|
|
27
camps/migrations/0011_auto_20161228_1750.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-28 17:50
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0010_auto_20161220_1714'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='logo_large',
|
||||
field=models.CharField(default='', help_text=b'The filename of the large logo to use on the frontpage of this camp', max_length=100, verbose_name=b'Large logo for this camp'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='logo_small',
|
||||
field=models.CharField(default='', help_text=b'The filename of the small logo to use in the top of the page for this camp', max_length=100, verbose_name=b'Small logo for this camp'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
23
camps/migrations/0012_auto_20161228_2312.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-28 23:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0011_auto_20161228_1750'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='logo_large',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='logo_small',
|
||||
),
|
||||
]
|
|
@ -1,13 +1,14 @@
|
|||
from django.views.generic.detail import SingleObjectMixin
|
||||
from camps.models import Camp
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
|
||||
class CampViewMixin(Object):
|
||||
class CampViewMixin(object):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.camp = get_object_or_404(Camp, slug=self.kwargs.camp_slug)
|
||||
self.camp = get_object_or_404(Camp, slug=self.kwargs['camp_slug'])
|
||||
return super(CampViewMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.objects.filter(camp=self.camp)
|
||||
queryset = super(CampViewMixin, self).get_queryset()
|
||||
return queryset.filter(camp=self.camp)
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from django.contrib import admin
|
||||
from .models import *
|
||||
|
||||
admin.site.register(InfoCategory)
|
||||
admin.site.register(InfoItem)
|
||||
|
||||
# Register your models here.
|
||||
|
|
29
info/migrations/0002_auto_20161228_2312.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-28 23:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('info', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='infocategory',
|
||||
options={'ordering': ['-weight', 'headline'], 'verbose_name_plural': 'Info Categories'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='infocategory',
|
||||
name='weight',
|
||||
field=models.PositiveIntegerField(default=100, help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='infoitem',
|
||||
name='weight',
|
||||
field=models.PositiveIntegerField(default=100, help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.'),
|
||||
),
|
||||
]
|
|
@ -9,6 +9,7 @@ class InfoCategory(CreatedUpdatedModel):
|
|||
class Meta:
|
||||
ordering = ['-weight', 'headline']
|
||||
unique_together = (('anchor', 'camp'), ('headline', 'camp'))
|
||||
verbose_name_plural = "Info Categories"
|
||||
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
|
@ -26,14 +27,18 @@ class InfoCategory(CreatedUpdatedModel):
|
|||
)
|
||||
|
||||
weight = models.PositiveIntegerField(
|
||||
help_text = 'Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically.'
|
||||
help_text = 'Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.',
|
||||
default = 100,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if InfoItem.objects.filter(camp=self.camp, anchor=self.anchor).exists():
|
||||
if InfoItem.objects.filter(category__camp=self.camp, anchor=self.anchor).exists():
|
||||
# this anchor is already in use on an item, so it cannot be used (must be unique on the page)
|
||||
raise ValidationError({'anchor': 'Anchor is already in use on an info item for this camp'})
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (self.headline, self.camp)
|
||||
|
||||
|
||||
class InfoItem(CreatedUpdatedModel):
|
||||
class Meta:
|
||||
|
@ -60,11 +65,12 @@ class InfoItem(CreatedUpdatedModel):
|
|||
)
|
||||
|
||||
weight = models.PositiveIntegerField(
|
||||
help_text = 'Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically.'
|
||||
help_text = 'Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.',
|
||||
default = 100,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if InfoCategory.objects.filter(camp=self.camp, anchor=self.anchor).exists():
|
||||
if InfoCategory.objects.filter(camp=self.category.camp, anchor=self.anchor).exists():
|
||||
# this anchor is already in use on a category, so it cannot be used here (they must be unique on the entire page)
|
||||
raise ValidationError({'anchor': 'Anchor is already in use on an info category for this camp'})
|
||||
|
||||
|
|
|
@ -26,60 +26,45 @@ Info | {{ block.super }}
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Table of Contents</h2>
|
||||
{% if categories %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Table of Contents</h3>
|
||||
<p class="list-group">
|
||||
{% for category in categories %}
|
||||
<a href="#{{ category.anchor }}" class="list-group-item">{{ category.headline }}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for category in categories %}
|
||||
<span class="anchor" id="{{ category.anchor }}"></span>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>When is BornHack happening?</h2>
|
||||
<div class="panel-group">
|
||||
{% for item in category.infoitems %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<span class="sub-anchor" id="{{ item.anchor }}"></span>
|
||||
<h4 class="panel-title">
|
||||
{{ item.title }}
|
||||
<a href="#{{ item.anchor }}">
|
||||
<i class="glyphicon glyphicon-link"></i>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>{{ item.body }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<script src="{% static 'js/leaflet.js' %}"></script>
|
||||
<script>
|
||||
var map = L.map('map', {center: [55.131520, 14.903000], zoom: 10});
|
||||
|
||||
L.tileLayer(
|
||||
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
{
|
||||
attribution: '© <a href="http://osm.org/copyright" target="_blank">OpenStreetMap</a> contributors',
|
||||
}
|
||||
).addTo(map);
|
||||
|
||||
var camp_latlong = [55.011520, 14.975360];
|
||||
L.marker(camp_latlong).addTo(map);
|
||||
|
||||
L.marker(camp_latlong).addTo(map)
|
||||
.bindPopup('<strong>Coordinates:</strong><br>55.011520, 14.975360<br><strong>Address:</strong><br>Baunevej 11, 3720 Aakirkeby')
|
||||
.openPopup();
|
||||
</script>
|
||||
{% for category in categories %}
|
||||
<span class="anchor" id="{{ category.anchor }}"></span>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ category.title }}</h2>
|
||||
<div class="panel-group">
|
||||
{% for item in category.infoitems.all %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<span class="sub-anchor" id="{{ item.anchor }}"></span>
|
||||
<h4 class="panel-title">{{ item.headline }}
|
||||
<a href="#{{ item.anchor }}">
|
||||
<i class="glyphicon glyphicon-link"></i>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>{{ item.body }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<h3>No info found for {{ camp.title }}</h3>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -2,16 +2,15 @@ from django.shortcuts import render
|
|||
from django.views.generic import ListView, DetailView
|
||||
from django.utils import timezone
|
||||
from .models import *
|
||||
from camps.mixins import CampViewMixin
|
||||
|
||||
|
||||
class CampInfoView(ListView):
|
||||
class CampInfoView(CampViewMixin, ListView):
|
||||
model = InfoCategory
|
||||
template_name = 'info.html'
|
||||
context_object_name = 'categories'
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
return InfoCategory.objects.filter(
|
||||
camp__slug=self.kwargs['camp_slug']
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super(CampInfoView, self).get_queryset()
|
||||
# do not show categories with 0 items
|
||||
return queryset.exclude(infoitems__isnull=True)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from collections import OrderedDict
|
|||
|
||||
import datetime
|
||||
from django.views.generic import ListView, TemplateView, DetailView
|
||||
from camp.mixins import CampViewMixin
|
||||
from camps.mixins import CampViewMixin
|
||||
|
||||
from . import models
|
||||
|
||||
|
|
0
sponsors/__init__.py
Normal file
3
sponsors/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
7
sponsors/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SponsorsConfig(AppConfig):
|
||||
name = 'sponsors'
|
0
sponsors/migrations/__init__.py
Normal file
5
sponsors/models.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
3
sponsors/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
9
sponsors/views.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.views.generic import TemplateView
|
||||
from camps.mixins import CampViewMixin
|
||||
|
||||
|
||||
class SponsorIndexView(CampViewMixin, TemplateView):
|
||||
def get_template_name(self):
|
||||
return '%s-sponsors.html' % self.camp.slug
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
body {
|
||||
margin-top: 90px;
|
||||
margin-top: 85px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
|
@ -42,23 +42,7 @@ a, a:active, a:focus {
|
|||
}
|
||||
|
||||
.navbar-fixed-top {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.nav li a {
|
||||
padding: 30px 15px;
|
||||
}
|
||||
.nav {
|
||||
float: right!important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.nav li {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
.navbar-toggle .icon-bar {
|
||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
BIN
static_src/img/bornhack-2016/logo-new.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
BIN
static_src/img/bornhack-2016/logo.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
BIN
static_src/img/logo-small.png
Normal file
After Width: | Height: | Size: 4 KiB |
|
@ -1,5 +1,7 @@
|
|||
{% load static from staticfiles %}
|
||||
{% load bootstrap3 %}
|
||||
{% static "" as baseurl %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -20,7 +22,6 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
|
@ -31,45 +32,50 @@
|
|||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="{% static 'img/logo-new.png' %}" class="img-responsive" />
|
||||
{% if request.resolver_match.kwargs.camp_slug %}
|
||||
<img src="{{ baseurl }}/img/{{ camp.slug }}/{{ camp.slug }}-logo-small.png" class="img-responsive" />
|
||||
{% else %}
|
||||
<img src="{% static 'img/logo-small.png' %}" class="img-responsive" />
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{% url 'news:index' %}">News</a></li>
|
||||
<li><a href="{% url 'shop:index' %}">Shop</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Camps <span class="caret"></span></a>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ camp.title|default:"Camps" }}<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for camp in upcoming_camps %}
|
||||
<li><a href="{% url 'camp_detail' camp_slug=camp.slug %}">{{ camp.title }}</a></li>
|
||||
{% endfor %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% for camp in previous_camps %}
|
||||
<li><a href="{% url 'camp_detail' camp_slug=camp.slug %}">{{ camp.title }}</a></li>
|
||||
{% for camp in camps %}
|
||||
<li><a href="{% url 'camp_detail' camp_slug=camp.slug %}">{{ camp.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{# only include camp specific menu items when relevant #}
|
||||
{% if request.resolver_match.kwargs.camp_slug %}
|
||||
<li><a href="{% url 'camp_info' camp_slug=camp.slug %}">Info</a></li>
|
||||
<li><a href="{% url 'camp_villages' camp_slug=camp.slug %}">Villages</a></li>
|
||||
<li><a href="{% url 'camp_schedule' camp_slug=camp.slug %}">Schedule</a></li>
|
||||
<li><a href="{% url 'camp_sponsors' camp_slug=camp.slug %}">Sponsors</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'contact' %}">Contact</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="{% url 'profiles:detail' %}">Profile</a></li>
|
||||
<li><a href="{% url 'profiles:detail' %}">Account</a></li>
|
||||
{% else %}
|
||||
<li><a href="{% url 'account_login' %}">Login</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="main" class="container container-fluid">
|
||||
{% if camp %}
|
||||
<div class="row">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<a class="btn {% if request.resolver_match.url_name == "camp_detail" %}btn-primary{% else %}btn-default{% endif %}" href="{% url 'camp_detail' camp_slug=camp.slug %}">{{ camp.title }}</a>
|
||||
<a class="btn {% if request.resolver_match.url_name == "info" %}btn-primary{% else %}btn-default{% endif %}" href="{% url 'info' camp_slug=camp.slug %}">Info</a></li>
|
||||
<a class="btn {% if request.resolver_match.url_name == "village_list" %}btn-primary{% else %}btn-default{% endif %}" href="{% url 'village_list' camp_slug=camp.slug %}">Villages</a>
|
||||
<a class="btn {% if request.resolver_match.url_name == "schedule_index" %}btn-primary{% else %}btn-default{% endif %}" href="{% url 'schedule_index' camp_slug=camp.slug %}">Schedule</a>
|
||||
<a class="btn {% if request.resolver_match.url_name == "sponsors" %}btn-primary{% else %}btn-default{% endif %}" href="{% url 'sponsors' camp_slug=camp.slug %}">Sponsors</a>
|
||||
</div>
|
||||
<p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_messages %}
|
||||
{% block content %}{% endblock %}
|
||||
<footer class="row">
|
||||
|
@ -78,6 +84,7 @@
|
|||
<a href="{% url 'general-terms' %}">General Terms & Conditions</a> |
|
||||
<a href="{% url 'conduct' %}">Code of Conduct</a> |
|
||||
<a href="{% url 'privacy-policy' %}">Privacy Policy</a>
|
||||
<a href="{% url 'contact' %}">Contact</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import uuid
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ class CleanedModel(models.Model):
|
|||
print(message)
|
||||
# dont save, just return
|
||||
return
|
||||
super(CreatedUpdatedModel, self).save(**kwargs)
|
||||
super(CleanedModel, self).save(**kwargs)
|
||||
|
||||
|
||||
class UUIDModel(CleanedModel):
|
||||
|
|
22
villages/migrations/0007_village_camp.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-28 22:08
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0011_auto_20161228_1750'),
|
||||
('villages', '0006_remove_village_camp'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='village',
|
||||
name='camp',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='camps.Camp'),
|
||||
),
|
||||
]
|
21
villages/migrations/0008_auto_20161228_2209.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-28 22:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('villages', '0007_village_camp'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='village',
|
||||
name='camp',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp'),
|
||||
),
|
||||
]
|
|
@ -15,7 +15,7 @@ class Village(CreatedUpdatedModel, UUIDModel):
|
|||
ordering = ['name']
|
||||
|
||||
contact = models.ForeignKey('auth.User')
|
||||
|
||||
camp = models.ForeignKey('camps.Camp')
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255, blank=True)
|
||||
description = models.TextField(
|
||||
|
@ -37,7 +37,7 @@ class Village(CreatedUpdatedModel, UUIDModel):
|
|||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('villages:detail', kwargs={'slug': self.slug})
|
||||
return reverse_lazy('village_detail', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug})
|
||||
|
||||
def save(self, **kwargs):
|
||||
if (
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
<button type="submit" class="btn btn-danger form-control">Confirm</button>
|
||||
<br />
|
||||
<br />
|
||||
<a href="{% url 'villages:detail' slug=village.slug %}" class="btn btn-default form-control">Cancel</a>
|
||||
<a href="{% url 'village_detail' slug=village.slug %}" class="btn btn-default form-control">Cancel</a>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,8 +13,8 @@ Village: {{ village.name }} | {{ block.super }}
|
|||
|
||||
{% if user == village.contact %}
|
||||
<hr />
|
||||
<a href="{% url 'villages:update' slug=village.slug %}" class="btn btn-primary">Edit</a>
|
||||
<a href="{% url 'villages:delete' slug=village.slug %}" class="btn btn-danger">Delete</a>
|
||||
<a href="{% url 'village_update' camp_slug=village.camp.slug slug=village.slug %}" class="btn btn-primary">Edit</a>
|
||||
<a href="{% url 'village_delete' camp_slug=village.camp.slug slug=village.slug %}" class="btn btn-danger">Delete</a>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,4 +13,4 @@
|
|||
<button class="btn btn-primary pull-right" type="submit">Save</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,7 +6,7 @@ Villages | {{ block.super }}
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>Villages</h2>
|
||||
<p>
|
||||
If this is your first hackercamp the term 'Village' might be confusing but it
|
||||
is fairly simple: a village is just a spot on the campsite where you and a
|
||||
|
@ -16,17 +16,15 @@ Villages | {{ block.super }}
|
|||
</p>
|
||||
|
||||
<p class="lead">
|
||||
<a href="https://bornhack.dk/shop/?category=villages">
|
||||
It is also possible to rent a tent, chairs and tables for villages here.
|
||||
</a>
|
||||
It is also possible to rent a tent, chairs and tables in the shop!
|
||||
</p>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'villages:create' %}" class="btn btn-primary">Create a village</a>
|
||||
<a href="{% url 'village_create' camp_slug=camp.slug %}" class="btn btn-primary">Create a village</a>
|
||||
{% endif %}
|
||||
|
||||
<hr />
|
||||
|
||||
{% if villages %}
|
||||
<table class="table table-hover table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -39,7 +37,7 @@ Villages | {{ block.super }}
|
|||
{% for village in villages %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'villages:detail' slug=village.slug %}">
|
||||
<a href="{% url 'village_detail' camp_slug=camp.slug slug=village.slug %}">
|
||||
{{ village.name }}
|
||||
</a>
|
||||
</td>
|
||||
|
@ -53,5 +51,7 @@ Villages | {{ block.super }}
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
{% else %}
|
||||
<h4>No villages for <b>{{ camp.title }}</b> yet!</h4>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,21 +2,20 @@ from django.http import Http404
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic import (
|
||||
ListView, DetailView, CreateView, UpdateView, DeleteView
|
||||
)
|
||||
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from .models import Village
|
||||
from camps.models import Camp
|
||||
from camps.mixins import CampViewMixin
|
||||
|
||||
|
||||
class VillageListView(ListView):
|
||||
class VillageListView(CampViewMixin, ListView):
|
||||
queryset = Village.objects.not_deleted()
|
||||
template_name = 'village_list.html'
|
||||
context_object_name = 'villages'
|
||||
|
||||
|
||||
class VillageDetailView(DetailView):
|
||||
class VillageDetailView(CampViewMixin, DetailView):
|
||||
queryset = Village.objects.not_deleted()
|
||||
template_name = 'village_detail.html'
|
||||
context_object_name = 'village'
|
||||
|
@ -31,6 +30,7 @@ class VillageCreateView(LoginRequiredMixin, CreateView):
|
|||
def form_valid(self, form):
|
||||
village = form.save(commit=False)
|
||||
village.contact = self.request.user
|
||||
village.camp = Camp.objects.get(slug=self.request.session['campslug'])
|
||||
village.save()
|
||||
return HttpResponseRedirect(village.get_absolute_url())
|
||||
|
||||
|
@ -64,3 +64,4 @@ class VillageDeleteView(EnsureUserOwnsVillageMixin, LoginRequiredMixin, DeleteVi
|
|||
success_url = reverse_lazy('villages:list')
|
||||
template_name = 'village_confirm_delete.html'
|
||||
context_object_name = 'village'
|
||||
|
||||
|
|