diff --git a/bornhack/settings/base.py b/bornhack/settings/base.py
index 4b7c2934..269257f9 100644
--- a/bornhack/settings/base.py
+++ b/bornhack/settings/base.py
@@ -29,6 +29,7 @@ INSTALLED_APPS = [
'shop',
'news',
'utils',
+ 'villages',
'allauth',
'allauth.account',
diff --git a/bornhack/templates/base.html b/bornhack/templates/base.html
index 97fcefb1..cbc2ad57 100644
--- a/bornhack/templates/base.html
+++ b/bornhack/templates/base.html
@@ -42,19 +42,8 @@
{% if current_camp.shop_open %}
Shop
-
- {% if current_order and current_order.get_number_of_items %}
-
-
-
-
- {{ current_order.get_number_of_items }}
-
-
-
- {% endif %}
-
{% endif %}
+ Villages
Speakers
Sponsors
Contact
diff --git a/bornhack/urls.py b/bornhack/urls.py
index d291f52a..5cdd323b 100644
--- a/bornhack/urls.py
+++ b/bornhack/urls.py
@@ -74,6 +74,10 @@ urlpatterns = [
r'^news/',
include('news.urls', namespace='news')
),
+ url(
+ r'^villages/',
+ include('villages.urls', namespace='villages')
+ ),
url(r'^accounts/', include('allauth.urls')),
url(r'^admin/', include(admin.site.urls)),
]
diff --git a/shop/templates/shop_base.html b/shop/templates/shop_base.html
index 71cf3a6e..9f213393 100644
--- a/shop/templates/shop_base.html
+++ b/shop/templates/shop_base.html
@@ -19,7 +19,21 @@
{% if has_tickets %}
Tickets
{% endif %}
- Orders
+ Orders
+
+
+ {% if current_order and current_order.get_number_of_items %}
+
+ {% endif %}
+
+
+
+ {{ current_order.get_number_of_items|default:0 }}
+
+ {% if current_order and current_order.get_number_of_items %}
+
+ {% endif %}
+
{% endif %}
diff --git a/villages/__init__.py b/villages/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/villages/admin.py b/villages/admin.py
new file mode 100644
index 00000000..c276e488
--- /dev/null
+++ b/villages/admin.py
@@ -0,0 +1,8 @@
+from django.contrib import admin
+
+from .models import Village
+
+
+@admin.register(Village)
+class VillageAdmin(admin.ModelAdmin):
+ pass
diff --git a/villages/apps.py b/villages/apps.py
new file mode 100644
index 00000000..fbf4978a
--- /dev/null
+++ b/villages/apps.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+
+from django.apps import AppConfig
+
+
+class VillagesConfig(AppConfig):
+ name = 'villages'
diff --git a/villages/migrations/0001_initial.py b/villages/migrations/0001_initial.py
new file mode 100644
index 00000000..47c87f10
--- /dev/null
+++ b/villages/migrations/0001_initial.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.6 on 2016-07-05 21:38
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('camps', '0005_auto_20160510_2011'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Village',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('name', models.CharField(max_length=255)),
+ ('slug', models.SlugField(blank=True, max_length=255)),
+ ('description', models.TextField()),
+ ('open', models.BooleanField(default=False, help_text='Is this village open for others to join?')),
+ ('camp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp')),
+ ('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'ordering': ['name'],
+ },
+ ),
+ ]
diff --git a/villages/migrations/__init__.py b/villages/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/villages/models.py b/villages/models.py
new file mode 100644
index 00000000..d57c9eb7
--- /dev/null
+++ b/villages/models.py
@@ -0,0 +1,58 @@
+from __future__ import unicode_literals
+
+from django.core.urlresolvers import reverse_lazy
+from django.db import models
+from django.utils.text import slugify
+
+from camps.models import Camp
+from utils.models import CreatedUpdatedModel, UUIDModel
+
+
+class Village(CreatedUpdatedModel, UUIDModel):
+
+ class Meta:
+ ordering = ['name']
+
+ camp = models.ForeignKey('camps.Camp')
+ contact = models.ForeignKey('auth.User')
+
+ name = models.CharField(max_length=255)
+ slug = models.SlugField(max_length=255, blank=True)
+ description = models.TextField()
+
+ open = models.BooleanField(
+ default=False,
+ help_text='Is this village open for others to join?'
+ )
+
+ def __str__(self):
+ return self.name
+
+ def get_absolute_url(self):
+ return reverse_lazy('villages:detail', kwargs={'slug': self.slug})
+
+ def save(self, **kwargs):
+ if (
+ not self.pk or
+ not self.slug or
+ Village.objects.filter(slug=self.slug).count() > 1
+ ):
+ slug = slugify(self.name)
+ incrementer = 1
+
+ # We have to make sure that the slug won't clash with current slugs
+ while Village.objects.filter(slug=slug).exists():
+ if incrementer == 1:
+ slug = '{}-1'.format(slug)
+ else:
+ slug = '{}-{}'.format(
+ '-'.join(slug.split('-')[:-1]),
+ incrementer
+ )
+ incrementer += 1
+ self.slug = slug
+
+ if not hasattr(self, 'camp'):
+ self.camp = Camp.objects.current()
+
+ super(Village, self).save(**kwargs)
diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html
new file mode 100644
index 00000000..3491060b
--- /dev/null
+++ b/villages/templates/village_detail.html
@@ -0,0 +1,16 @@
+{% extends 'base.html' %}
+{% load commonmark %}
+
+{% block content %}
+
+{{ village.name }}
+
+{{ village.description|commonmark }}
+
+
+
+{% if user in village.users.all %}
+Edit
+{% endif %}
+
+{% endblock %}
diff --git a/villages/templates/village_form.html b/villages/templates/village_form.html
new file mode 100644
index 00000000..3b5f8534
--- /dev/null
+++ b/villages/templates/village_form.html
@@ -0,0 +1,16 @@
+{% extends 'base.html' %}
+{% load bootstrap3 %}
+
+{% block content %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html
new file mode 100644
index 00000000..c89cbeb0
--- /dev/null
+++ b/villages/templates/village_list.html
@@ -0,0 +1,38 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+ 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 bunch of your friends/likeminded people camp together. Apart from peoples individual tents which they sleep in, many villages bring a large common tent where you can hack and hang out during the day.
+
+
+{% if user.is_authenticated %}
+Create a village
+{% endif %}
+
+
+
+
+
+
+ Name |
+ Price |
+
+
+
+ {% for village in villages %}
+
+
+
+ {{ village.name }}
+
+ |
+
+ {{ village.description|truncatewords:15 }}
+ |
+
+ {% endfor %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/villages/tests.py b/villages/tests.py
new file mode 100644
index 00000000..7ce503c2
--- /dev/null
+++ b/villages/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/villages/urls.py b/villages/urls.py
new file mode 100644
index 00000000..edc57aa5
--- /dev/null
+++ b/villages/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls import url
+from views import *
+
+urlpatterns = [
+ url(r'^$', VillageListView.as_view(), name='list'),
+ url(r'create/$', VillageCreateView.as_view(), name='create'),
+ url(r'(?P[-_\w+]+)/edit/$', VillageUpdateView.as_view(), name='update'),
+ url(r'(?P[-_\w+]+)/$', VillageDetailView.as_view(), name='detail'),
+]
diff --git a/villages/views.py b/villages/views.py
new file mode 100644
index 00000000..5d4e589f
--- /dev/null
+++ b/villages/views.py
@@ -0,0 +1,40 @@
+from django.core.urlresolvers import reverse_lazy
+from django.http import HttpResponseRedirect
+from django.views.generic import ListView, DetailView, CreateView, UpdateView
+from .models import (
+ Village,
+)
+
+
+class VillageListView(ListView):
+ model = Village
+ template_name = 'village_list.html'
+ context_object_name = 'villages'
+
+
+class VillageDetailView(DetailView):
+ model = Village
+ template_name = 'village_detail.html'
+ context_object_name = 'village'
+
+
+class VillageCreateView(CreateView):
+ model = Village
+ template_name = 'village_form.html'
+ fields = ['name', 'description', 'open']
+ success_url = reverse_lazy('villages:list')
+
+ def form_valid(self, form):
+ village = form.save(commit=False)
+ village.contact = self.request.user
+ village.save()
+ return HttpResponseRedirect(village.get_absolute_url())
+
+
+class VillageUpdateView(UpdateView):
+ model = Village
+ template_name = 'village_form.html'
+ fields = ['name', 'description', 'open']
+
+ def get_success_url(self):
+ return self.get_object().get_absolute_url()