From 049ca29e77082c8187529b5a7140cca40f2593a2 Mon Sep 17 00:00:00 2001 From: Livvy Mackintosh Date: Sun, 8 Oct 2017 21:21:51 +0100 Subject: [PATCH] Add registration and login templates plus UI stuff, moderation --- Dockerfile | 4 +- apps/contact/__init__.py | 0 apps/contact/forms.py | 11 + apps/contact/templates/envelope/contact.html | 21 + apps/contact/tests.py | 3 + apps/contact/urls.py | 7 + apps/contact/views.py | 12 + apps/map/admin.py | 8 +- apps/map/forms.py | 37 +- .../map/migrations/0004_auto_20171006_1559.py | 169 + .../map/migrations/0005_auto_20171006_2033.py | 165 + .../map/migrations/0006_auto_20171007_1349.py | 30 + apps/map/migrations/0007_casestudy_slug.py | 21 + .../migrations/0008_casestudy_date_created.py | 20 + .../map/migrations/0009_auto_20171007_1544.py | 26 + apps/map/models.py | 231 +- apps/map/moderator.py | 4 + apps/map/requirements.txt | 11 - apps/map/static/map/bundle.js | 17274 ++++++++++++++++ .../static/map/images/ojuso-logo-black.png | Bin 0 -> 23774 bytes .../static/map/images/ojuso-logo-white.png | Bin 0 -> 5081 bytes apps/map/templates/map/_nav.html | 15 - .../templates/map/base_with_container.html | 21 +- apps/map/templates/map/base_with_jumbo.html | 15 +- apps/map/templates/map/detail.html | 117 + apps/map/templates/map/form.html | 12 +- apps/map/templates/map/how_much_time.html | 47 + apps/map/templates/map/index.html | 9 +- apps/map/urls.py | 11 +- apps/map/views.py | 39 +- apps/profiles/__init__.py | 0 apps/profiles/admin.py | 3 + apps/profiles/apps.py | 5 + apps/profiles/migrations/__init__.py | 0 apps/profiles/models.py | 3 + apps/profiles/templates/profiles/profile.html | 2 + apps/profiles/tests.py | 3 + apps/profiles/urls.py | 7 + apps/profiles/views.py | 5 + docker-compose.yml | 6 +- environment.sh | 46 +- local.env | 17 + local.yml | 41 + ojusomap/settings.py | 21 +- ojusomap/templates/400.html | 10 + ojusomap/templates/403.html | 9 + ojusomap/templates/404.html | 9 + ojusomap/templates/500.html | 9 + ojusomap/templates/base.html | 66 + ojusomap/templates/base_page.html | 27 + ojusomap/templates/base_with_jumbo.html | 25 + ojusomap/templates/registration/activate.html | 1 + .../registration/activation_complete.html | 1 + .../registration/activation_email.html | 10 + .../registration/activation_email.txt | 0 .../registration/activation_email_subject.txt | 1 + ojusomap/templates/registration/login.html | 24 + ojusomap/templates/registration/logout.html | 11 + .../registration/password_reset_email.html | 14 + .../registration/registration_closed.html | 12 + .../registration/registration_complete.html | 0 .../registration/registration_form.html | 22 + ojusomap/urls.py | 5 + requirements.txt | 11 +- 64 files changed, 18607 insertions(+), 159 deletions(-) create mode 100644 apps/contact/__init__.py create mode 100644 apps/contact/forms.py create mode 100644 apps/contact/templates/envelope/contact.html create mode 100644 apps/contact/tests.py create mode 100644 apps/contact/urls.py create mode 100644 apps/contact/views.py create mode 100644 apps/map/migrations/0004_auto_20171006_1559.py create mode 100644 apps/map/migrations/0005_auto_20171006_2033.py create mode 100644 apps/map/migrations/0006_auto_20171007_1349.py create mode 100644 apps/map/migrations/0007_casestudy_slug.py create mode 100644 apps/map/migrations/0008_casestudy_date_created.py create mode 100644 apps/map/migrations/0009_auto_20171007_1544.py create mode 100644 apps/map/moderator.py delete mode 100644 apps/map/requirements.txt create mode 100644 apps/map/static/map/bundle.js create mode 100644 apps/map/static/map/images/ojuso-logo-black.png create mode 100644 apps/map/static/map/images/ojuso-logo-white.png delete mode 100644 apps/map/templates/map/_nav.html create mode 100644 apps/map/templates/map/detail.html create mode 100644 apps/map/templates/map/how_much_time.html create mode 100644 apps/profiles/__init__.py create mode 100644 apps/profiles/admin.py create mode 100644 apps/profiles/apps.py create mode 100644 apps/profiles/migrations/__init__.py create mode 100644 apps/profiles/models.py create mode 100644 apps/profiles/templates/profiles/profile.html create mode 100644 apps/profiles/tests.py create mode 100644 apps/profiles/urls.py create mode 100644 apps/profiles/views.py create mode 100644 local.env create mode 100644 local.yml create mode 100644 ojusomap/templates/400.html create mode 100644 ojusomap/templates/403.html create mode 100644 ojusomap/templates/404.html create mode 100644 ojusomap/templates/500.html create mode 100644 ojusomap/templates/base.html create mode 100644 ojusomap/templates/base_page.html create mode 100644 ojusomap/templates/base_with_jumbo.html create mode 100644 ojusomap/templates/registration/activate.html create mode 100644 ojusomap/templates/registration/activation_complete.html create mode 100644 ojusomap/templates/registration/activation_email.html create mode 100644 ojusomap/templates/registration/activation_email.txt create mode 100644 ojusomap/templates/registration/activation_email_subject.txt create mode 100644 ojusomap/templates/registration/login.html create mode 100644 ojusomap/templates/registration/logout.html create mode 100644 ojusomap/templates/registration/password_reset_email.html create mode 100644 ojusomap/templates/registration/registration_closed.html create mode 100644 ojusomap/templates/registration/registration_complete.html create mode 100644 ojusomap/templates/registration/registration_form.html 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 %} +
+ {% csrf_token %} + {% antispam_fields %} + {% crispy form %} +
+
+{% 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 %} +
+ +

20+ Minutes

+

Full Form (Preferred)

+
+ +

5-10 Minutes

+

Express Form

+
+
+{% 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( "