diff --git a/bornhack/settings.py b/bornhack/settings.py index c9108e34..7c5d3050 100644 --- a/bornhack/settings.py +++ b/bornhack/settings.py @@ -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', ], }, }, diff --git a/bornhack/urls.py b/bornhack/urls.py index 03759c06..3133c851 100644 --- a/bornhack/urls.py +++ b/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[-_\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[-_\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[-_\w+]+)/delete/$', - VillageDeleteView.as_view(), - name='village_delete' + r'^schedule/', include([ + url( + r'^(?P\d{4})-(?P\d{2})-(?P\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[-_\w+]+)/$', + SpeakerDetailView.as_view(), + name='speaker_detail' + ), + url( + r'^events/$', + EventListView.as_view(), + name='event_index' + ), + url( + r'^(?P[-_\w+]+)/$', + EventDetailView.as_view(), + name='event' + ), + ]) ), + url( - r'(?P[-_\w+]+)/edit/$', - VillageUpdateView.as_view(), - name='village_update' + r'^sponsors/$', + SponsorIndexView.as_view(), + name='sponsors' ), + url( - r'(?P[-_\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[-_\w+]+)/delete/$', + VillageDeleteView.as_view(), + name='village_delete' + ), + url( + r'(?P[-_\w+]+)/edit/$', + VillageUpdateView.as_view(), + name='village_update' + ), + url( + r'(?P[-_\w+]+)/$', + VillageDetailView.as_view(), + name='village_detail' + ), + ]) ), - ])), - ])), + ]) + ) ] diff --git a/camps/context_processors.py b/camps/context_processors.py index faf0aed7..6874755f 100644 --- a/camps/context_processors.py +++ b/camps/context_processors.py @@ -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 } diff --git a/camps/migrations/0011_auto_20161228_1750.py b/camps/migrations/0011_auto_20161228_1750.py new file mode 100644 index 00000000..0f304a50 --- /dev/null +++ b/camps/migrations/0011_auto_20161228_1750.py @@ -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, + ), + ] diff --git a/camps/migrations/0012_auto_20161228_2312.py b/camps/migrations/0012_auto_20161228_2312.py new file mode 100644 index 00000000..c9fc86fc --- /dev/null +++ b/camps/migrations/0012_auto_20161228_2312.py @@ -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', + ), + ] diff --git a/camps/mixins.py b/camps/mixins.py index cee4d1ad..fca375da 100644 --- a/camps/mixins.py +++ b/camps/mixins.py @@ -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) + diff --git a/info/admin.py b/info/admin.py index 8c38f3f3..e8b9235a 100644 --- a/info/admin.py +++ b/info/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin +from .models import * + +admin.site.register(InfoCategory) +admin.site.register(InfoItem) -# Register your models here. diff --git a/info/migrations/0002_auto_20161228_2312.py b/info/migrations/0002_auto_20161228_2312.py new file mode 100644 index 00000000..33317daf --- /dev/null +++ b/info/migrations/0002_auto_20161228_2312.py @@ -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.'), + ), + ] diff --git a/info/models.py b/info/models.py index 303b083c..04d30a69 100644 --- a/info/models.py +++ b/info/models.py @@ -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'}) diff --git a/info/templates/info.html b/info/templates/info.html index 1c2d1b2d..2f2cb05a 100644 --- a/info/templates/info.html +++ b/info/templates/info.html @@ -26,60 +26,45 @@ Info | {{ block.super }} } -
-
-

Table of Contents

+ {% if categories %} +
+
+

Table of Contents

{% for category in categories %} {{ category.headline }} {% endfor %}

-
-
- - {% for category in categories %} - -
-
-

When is BornHack happening?

-
- {% for item in category.infoitems %} -
-
- -

- {{ item.title }} - - - -

-
-
-

{{ item.body }}

-
-
- {% endfor %} -
- {% endfor %} - - - + {% for category in categories %} + +
+
+

{{ category.title }}

+
+ {% for item in category.infoitems.all %} +
+
+ +

{{ item.headline }} + + + +

+
+
+

{{ item.body }}

+
+
+ {% endfor %} +
+
+
+ {% endfor %} + {% else %} +

No info found for {{ camp.title }}

