diff --git a/Dockerfile b/Dockerfile
index f598b7e..3dbcb30 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -47,6 +47,8 @@ RUN wget https://nodejs.org/dist/v7.10.0/node-v7.10.0-linux-x64.tar.gz\
&& tar -xvf ../../proj-datumgrid-1.5.tar.gz && cd ../ \
&& ./configure && make && make install
+RUN apt-get update && apt-get install -y gettext git
+
# PACKAGES
WORKDIR /app
COPY ./requirements.txt .
@@ -59,8 +61,6 @@ COPY ./support/cron/* /etc/cron.d/
RUN chmod 0644 /etc/cron.d/*\
&& touch /var/log/cron.log
-RUN apt-get install -y gettext
-
EXPOSE 8000
CMD [ "python3", "manage.py", "migrate", "&&", \
diff --git a/apps/contact/__init__.py b/apps/contact/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/contact/forms.py b/apps/contact/forms.py
new file mode 100644
index 0000000..2471534
--- /dev/null
+++ b/apps/contact/forms.py
@@ -0,0 +1,11 @@
+from envelope.forms import ContactForm
+from crispy_forms.helper import FormHelper
+from crispy_forms.layout import Submit
+
+
+class ContactForm(ContactForm):
+ def __init__(self, *args, **kwargs):
+ super(ContactForm, self).__init__(*args, **kwargs)
+ self.helper = FormHelper()
+ self.helper.add_input(Submit('submit', 'Submit',
+ css_class='btn-lg pull-right'))
\ No newline at end of file
diff --git a/apps/contact/templates/envelope/contact.html b/apps/contact/templates/envelope/contact.html
new file mode 100644
index 0000000..e4de921
--- /dev/null
+++ b/apps/contact/templates/envelope/contact.html
@@ -0,0 +1,21 @@
+{% extends "base_page.html" %}
+{% load bootstrap3 %}
+{% load crispy_forms_tags %}
+{% load envelope_tags %}
+
+{% block page_name %}Contact{% endblock %}
+
+{% block content %}
+
+
+
Contact
+
Send us your thoughts and feedback.
+
+ {% bootstrap_messages %}
+
+
+{% endblock %}
diff --git a/apps/contact/tests.py b/apps/contact/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/apps/contact/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/apps/contact/urls.py b/apps/contact/urls.py
new file mode 100644
index 0000000..7074f9c
--- /dev/null
+++ b/apps/contact/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+ url(r'^$', views.ContactView.as_view(), name='contact'),
+]
diff --git a/apps/contact/views.py b/apps/contact/views.py
new file mode 100644
index 0000000..4028c13
--- /dev/null
+++ b/apps/contact/views.py
@@ -0,0 +1,12 @@
+from braces.views import FormMessagesMixin
+from envelope.views import ContactView
+
+from django.utils.translation import ugettext_lazy as _
+
+from .forms import ContactForm
+
+
+class ContactView(FormMessagesMixin, ContactView):
+ form_invalid_message = _(u"There was an error in the contact form.")
+ form_valid_message = _(u"Thank you for your message.")
+ form_class = ContactForm
diff --git a/apps/map/admin.py b/apps/map/admin.py
index 30248af..9322dfe 100644
--- a/apps/map/admin.py
+++ b/apps/map/admin.py
@@ -1,6 +1,12 @@
from django.contrib import admin
+from moderation.admin import ModerationAdmin
from leaflet.admin import LeafletGeoAdmin
from .models import CaseStudy
-admin.site.register(CaseStudy, LeafletGeoAdmin)
+
+class CaseStudyAdmin(ModerationAdmin, LeafletGeoAdmin):
+ pass
+
+
+admin.site.register(CaseStudy, CaseStudyAdmin)
diff --git a/apps/map/forms.py b/apps/map/forms.py
index 8b85f8a..977c3aa 100644
--- a/apps/map/forms.py
+++ b/apps/map/forms.py
@@ -6,15 +6,15 @@ from leaflet.forms.widgets import LeafletWidget
from .models import CaseStudy
-class CaseStudyForm(forms.ModelForm):
+class BaseCaseStudyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
- super(CaseStudyForm, self).__init__(*args, **kwargs)
+ super(BaseCaseStudyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'case-study-form'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
- self.helper.form_action = 'submit'
+ self.helper.form_action = 'add'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(Submit('submit', 'Submit'))
@@ -22,4 +22,33 @@ class CaseStudyForm(forms.ModelForm):
class Meta:
model = CaseStudy
fields = '__all__'
- widgets = {'location': LeafletWidget()}
+ widgets = {'location': LeafletWidget(attrs={})}
+
+
+class ShortCaseStudyForm(BaseCaseStudyForm):
+ class Meta(BaseCaseStudyForm.Meta):
+ fields = [
+ 'entry_name',
+ 'location',
+ 'sector_of_economy',
+ 'positive_or_negative',
+ 'country',
+ 'area_of_land',
+ 'land_ownership',
+ 'land_ownership_details',
+ 'location_context',
+ 'type_of_ecosystem',
+ 'describe_ecosystem',
+ 'affects_indigenous',
+ 'affects_indigenous_detail',
+ 'project_status',
+ 'synopsis',
+ 'full_description',
+ 'image',
+ 'community_voices'
+ ]
+
+
+class LongCaseStudyForm(BaseCaseStudyForm):
+ class Meta(BaseCaseStudyForm.Meta):
+ fields = '__all__'
diff --git a/apps/map/migrations/0004_auto_20171006_1559.py b/apps/map/migrations/0004_auto_20171006_1559.py
new file mode 100644
index 0000000..ed4188c
--- /dev/null
+++ b/apps/map/migrations/0004_auto_20171006_1559.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-06 15:59
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0003_auto_20170521_0643'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='affects_indigenous',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='affects_indigenous_reason',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='associated_companies',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='commodities',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='description',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='financiers',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='generation_type',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='important_lenders',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='like_to_engage_developer',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='like_to_engage_investors',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='link_to_forum',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='project_name',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='proposed_completion',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='proposed_start',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='references',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='supply_chain',
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Affects Indigenous - Details',
+ field=models.CharField(default='', help_text='What group of indigenous people does the community belong to?', max_length=256),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Affects indigenous people?',
+ field=models.BooleanField(default=False, help_text='Does the project affect indigenous people?'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Approximate land area',
+ field=models.IntegerField(default=0, help_text='The area of land covered by the project (in km²)'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Community Voices',
+ field=models.TextField(default='', help_text='Add any direct quotes from members of the community that relate to this project'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Describe the ecosystem',
+ field=models.CharField(default='', help_text='In your own words, add more detail about the ecosystem.', max_length=256),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Entry Name',
+ field=models.CharField(default='', help_text='Enter the name of the entry. This should usually be the name of project.', max_length=128),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Full Description',
+ field=models.TextField(default='', help_text='Describe the project in full. Separate paragraphs with a new line Please add as much detail as you feel is necessary here.'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Land ownership',
+ field=models.CharField(choices=[('PRI', 'Private Land'), ('PUB', 'Public Land'), ('COM', 'Community Land'), ('OTH', 'Other')], default='', help_text='What type of ownership does the land fall under?', max_length=3),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Land ownership details',
+ field=models.CharField(default='', help_text='Add any details and other remarks about the land ownership', max_length=256),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Location',
+ field=models.CharField(choices=[('RUR', 'Rural'), ('URB', 'Urban')], default=None, help_text='Select the context that is most applicable to this case study.', max_length=1),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Positive or negative?',
+ field=models.CharField(choices=[('POS', 'Positive'), ('NEG', 'Negative')], default=None, help_text='Is the case study a positive case or a negative case?', max_length=1),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Sector of economy',
+ field=models.CharField(choices=[('Renewable Energy Generation', (('WND', 'Wind'), ('SOL', 'Solar'), ('HYD', 'Hydro'))), ('PG', 'Power Grids'), ('SM', 'Supply of Minerals')], default=None, help_text='Which sector of the renewable energy economy is most relevant?', max_length=2),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Status of Project',
+ field=models.CharField(choices=[('EXSTNG', 'Existing Project'), ('UCONST', 'Under Construction'), ('PROJCD', 'Projected Project')], default=None, help_text='What is the status of the current project?', max_length=6),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Synopsis',
+ field=models.TextField(default=None, help_text='Briefly describe the project. This will be displayed at the top of the case study page. Maximum 500 chars (about 3½ tweets)', max_length=500),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='Type of ecosystem',
+ field=models.CharField(choices=[('Water Based', (('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'))), ('Land Based', (('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')))], default=None, help_text='Select the most relevant type of ecosystem.', max_length=6),
+ preserve_default=False,
+ ),
+ ]
diff --git a/apps/map/migrations/0005_auto_20171006_2033.py b/apps/map/migrations/0005_auto_20171006_2033.py
new file mode 100644
index 0000000..bd4f85c
--- /dev/null
+++ b/apps/map/migrations/0005_auto_20171006_2033.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-06 20:33
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0004_auto_20171006_1559'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Affects Indigenous - Details',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Affects indigenous people?',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Approximate land area',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Community Voices',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Describe the ecosystem',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Entry Name',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Full Description',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Land ownership',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Land ownership details',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Location',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Positive or negative?',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Sector of economy',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Status of Project',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Synopsis',
+ ),
+ migrations.RemoveField(
+ model_name='casestudy',
+ name='Type of ecosystem',
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='affects_indigenous',
+ field=models.BooleanField(default=None, help_text='Does the project affect indigenous people?', verbose_name='Affects indigenous people?'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='affects_indigenous_detail',
+ field=models.CharField(default=None, help_text='What group of indigenous people does the community belong to?', max_length=256, verbose_name='Affects Indigenous - Details'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='area_of_land',
+ field=models.IntegerField(default=None, help_text='The area of land covered by the project (in km²)', verbose_name='Approximate land area'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='community_voices',
+ field=models.TextField(default=None, help_text='Add any direct quotes from members of the community that relate to this project', verbose_name='Community Voices'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='describe_ecosystem',
+ field=models.CharField(default=None, help_text='In your own words, add more detail about the ecosystem.', max_length=256, verbose_name='Describe the ecosystem'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='entry_name',
+ field=models.CharField(default=None, help_text='Enter the name of the entry. This should usually be the name of project.', max_length=128, verbose_name='Entry Name'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='full_description',
+ field=models.TextField(default=None, help_text='Describe the project in full. Separate paragraphs with a new line Please add as much detail as you feel is necessary here.', verbose_name='Full Description'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='land_ownership',
+ field=models.CharField(choices=[('PRI', 'Private Land'), ('PUB', 'Public Land'), ('COM', 'Community Land'), ('OTH', 'Other')], default=None, help_text='What type of ownership does the land fall under?', max_length=3, verbose_name='Land ownership'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='land_ownership_details',
+ field=models.CharField(default=None, help_text='Add any details and other remarks about the land ownership', max_length=256, verbose_name='Land ownership details'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='location_context',
+ field=models.CharField(choices=[('RUR', 'Rural'), ('URB', 'Urban')], default=None, help_text='Select the context that is most applicable to this case study.', max_length=1, verbose_name='Location'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='positive_or_negative',
+ field=models.CharField(choices=[('POS', 'Positive'), ('NEG', 'Negative')], default=None, help_text='Is the case study a positive case or a negative case?', max_length=1, verbose_name='Positive or negative?'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='project_status',
+ field=models.CharField(choices=[('EXSTNG', 'Existing Project'), ('UCONST', 'Under Construction'), ('PROJCD', 'Projected Project')], default=None, help_text='What is the status of the current project?', max_length=6, verbose_name='Status of Project'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='sector_of_economy',
+ field=models.CharField(choices=[('Renewable Energy Generation', (('WND', 'Wind'), ('SOL', 'Solar'), ('HYD', 'Hydro'))), ('PG', 'Power Grids'), ('SM', 'Supply of Minerals')], default=None, help_text='Which sector of the renewable energy economy is most relevant?', max_length=2, verbose_name='Sector of economy'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='synopsis',
+ field=models.TextField(default=None, help_text='Briefly describe the project. This will be displayed at the top of the case study page. Maximum 500 chars (about 3½ tweets)', max_length=500, verbose_name='Synopsis'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='casestudy',
+ name='type_of_ecosystem',
+ field=models.CharField(choices=[('Water Based', (('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'))), ('Land Based', (('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')))], default=None, help_text='Select the most relevant type of ecosystem.', max_length=6, verbose_name='Type of ecosystem'),
+ preserve_default=False,
+ ),
+ ]
diff --git a/apps/map/migrations/0006_auto_20171007_1349.py b/apps/map/migrations/0006_auto_20171007_1349.py
new file mode 100644
index 0000000..74a1e89
--- /dev/null
+++ b/apps/map/migrations/0006_auto_20171007_1349.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-07 13:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0005_auto_20171006_2033'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='casestudy',
+ name='location_context',
+ field=models.CharField(choices=[('RUR', 'Rural'), ('URB', 'Urban')], help_text='Select the context that is most applicable to this case study.', max_length=3, verbose_name='Location'),
+ ),
+ migrations.AlterField(
+ model_name='casestudy',
+ name='positive_or_negative',
+ field=models.CharField(choices=[('P', 'Positive'), ('N', 'Negative')], help_text='Is the case study a positive case or a negative case?', max_length=1, verbose_name='Positive or negative?'),
+ ),
+ migrations.AlterField(
+ model_name='casestudy',
+ name='sector_of_economy',
+ field=models.CharField(choices=[('Renewable Energy Generation', (('WND', 'Wind'), ('SOL', 'Solar'), ('HYD', 'Hydro'))), ('PG', 'Power Grids'), ('SM', 'Supply of Minerals')], help_text='Which sector of the renewable energy economy is most relevant?', max_length=3, verbose_name='Sector of economy'),
+ ),
+ ]
diff --git a/apps/map/migrations/0007_casestudy_slug.py b/apps/map/migrations/0007_casestudy_slug.py
new file mode 100644
index 0000000..9c6d5f9
--- /dev/null
+++ b/apps/map/migrations/0007_casestudy_slug.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-07 14:23
+from __future__ import unicode_literals
+
+from django.db import migrations
+import django_extensions.db.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0006_auto_20171007_1349'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='casestudy',
+ name='slug',
+ field=django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from=['entry_name']),
+ ),
+ ]
diff --git a/apps/map/migrations/0008_casestudy_date_created.py b/apps/map/migrations/0008_casestudy_date_created.py
new file mode 100644
index 0000000..8f80d42
--- /dev/null
+++ b/apps/map/migrations/0008_casestudy_date_created.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-07 15:02
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0007_casestudy_slug'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='casestudy',
+ name='date_created',
+ field=models.DateTimeField(auto_now=True),
+ ),
+ ]
diff --git a/apps/map/migrations/0009_auto_20171007_1544.py b/apps/map/migrations/0009_auto_20171007_1544.py
new file mode 100644
index 0000000..aad5d57
--- /dev/null
+++ b/apps/map/migrations/0009_auto_20171007_1544.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-07 15:44
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('map', '0008_casestudy_date_created'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='casestudy',
+ name='video',
+ field=models.URLField(default="", max_length=43, verbose_name='Video'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='casestudy',
+ name='image',
+ field=models.ImageField(upload_to='', verbose_name='Image'),
+ ),
+ ]
diff --git a/apps/map/models.py b/apps/map/models.py
index 3c1ce2e..75ec87c 100644
--- a/apps/map/models.py
+++ b/apps/map/models.py
@@ -1,21 +1,67 @@
from django.contrib.gis.db import models
from django.contrib.auth.models import User
+from django_extensions.db.fields import AutoSlugField
from django_countries.fields import CountryField
+from django.utils.translation import ugettext as _
+from django.template.defaultfilters import slugify
class CaseStudy(models.Model):
"""Model for case studies submitted to the Ojuso Platform"""
- # Choice lists for dropdowns
- SUPPLY_CHAIN_CHOICES = (
- ('A', 'Option A'),
- ('B', 'Option B'),
+ # Choice lists for drop-downs
+ SECTOR_CHOICES = (
+ (_('Renewable Energy Generation'), (
+ ('WND', _('Wind')),
+ ('SOL', _('Solar')),
+ ('HYD', _('Hydro')),
+ )),
+ ('PG', _('Power Grids')),
+ ('SM', _('Supply of Minerals')),
)
- GENERATION_TYPE_CHOICES = (
- ('W', 'Wind'),
- ('S', 'Solar'),
+
+ POSITIVE_NEGATIVE_CHOICES = (
+ ('P', _('Positive')),
+ ('N', _('Negative'))
)
+ LAND_OWNERSHIP_CHOICES = (
+ ('PRI', _('Private Land')),
+ ('PUB', _('Public Land')),
+ ('COM', _('Community Land')),
+ ('OTH', _('Other')),
+ )
+
+ LOCATION_CONTEXT_CHOICES = (
+ ('RUR', _('Rural')),
+ ('URB', _('Urban')),
+ )
+
+ TYPE_OF_ECOSYSTEM_CHOICES = (
+ (_('Water Based'), (
+ ('MARINE', _('Marine (e.g. Ocean, Sea)')),
+ ('FRESH', _('Freshwater (e.g. Freshwater, Lake)')),
+ )),
+ (_('Land Based'), (
+ ('FOREST', _('Forest/Jungle')),
+ ('AGRI', _('Agricultural Land')),
+ ('GRASS', _('Grassland')),
+ ('DESERT', _('Desert (Tundra, Ice or Sand)')),
+ ('WETLND', _('Wetland (Marsh, Mangrove, Peat Soil)')),
+ ('URBAN', _('Urban')),
+ ))
+ )
+
+ PROJECT_STATUS_CHOICES = (
+ ('EXSTNG', _('Existing Project')),
+ ('UCONST', _('Under Construction')),
+ ('PROJCD', _('Projected Project')),
+ )
+
+ ##
+ # Meta Fields
+ ##
+
# User who submitted case study
author = models.ForeignKey(
User,
@@ -25,32 +71,159 @@ class CaseStudy(models.Model):
editable=False
)
- project_name = models.CharField(max_length=128)
- # Location of map pin
+ # Date and time of submission
+ date_created = models.DateTimeField(auto_now=True, null=False)
+
+ # Slug derived from entry_name, used in urls for SEO
+ slug = AutoSlugField(populate_from=['entry_name'])
+
+ # 1.1
+ entry_name = models.CharField(
+ verbose_name=_("Entry Name"),
+ help_text=_("Enter the name of the entry. This should usually be the\
+ name of project."),
+ max_length=128
+ )
+
+ # N/A - Not explicitly listed in spec
location = models.PointField()
- supply_chain = models.CharField(
- max_length=1,
- choices=SUPPLY_CHAIN_CHOICES
+
+ # 1.2
+ sector_of_economy = models.CharField(
+ verbose_name=_("Sector of economy"),
+ help_text=_("Which sector of the renewable energy economy is most\
+ relevant?"),
+ max_length=3,
+ choices=SECTOR_CHOICES
)
- generation_type = models.CharField(
+
+ # 1.3
+ positive_or_negative = models.CharField(
+ verbose_name=_("Positive or negative?"),
+ help_text=_("Is the case study a positive case or a negative case?"),
max_length=1,
- choices=GENERATION_TYPE_CHOICES
+ choices=POSITIVE_NEGATIVE_CHOICES
)
- associated_companies = models.CharField(max_length=128)
- financiers = models.CharField(max_length=128)
- important_lenders = models.CharField(max_length=128)
+
+ # 1.4
country = CountryField()
- affects_indigenous = models.BooleanField()
- affects_indigenous_reason = models.TextField()
- proposed_start = models.DateField()
- proposed_completion = models.DateField()
- description = models.TextField()
- link_to_forum = models.URLField()
- image = models.ImageField()
- references = models.TextField()
- commodities = models.CharField(max_length=128)
- like_to_engage_developer = models.BooleanField()
- like_to_engage_investors = models.BooleanField()
+
+ # 1.5.1
+ area_of_land = models.IntegerField(
+ verbose_name=_("Approximate land area"),
+ help_text=_("The area of land covered by the project (in km²)")
+ )
+
+ # 1.5.2
+ land_ownership = models.CharField(
+ verbose_name=_("Land ownership"),
+ help_text=_("What type of ownership does the land fall under?"),
+ max_length=3,
+ choices=LAND_OWNERSHIP_CHOICES
+ )
+
+ # 1.5.3
+ land_ownership_details = models.CharField(
+ verbose_name=_("Land ownership details"),
+ help_text=_("Add any details and other remarks about the land\
+ ownership"),
+ max_length=256
+ )
+
+ # 1.5.4
+ location_context = models.CharField(
+ verbose_name=_("Location"),
+ help_text=_("Select the context that is most applicable to this case\
+ study."),
+ max_length=3,
+ choices=LOCATION_CONTEXT_CHOICES
+ )
+
+ # 1.5.5
+ type_of_ecosystem = models.CharField(
+ verbose_name=_("Type of ecosystem"),
+ help_text=_("Select the most relevant type of ecosystem."),
+ max_length=6,
+ choices=TYPE_OF_ECOSYSTEM_CHOICES,
+ )
+
+ # 1.5.5.3
+ describe_ecosystem = models.CharField(
+ verbose_name=_("Describe the ecosystem"),
+ help_text=_("In your own words, add more detail about the ecosystem."),
+ max_length=256,
+ )
+
+ # 1.5.6
+ affects_indigenous = models.BooleanField(
+ verbose_name=_("Affects indigenous people?"),
+ help_text=_("Does the project affect indigenous people?")
+ )
+
+ # 1.5.6.1
+ affects_indigenous_detail = models.CharField(
+ verbose_name=_("Affects Indigenous - Details"),
+ help_text=_("What group of indigenous people does the community belong\
+ to?"),
+ max_length=256
+ )
+
+ # 1.6
+ project_status = models.CharField(
+ verbose_name=_("Status of Project"),
+ help_text=_("What is the status of the current project?"),
+ max_length=6,
+ choices=PROJECT_STATUS_CHOICES
+ )
+
+ # 1.9
+ synopsis = models.TextField(
+ verbose_name=_("Synopsis"),
+ help_text=_("Briefly describe the project. This will be displayed at\
+ the top of the case study page. Maximum 500 chars (about \
+ 3½ tweets)"),
+ max_length=500
+ )
+
+ # 1.10
+ full_description = models.TextField(
+ verbose_name=_("Full Description"),
+ help_text=_("Describe the project in full. Separate paragraphs with a\
+ new line Please add as much detail as you feel is necessary\
+ here.")
+ )
+
+ # 1.15
+ image = models.ImageField(
+ verbose_name=_("Image")
+ )
+
+ # 1.16
+ video = models.URLField(
+ verbose_name=_("Video"),
+ max_length=43
+ )
+
+ # 1.18
+ community_voices = models.TextField(
+ verbose_name=_("Community Voices"),
+ help_text=_("Add any direct quotes from members of the community that\
+ relate to this project")
+ )
def __str__(self):
- return "%s in %s" % (self.project_name, self.country.name)
+ """The String representation of the case study. (Entry name with country name.)"""
+ return "%s in %s" % (self.entry_name, self.country.name)
+
+ def clean(self, *args, **kwargs):
+ """Perform validation on the model as a whole and throw a ValidationError if anything isn't how it should be."""
+ pass
+
+ def save(self, *args, **kwargs):
+ """Override the save method to create a slug when the model is created. Slug is only created and never modified
+ as we are basing our URLs on it and don't want it to change - ever."""
+ if not self.pk:
+ # Newly created object, so set slug
+ self.slug = slugify(self.entry_name)
+ # Continue normal save method by calling original save method.
+ super(CaseStudy, self).save(*args, **kwargs)
diff --git a/apps/map/moderator.py b/apps/map/moderator.py
new file mode 100644
index 0000000..da91805
--- /dev/null
+++ b/apps/map/moderator.py
@@ -0,0 +1,4 @@
+from moderation import moderation
+from apps.map.models import CaseStudy
+
+moderation.register(CaseStudy) # Uses default moderation settings
diff --git a/apps/map/requirements.txt b/apps/map/requirements.txt
deleted file mode 100644
index a03e38e..0000000
--- a/apps/map/requirements.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-appdirs==1.4.3
-Django==1.11.1
-django-countries==4.5
-django-crispy-forms==1.6.1
-django-leaflet==0.22.0
-gunicorn==19.7.1
-packaging==16.8
-psycopg2==2.7.1
-pyparsing==2.2.0
-pytz==2017.2
-six==1.10.0
diff --git a/apps/map/static/map/bundle.js b/apps/map/static/map/bundle.js
new file mode 100644
index 0000000..25ed3b9
--- /dev/null
+++ b/apps/map/static/map/bundle.js
@@ -0,0 +1,17274 @@
+/******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, {
+/******/ configurable: false,
+/******/ enumerable: true,
+/******/ get: getter
+/******/ });
+/******/ }
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 2);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports) {
+
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+// css base code, injected by the css-loader
+module.exports = function(useSourceMap) {
+ var list = [];
+
+ // return the list of modules as css string
+ list.toString = function toString() {
+ return this.map(function (item) {
+ var content = cssWithMappingToString(item, useSourceMap);
+ if(item[2]) {
+ return "@media " + item[2] + "{" + content + "}";
+ } else {
+ return content;
+ }
+ }).join("");
+ };
+
+ // import a list of modules into the list
+ list.i = function(modules, mediaQuery) {
+ if(typeof modules === "string")
+ modules = [[null, modules, ""]];
+ var alreadyImportedModules = {};
+ for(var i = 0; i < this.length; i++) {
+ var id = this[i][0];
+ if(typeof id === "number")
+ alreadyImportedModules[id] = true;
+ }
+ for(i = 0; i < modules.length; i++) {
+ var item = modules[i];
+ // skip already imported module
+ // this implementation is not 100% perfect for weird media query combinations
+ // when a module is imported multiple times with different media queries.
+ // I hope this will never occur (Hey this way we have smaller bundles)
+ if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
+ if(mediaQuery && !item[2]) {
+ item[2] = mediaQuery;
+ } else if(mediaQuery) {
+ item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
+ }
+ list.push(item);
+ }
+ }
+ };
+ return list;
+};
+
+function cssWithMappingToString(item, useSourceMap) {
+ var content = item[1] || '';
+ var cssMapping = item[3];
+ if (!cssMapping) {
+ return content;
+ }
+
+ if (useSourceMap && typeof btoa === 'function') {
+ var sourceMapping = toComment(cssMapping);
+ var sourceURLs = cssMapping.sources.map(function (source) {
+ return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
+ });
+
+ return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
+ }
+
+ return [content].join('\n');
+}
+
+// Adapted from convert-source-map (MIT)
+function toComment(sourceMap) {
+ // eslint-disable-next-line no-undef
+ var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
+ var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;
+
+ return '/*# ' + data + ' */';
+}
+
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+var stylesInDom = {};
+
+var memoize = function (fn) {
+ var memo;
+
+ return function () {
+ if (typeof memo === "undefined") memo = fn.apply(this, arguments);
+ return memo;
+ };
+};
+
+var isOldIE = memoize(function () {
+ // Test for IE <= 9 as proposed by Browserhacks
+ // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
+ // Tests for existence of standard globals is to allow style-loader
+ // to operate correctly into non-standard environments
+ // @see https://github.com/webpack-contrib/style-loader/issues/177
+ return window && document && document.all && !window.atob;
+});
+
+var getElement = (function (fn) {
+ var memo = {};
+
+ return function(selector) {
+ if (typeof memo[selector] === "undefined") {
+ memo[selector] = fn.call(this, selector);
+ }
+
+ return memo[selector]
+ };
+})(function (target) {
+ return document.querySelector(target)
+});
+
+var singleton = null;
+var singletonCounter = 0;
+var stylesInsertedAtTop = [];
+
+var fixUrls = __webpack_require__(9);
+
+module.exports = function(list, options) {
+ if (typeof DEBUG !== "undefined" && DEBUG) {
+ if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
+ }
+
+ options = options || {};
+
+ options.attrs = typeof options.attrs === "object" ? options.attrs : {};
+
+ // Force single-tag solution on IE6-9, which has a hard limit on the # of
+{% endblock %}
+
+{% block title %}{% trans "How much time do you have?" %}{% endblock %}
+{% block description %}
+ {% trans "A complete picture is always more helpful but sometimes you don't have the time" %}
+{% endblock %}
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/map/templates/map/index.html b/apps/map/templates/map/index.html
index 584ebfb..8a29c75 100644
--- a/apps/map/templates/map/index.html
+++ b/apps/map/templates/map/index.html
@@ -1,6 +1,5 @@
-{% extends "map/base_with_jumbo.html" %}
+{% extends "base_with_jumbo.html" %}
{% load bootstrap3 %}
-{% load compress %}
{% load i18n %}
{% load leaflet_tags %}
@@ -46,9 +45,9 @@
onEachFeature: function (feature, layer) {
var modalname = "case-study-"+feature.id
layer.bindPopup(
- ""+feature.properties.project_name+"
"+
- ""
+ ""+feature.properties.entry_name+"
"+
+ ""
+ +"{% trans "View" %}"+""
);
var modal = create(
""+
diff --git a/apps/map/urls.py b/apps/map/urls.py
index b0a8da8..688bca4 100644
--- a/apps/map/urls.py
+++ b/apps/map/urls.py
@@ -5,9 +5,10 @@ from .models import CaseStudy
from . import views
urlpatterns = [
- url(r'^$', views.index, name='index'),
- url(r'^data.geojson$',
- GeoJSONLayerView.as_view(model=CaseStudy, geometry_field='location'),
- name='data'),
- url(r'^case-study/add', views.form, name='form')
+ url(r'^$', views.Map.as_view(), name='index'),
+ url(r'^data.geojson$', GeoJSONLayerView.as_view(model=CaseStudy, geometry_field='location'), name='data'),
+ url(r'^case-study/create/?$', views.Create.as_view(), name="create"),
+ url(r'^case-study/create/short/?$', views.ShortForm.as_view(), name='short-form'),
+ url(r'^case-study/create/long/?$', views.LongForm.as_view(), name='long-form'),
+ url(r'^case-study/(?P
[-\w]+)/?$', views.CaseStudyDetail.as_view(), name='detail')
]
diff --git a/apps/map/views.py b/apps/map/views.py
index 60dee31..a670e5d 100644
--- a/apps/map/views.py
+++ b/apps/map/views.py
@@ -1,12 +1,35 @@
-from django.shortcuts import render
-
-from .forms import CaseStudyForm
+from django.views.generic import DetailView
+from django.views.generic.base import TemplateView
+from django.views.generic.edit import FormView
+from django.contrib.auth.mixins import LoginRequiredMixin
+from .models import CaseStudy
+from .forms import ShortCaseStudyForm, LongCaseStudyForm
-def index(request):
- return render(request, 'map/index.html')
+class Map(TemplateView):
+ template_name = "map/index.html"
-def form(request):
- form = CaseStudyForm
- return render(request, 'map/form.html', {'form': form})
+class Create(LoginRequiredMixin, TemplateView):
+ template_name = "map/how_much_time.html"
+
+
+class BaseForm(LoginRequiredMixin, FormView):
+ """This is the base class for the short and long forms. It handles any shared logic between the two subclasses."""
+ template_name = 'map/form.html'
+
+
+class ShortForm(BaseForm):
+ """Here, we use the short version of the form."""
+ form_class = ShortCaseStudyForm
+
+
+class LongForm(BaseForm):
+ """Here, we use the long version of the form."""
+ form_class = LongCaseStudyForm
+
+
+class CaseStudyDetail(DetailView):
+ template_name = "map/detail.html"
+ model = CaseStudy
+ context_object_name = "case_study"
diff --git a/apps/profiles/__init__.py b/apps/profiles/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/profiles/admin.py b/apps/profiles/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/apps/profiles/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/apps/profiles/apps.py b/apps/profiles/apps.py
new file mode 100644
index 0000000..5501fda
--- /dev/null
+++ b/apps/profiles/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class ProfilesConfig(AppConfig):
+ name = 'profiles'
diff --git a/apps/profiles/migrations/__init__.py b/apps/profiles/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/profiles/models.py b/apps/profiles/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/apps/profiles/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/apps/profiles/templates/profiles/profile.html b/apps/profiles/templates/profiles/profile.html
new file mode 100644
index 0000000..6ba97c8
--- /dev/null
+++ b/apps/profiles/templates/profiles/profile.html
@@ -0,0 +1,2 @@
+{% extends "base_page.html" %}
+Profile page
\ No newline at end of file
diff --git a/apps/profiles/tests.py b/apps/profiles/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/apps/profiles/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/apps/profiles/urls.py b/apps/profiles/urls.py
new file mode 100644
index 0000000..e006ca7
--- /dev/null
+++ b/apps/profiles/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+ url(r'^$', views.Profile.as_view(), name='profile'),
+]
diff --git a/apps/profiles/views.py b/apps/profiles/views.py
new file mode 100644
index 0000000..d67ec90
--- /dev/null
+++ b/apps/profiles/views.py
@@ -0,0 +1,5 @@
+from django.views.generic.base import TemplateView
+
+
+class Profile(TemplateView):
+ template_name = "profiles/profile.html"
diff --git a/docker-compose.yml b/docker-compose.yml
index 592dec6..2da71c2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,11 +6,13 @@ services:
- db:db
- cache:cache
volumes:
+ - ./apps:/app/apps
+ - ./ojusomap:/app/ojusomap
- /containers/map/static:/app/static
- /containers/map/gunicorn.sock:/app/gunicorn.sock
env_file:
- - ./environment
- command: /bin/sh -c "python3 manage.py migrate && python3 manage.py collectstatic --noinput && gunicorn --bind 0.0.0.0:8000 ojusomap.wsgi"
+ - ./local.env
+ command: /bin/sh -c "python3 manage.py migrate && DEBUG=1 python3 manage.py collectstatic --noinput && gunicorn --bind 0.0.0.0:8000 ojusomap.wsgi"
db:
image: mdillon/postgis:9.6-alpine
diff --git a/environment.sh b/environment.sh
index ae0007c..a9bf588 100755
--- a/environment.sh
+++ b/environment.sh
@@ -1,45 +1 @@
-#!/bin/bash -e
-export DEBUG=0
-export ALLOWED_HOSTS=map.ojuso.org
-export DATABASE_HOST=localhost
-export DATABASE_NAME=postgres
-export DATABASE_PASSWORD=2xXKKS9zdrBX9QJaV5Z5NPTiiW8LtTiR4vAGSACddqFTrBdhgwZHKYnLqjJedAi3
-export EMAIL_HOST=mail.gandi.net
-export EMAIL_HOST_USER=admin@ojuso.org
-export EMAIL_HOST_PASSWORD=QN7yosrnch1le474H56mesVR1SRw6sfO3izJDZnJ6T62Cj9I57CplW6UYZY6VXsq7lLr868bIK3kSXGyWiSrAyWK
-export EMAIL_PORT=587
-export EMAIL_USE_TLS=1
-export SECRET_KEY=a3DfjSmWkSffsPscRscqaxGv6HsBN8VKL8Q4EU4QcdEckB8scogrMP4tv7Eo7LZw
-export SERVER_EMAIL=Ojuso\ Platform\ Notification\ \
-export POSTGRES_USER=postgres
-export POSTGRES_PASSWORD=2xXKKS9zdrBX9QJaV5Z5NPTiiW8LtTiR4vAGSACddqFTrBdhgwZHKYnLqjJedAi3
-export DISCOURSE_DB_SOCKET=/var/run/postgresql
-export DISCOURSE_DEVELOPER_EMAILS=admin@ojuso.org
-export DISCOURSE_HOSTNAME=forum.ojuso.org
-export DISCOURSE_SMTP_ADDRESS=mail.gandi.net
-export DISCOURSE_SMTP_PASSWORD=QN7yosrnch1le474H56mesVR1SRw6sfO3izJDZnJ6T62Cj9I57CplW6UYZY6VXsq7lLr868bIK3kSXGyWiSrAyWK
-export DISCOURSE_SMTP_PORT=587
-export DISCOURSE_SMTP_USER_NAME=admin@ojuso.org
-export DOCKER_HOST_IP=172.17.0.1
-export LANG=en_US.UTF-8
-export RAILS_ENV=production
-export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=40000
-export RUBY_GC_HEAP_INIT_SLOTS=400000
-export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=1.5
-export RUBY_GLOBAL_METHOD_CACHE_SIZE=131072
-export UNICORN_SIDEKIQS=1
-export UNICORN_WORKERS=4
-export WEBLATE_DEBUG=0
-export WEBLATE_LOGLEVEL=DEBUG
-export WEBLATE_SITE_TITLE=Ojuso\ Weblate
-export WEBLATE_ADMIN_NAME=Weblate\ Admin
-export WEBLATE_ADMIN_EMAIL=admin@ojuso.org
-export WEBLATE_ADMIN_PASSWORD=zPFPtHLsRRFAAcApeGd23aV6Hg66KpTkWs2becsMMoL9dTeKLNt3PfH5Bzhyna8q
-export WEBLATE_SERVER_EMAIL=noreply@ojuso.org
-export WEBLATE_DEFAULT_FROM_EMAIL=noreply@ojuso.org
-export WEBLATE_ALLOWED_HOSTS=*
-export WEBLATE_REGISTRATION_OPEN=1
-export WEBLATE_GITHUB_USERNAME=livmackintosh
-export WEBLATE_EMAIL_HOST=mail.gandi.net
-export WEBLATE_EMAIL_USER=admin@ojuso.org
-export WEBLATE_EMAIL_PASSWORD=QN7yosrnch1le474H56mesVR1SRw6sfO3izJDZnJ6T62Cj9I57CplW6UYZY6VXsq7lLr868bIK3kSXGyWiSrAyWK
+#!/bin/bash
diff --git a/local.env b/local.env
new file mode 100644
index 0000000..96ce936
--- /dev/null
+++ b/local.env
@@ -0,0 +1,17 @@
+# Django Configuration
+DEBUG=1
+ALLOWED_HOSTS=localhost
+DATABASE_HOST=db
+DATABASE_NAME=postgres
+DATABASE_PASSWORD=postgres
+EMAIL_HOST=mail
+EMAIL_HOST_USER=admin@ojuso.org
+EMAIL_HOST_PASSWORD=
+EMAIL_PORT=1025
+EMAIL_USE_TLS=0
+SECRET_KEY=kZQPK56roVhFkwPqFF9xZoHFlw38uVuXNCjtXor6FRiieiYds9ltuM6oZeZ75CEe
+SERVER_EMAIL=Ojuso Platform Notification
+
+# Postgres Database Setup
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=postgres
diff --git a/local.yml b/local.yml
new file mode 100644
index 0000000..b4eceb5
--- /dev/null
+++ b/local.yml
@@ -0,0 +1,41 @@
+version: "3"
+services:
+ map:
+ build: .
+ links:
+ - db:db
+ - cache:cache
+ - mailhog:mail
+ volumes:
+ - .containers/map/static:/app/static
+ - .containers/map/gunicorn.sock:/app/gunicorn.sock
+ - ./apps:/app/apps
+ - ./ojusomap:/app/ojusomap
+ - ./support:/app/support
+ ports:
+ - 8000:8000
+ env_file:
+ - ./local.env
+ command: /bin/sh -c "python3 manage.py collectstatic --noinput ; python3 manage.py runserver 0.0.0.0:8000"
+
+ db:
+ image: mdillon/postgis:9.6-alpine
+ volumes:
+ - .containers/db:/var/lib/postgresql/data
+ - ./support/postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
+ ports:
+ - "127.0.0.1:5432:5432"
+ env_file:
+ - ./local.env
+
+ mailhog:
+ image: mailhog/mailhog
+ ports:
+ - 1025:1025
+ - 8025:8025
+
+ cache:
+ image: memcached:1.4
+
+volumes:
+ weblate-data:
diff --git a/ojusomap/settings.py b/ojusomap/settings.py
index 3086da6..7b5aab8 100644
--- a/ojusomap/settings.py
+++ b/ojusomap/settings.py
@@ -35,12 +35,18 @@ ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost').split()
# Application definition
INSTALLED_APPS = [
+ 'apps.contact',
'apps.map',
+ 'apps.profiles',
'django.contrib.admin',
+ 'registration',
'django.contrib.auth',
'django.contrib.contenttypes',
+ 'django.contrib.humanize',
'django.contrib.sessions',
+ 'django.contrib.sites',
'django.contrib.messages',
+ 'whitenoise.runserver_nostatic',
'django.contrib.staticfiles',
'django.contrib.gis',
'bootstrap3',
@@ -48,13 +54,16 @@ INSTALLED_APPS = [
'compressor',
'crispy_forms',
'django_extensions',
+ 'envelope',
'leaflet',
+ 'moderation',
'rest_framework',
'rest_framework_gis',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
+ 'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
@@ -66,10 +75,12 @@ MIDDLEWARE = [
ROOT_URLCONF = 'ojusomap.urls'
+SITE_ID = 1
+
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
+ 'DIRS': [os.path.join(BASE_DIR, 'ojusomap/templates/')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
@@ -95,7 +106,6 @@ EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')
EMAIL_PORT = os.getenv('EMAIL_PORT', 25)
EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
EMAIL_SUBJECT_PREFIX = "Ojuso Platform"
-
SERVER_EMAIL = os.getenv('SERVER_EMAIL', 'root@localhost')
@@ -131,6 +141,8 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
+# Registration (Redux)
+ACCOUNT_ACTIVATION_DAYS = 3
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
@@ -163,10 +175,9 @@ STATIC_URL = os.getenv("STATIC_URL", '/static/')
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
- 'compressor.finders.CompressorFinder',
]
-
+STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Cache
# https://docs.djangoproject.com/en/1.11/topics/cache/
@@ -204,7 +215,7 @@ REST_FRAMEWORK = {
# Django Crispy Forms
# http://django-crispy-forms.readthedocs.io/en/latest/
-CRISPY_TEMPLATE_PACK = 'bootstrap3'
+CRISPY_TEMPLATE_PACK = 'bootstrap4'
# Django-Leaflet
# https://django-leaflet.readthedocs.io/en/latest/
diff --git a/ojusomap/templates/400.html b/ojusomap/templates/400.html
new file mode 100644
index 0000000..566549b
--- /dev/null
+++ b/ojusomap/templates/400.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Title
+
+
+
+
+
\ No newline at end of file
diff --git a/ojusomap/templates/403.html b/ojusomap/templates/403.html
new file mode 100644
index 0000000..206243c
--- /dev/null
+++ b/ojusomap/templates/403.html
@@ -0,0 +1,9 @@
+{% extends 'base_page.html' %}
+
+{% block title %}
+ Shucks.
+{% endblock %}
+
+{% block description %}
+Looks like you're not supposed to be here.
+{% endblock %}
diff --git a/ojusomap/templates/404.html b/ojusomap/templates/404.html
new file mode 100644
index 0000000..ae78068
--- /dev/null
+++ b/ojusomap/templates/404.html
@@ -0,0 +1,9 @@
+{% extends 'base_page.html' %}
+
+{% block title %}
+ Shucks.
+{% endblock %}
+
+{% block description %}
+You've taken a wrong turn and we can't find the page you're looking for.
+{% endblock %}
diff --git a/ojusomap/templates/500.html b/ojusomap/templates/500.html
new file mode 100644
index 0000000..0d4bd24
--- /dev/null
+++ b/ojusomap/templates/500.html
@@ -0,0 +1,9 @@
+{% extends 'base_page.html' %}
+
+{% block title %}
+ Shucks.
+{% endblock %}
+
+{% block description %}
+ An unknown server error occurred. Sorry about that.
+{% endblock %}
\ No newline at end of file
diff --git a/ojusomap/templates/base.html b/ojusomap/templates/base.html
new file mode 100644
index 0000000..d73a5fa
--- /dev/null
+++ b/ojusomap/templates/base.html
@@ -0,0 +1,66 @@
+{% spaceless %}
+{% load i18n %}
+{% load leaflet_tags %}
+{% load static %}
+
+
+
+
+
+ {% block page_title %}Ojuso{% endblock %}
+
+
+
+ {# Additional Stylesheets #}
+ {% block stylesheets %}
+
+ {% endblock %}
+
+
+
+
+
+ {% block body %}{% endblock %}
+
+
+ {# CDN Javascript #}
+
+ {% block scripts %}{% endblock %}
+
+{% endspaceless %}
diff --git a/ojusomap/templates/base_page.html b/ojusomap/templates/base_page.html
new file mode 100644
index 0000000..979aebc
--- /dev/null
+++ b/ojusomap/templates/base_page.html
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+
+{% block stylesheets %}
+ {{ block.super }}
+
+{% endblock %}
+
+{% block body %}
+
+
+
+
+
{% block title %}{% endblock %}
+
{% block description %}{% endblock %}
+
+
+ {% block content %}
+ {% endblock %}
+
+
+{% endblock %}
diff --git a/ojusomap/templates/base_with_jumbo.html b/ojusomap/templates/base_with_jumbo.html
new file mode 100644
index 0000000..cfb4f64
--- /dev/null
+++ b/ojusomap/templates/base_with_jumbo.html
@@ -0,0 +1,25 @@
+{% extends "base.html" %}
+
+{% block stylesheets %}
+ {{ block.super }}
+
+{% endblock %}
+
+{% block body %}
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+{% endblock %}
diff --git a/ojusomap/templates/registration/activate.html b/ojusomap/templates/registration/activate.html
new file mode 100644
index 0000000..8fce34f
--- /dev/null
+++ b/ojusomap/templates/registration/activate.html
@@ -0,0 +1 @@
+{% extends 'base_page.html' %}
diff --git a/ojusomap/templates/registration/activation_complete.html b/ojusomap/templates/registration/activation_complete.html
new file mode 100644
index 0000000..8fce34f
--- /dev/null
+++ b/ojusomap/templates/registration/activation_complete.html
@@ -0,0 +1 @@
+{% extends 'base_page.html' %}
diff --git a/ojusomap/templates/registration/activation_email.html b/ojusomap/templates/registration/activation_email.html
new file mode 100644
index 0000000..52852e8
--- /dev/null
+++ b/ojusomap/templates/registration/activation_email.html
@@ -0,0 +1,10 @@
+{% extends 'base_email.html' %}
+{% block content %}
+
+ {{user}},
+ To activate your account and log in,
click here.
+ This link will expire in 7 days so be sure do it soon.
+ See you on the activist trail!
+ - Animal Rights Map
+
+{% endblock %}
diff --git a/ojusomap/templates/registration/activation_email.txt b/ojusomap/templates/registration/activation_email.txt
new file mode 100644
index 0000000..e69de29
diff --git a/ojusomap/templates/registration/activation_email_subject.txt b/ojusomap/templates/registration/activation_email_subject.txt
new file mode 100644
index 0000000..2f9b306
--- /dev/null
+++ b/ojusomap/templates/registration/activation_email_subject.txt
@@ -0,0 +1 @@
+Activate your Ojuso Identity account.
\ No newline at end of file
diff --git a/ojusomap/templates/registration/login.html b/ojusomap/templates/registration/login.html
new file mode 100644
index 0000000..7117ebc
--- /dev/null
+++ b/ojusomap/templates/registration/login.html
@@ -0,0 +1,24 @@
+{% extends "base_page.html" %}
+{% load bootstrap3 %}
+{% load crispy_forms_tags %}
+{% load envelope_tags %}
+{% load i18n %}
+
+{% block page_title %}{% trans "Login"%} | Ojuso{% endblock %}
+
+{% block content %}
+
+
+
Login
+
{% trans "Welcome!" %}
+
+ {% bootstrap_messages %}
+
+
+{% endblock %}
diff --git a/ojusomap/templates/registration/logout.html b/ojusomap/templates/registration/logout.html
new file mode 100644
index 0000000..61f162c
--- /dev/null
+++ b/ojusomap/templates/registration/logout.html
@@ -0,0 +1,11 @@
+{% extends "base_page.html" %}
+{% load bootstrap3 %}
+{% load crispy_forms_tags %}
+{% load envelope_tags %}
+{% load i18n %}
+
+{% block page_name %}Registration{% endblock %}
+
+{% block content %}
+{% trans "You've just been logged out." %} {% trans "Click here to login again." %}
+{% endblock %}
diff --git a/ojusomap/templates/registration/password_reset_email.html b/ojusomap/templates/registration/password_reset_email.html
new file mode 100644
index 0000000..2e5caa9
--- /dev/null
+++ b/ojusomap/templates/registration/password_reset_email.html
@@ -0,0 +1,14 @@
+{% load i18n %}{% autoescape off %}
+{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
+
+{% trans "Please go to the following page and choose a new password:" %}
+{% block reset_link %}
+{{ protocol }}://{{ domain }}{% url 'auth_password_reset_confirm' uidb64=uid token=token %}
+{% endblock %}
+{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
+
+{% trans "Thanks for using our site!" %}
+
+{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
+
+{% endautoescape %}
\ No newline at end of file
diff --git a/ojusomap/templates/registration/registration_closed.html b/ojusomap/templates/registration/registration_closed.html
new file mode 100644
index 0000000..d1093a7
--- /dev/null
+++ b/ojusomap/templates/registration/registration_closed.html
@@ -0,0 +1,12 @@
+{% extends "base_page.html" %}
+{% load bootstrap3 %}
+{% block page_name %}Registration{% endblock %}
+
+{% block content %}
+
+
+
Registration
+
Account registration is currently closed. Come back soon!
+
+
+{% endblock %}
diff --git a/ojusomap/templates/registration/registration_complete.html b/ojusomap/templates/registration/registration_complete.html
new file mode 100644
index 0000000..e69de29
diff --git a/ojusomap/templates/registration/registration_form.html b/ojusomap/templates/registration/registration_form.html
new file mode 100644
index 0000000..49b1be3
--- /dev/null
+++ b/ojusomap/templates/registration/registration_form.html
@@ -0,0 +1,22 @@
+{% extends "base_page.html" %}
+{% load bootstrap3 %}
+{% load crispy_forms_tags %}
+{% load envelope_tags %}
+
+{% block page_name %}Registration{% endblock %}
+
+{% block content %}
+
+
+
Registration
+
Create your account and get active!
+
+ {% bootstrap_messages %}
+
+
+{% endblock %}
diff --git a/ojusomap/urls.py b/ojusomap/urls.py
index 953a5ad..41d1dd7 100644
--- a/ojusomap/urls.py
+++ b/ojusomap/urls.py
@@ -14,6 +14,7 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import include, url
+from django.urls import reverse
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.contrib.auth.models import User
@@ -51,8 +52,12 @@ urlpatterns = [
url(r'api/', include(apirouter.urls)),
url(r'^admin/', admin.site.urls),
url(r'^cas/', include('cas_server.urls', namespace='cas_server')),
+ # url(r'^contact/', include('apps.contact.urls'), name="contact")
]
urlpatterns += i18n_patterns(
+ url(r'^accounts/profile/', include('apps.profiles.urls')),
+ # url(r'^accounts/logout/?$', include('django.contrib.auth.views.logout'), {'next_page': '/'}),
+ url(r'^accounts/', include('registration.backends.default.urls')),
url(r'', include('apps.map.urls'), name="map"),
)
diff --git a/requirements.txt b/requirements.txt
index d364449..9e7725f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,14 +1,20 @@
appdirs==1.4.3
-Django==1.10.7
+brotlipy==0.7.0
+Django==1.11.6
django-appconf==1.0.2
django-bootstrap3==8.2.3
+django-braces==1.11.0
django-cas-server==0.8.0
django-compressor==2.1.1
django-countries==4.5
django-crispy-forms==1.6.1
+django-envelope==1.3
django-extensions==1.7.9
django-geojson==2.10.0
-django-leaflet==0.22.0
+#django-leaflet==0.22.0
+-e git://github.com/makinacorpus/django-leaflet.git@a43acc5fed6674b413a6fab0feeb7c44e67c2ca8#egg=django-leaflet
+django-moderation==0.5.0
+django-registration-redux==1.6
djangorestframework==3.6.3
djangorestframework-gis==0.11.2
gunicorn==19.7.1
@@ -25,3 +31,4 @@ requests==2.14.2
requests-futures==0.9.7
rjsmin==1.0.12
six==1.10.0
+whitenoise==3.3.1