+ {% endif %} {% endblock %} + diff --git a/info/views.py b/info/views.py index 3a00a0f7..08e14ea5 100644 --- a/info/views.py +++ b/info/views.py @@ -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) diff --git a/program/views.py b/program/views.py index 79e13227..44a37188 100644 --- a/program/views.py +++ b/program/views.py @@ -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 diff --git a/sponsors/__init__.py b/sponsors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sponsors/admin.py b/sponsors/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/sponsors/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/sponsors/apps.py b/sponsors/apps.py new file mode 100644 index 00000000..85079993 --- /dev/null +++ b/sponsors/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class SponsorsConfig(AppConfig): + name = 'sponsors' diff --git a/sponsors/migrations/__init__.py b/sponsors/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sponsors/models.py b/sponsors/models.py new file mode 100644 index 00000000..bd4b2abe --- /dev/null +++ b/sponsors/models.py @@ -0,0 +1,5 @@ +from __future__ import unicode_literals + +from django.db import models + +# Create your models here. diff --git a/templates/sponsors.html b/sponsors/templates/bornhack-2016-sponsors.html similarity index 100% rename from templates/sponsors.html rename to sponsors/templates/bornhack-2016-sponsors.html diff --git a/sponsors/tests.py b/sponsors/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/sponsors/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/sponsors/views.py b/sponsors/views.py new file mode 100644 index 00000000..2beef2e5 --- /dev/null +++ b/sponsors/views.py @@ -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 + + diff --git a/static_src/css/bornhack.css b/static_src/css/bornhack.css index dbb1fbff..c549c03b 100644 --- a/static_src/css/bornhack.css +++ b/static_src/css/bornhack.css @@ -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 { diff --git a/static_src/img/logo.png b/static_src/img/bornhack-2016/bornhack-2016-logo-large.png similarity index 100% rename from static_src/img/logo.png rename to static_src/img/bornhack-2016/bornhack-2016-logo-large.png diff --git a/static_src/img/logo-new.png b/static_src/img/bornhack-2016/bornhack-2016-logo-small.png similarity index 100% rename from static_src/img/logo-new.png rename to static_src/img/bornhack-2016/bornhack-2016-logo-small.png diff --git a/static_src/img/logo-new-paths.svg b/static_src/img/bornhack-2016/logo-new-paths.svg similarity index 100% rename from static_src/img/logo-new-paths.svg rename to static_src/img/bornhack-2016/logo-new-paths.svg diff --git a/static_src/img/bornhack-2016/logo-new.png b/static_src/img/bornhack-2016/logo-new.png new file mode 100644 index 00000000..ede32e9e Binary files /dev/null and b/static_src/img/bornhack-2016/logo-new.png differ diff --git a/static_src/img/logo-new.svg b/static_src/img/bornhack-2016/logo-new.svg similarity index 100% rename from static_src/img/logo-new.svg rename to static_src/img/bornhack-2016/logo-new.svg diff --git a/static_src/img/bornhack-2016/logo.png b/static_src/img/bornhack-2016/logo.png new file mode 100644 index 00000000..3a503c33 Binary files /dev/null and b/static_src/img/bornhack-2016/logo.png differ diff --git a/static_src/img/logo.svg b/static_src/img/bornhack-2016/logo.svg similarity index 100% rename from static_src/img/logo.svg rename to static_src/img/bornhack-2016/logo.svg diff --git a/static_src/img/logo-small.png b/static_src/img/logo-small.png new file mode 100644 index 00000000..1529b2d6 Binary files /dev/null and b/static_src/img/logo-small.png differ diff --git a/templates/info.html b/templates/2016info.html similarity index 100% rename from templates/info.html rename to templates/2016info.html diff --git a/templates/base.html b/templates/base.html index 7f2547c2..9efb2401 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,5 +1,7 @@ {% load static from staticfiles %} {% load bootstrap3 %} +{% static "" as baseurl %} + @@ -20,7 +22,6 @@ - - +
+ {% if camp %} + + {% endif %} {% bootstrap_messages %} {% block content %}{% endblock %}
diff --git a/utils/models.py b/utils/models.py index d94a9162..54da5790 100644 --- a/utils/models.py +++ b/utils/models.py @@ -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): diff --git a/villages/migrations/0007_village_camp.py b/villages/migrations/0007_village_camp.py new file mode 100644 index 00000000..338d8b3c --- /dev/null +++ b/villages/migrations/0007_village_camp.py @@ -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'), + ), + ] diff --git a/villages/migrations/0008_auto_20161228_2209.py b/villages/migrations/0008_auto_20161228_2209.py new file mode 100644 index 00000000..aba16e5c --- /dev/null +++ b/villages/migrations/0008_auto_20161228_2209.py @@ -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'), + ), + ] diff --git a/villages/models.py b/villages/models.py index da7104b0..8860a280 100644 --- a/villages/models.py +++ b/villages/models.py @@ -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 ( diff --git a/villages/templates/village_confirm_delete.html b/villages/templates/village_confirm_delete.html index d4fd816c..f3a8207a 100644 --- a/villages/templates/village_confirm_delete.html +++ b/villages/templates/village_confirm_delete.html @@ -8,6 +8,6 @@

- Cancel + Cancel {% endblock %} diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index db73c6d8..0a49e09e 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -13,8 +13,8 @@ Village: {{ village.name }} | {{ block.super }} {% if user == village.contact %}
-Edit -Delete +Edit +Delete {% endif %} {% endblock %} diff --git a/villages/templates/village_form.html b/villages/templates/village_form.html index 3b5f8534..2accd283 100644 --- a/villages/templates/village_form.html +++ b/villages/templates/village_form.html @@ -13,4 +13,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html index 6af03edb..7e7ae95e 100644 --- a/villages/templates/village_list.html +++ b/villages/templates/village_list.html @@ -6,7 +6,7 @@ Villages | {{ block.super }} {% endblock %} {% block content %} - +

Villages

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 }}

- - It is also possible to rent a tent, chairs and tables for villages here. - + It is also possible to rent a tent, chairs and tables in the shop!

{% if user.is_authenticated %} -Create a village +Create a village {% endif %}
- +{% if villages %} @@ -39,7 +37,7 @@ Villages | {{ block.super }} {% for village in villages %} @@ -53,5 +51,7 @@ Villages | {{ block.super }} {% endfor %}
- + {{ village.name }}
- -{% endblock %} \ No newline at end of file +{% else %} +

No villages for {{ camp.title }} yet!

+{% endif %} +{% endblock %} diff --git a/villages/views.py b/villages/views.py index 901e79cf..69fa59a9 100644 --- a/villages/views.py +++ b/villages/views.py @@ -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' +