This commit is contained in:
Matthew Dillon 2016-01-19 15:10:09 -07:00
commit 7ba937ef66
66 changed files with 1823 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
venv
*.py[cod]
*.pyc
__pycache__

2
AUTHORS.txt Normal file
View file

@ -0,0 +1,2 @@
Andre Breton <insightdd@gmail.com>
Matthew Ryan Dillon <matthewrdillon@gmail.com>

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2013-2016 CCDB Authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

25
README.md Normal file
View file

@ -0,0 +1,25 @@
# CCDB
A collections and contaminants database.
## Development Setup
$ pyvenv venv
$ source venv/bin/activate
$ pip install -r requirements/local.txt
$ createdb tucotuco
$ python manage.py migrate
$ python manage.py runserver
## Basic Commands
### Setting Up Your Users
To create a **normal user account**, just go to Sign Up and fill out the form.
Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your
console to see a simulated email verification message. Copy the link into your
browser. Now the user's email should be verified and ready to go.
To create a **superuser account**, use this command::
$ python manage.py createsuperuser

0
ccdb/__init__.py Normal file
View file

1
ccdb/contrib/__init__.py Normal file
View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.contrib.sites.models
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Site',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('domain', models.CharField(verbose_name='domain name', max_length=100, validators=[django.contrib.sites.models._simple_domain_name_validator])),
('name', models.CharField(verbose_name='display name', max_length=50)),
],
options={
'verbose_name_plural': 'sites',
'verbose_name': 'site',
'db_table': 'django_site',
'ordering': ('domain',),
},
managers=[
(b'objects', django.contrib.sites.models.SiteManager()),
],
),
]

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations
def update_site_forward(apps, schema_editor):
"""Set site domain and name."""
Site = apps.get_model("sites", "Site")
Site.objects.update_or_create(
id=settings.SITE_ID,
defaults={
"domain": "ccdb.info",
"name": "ccdb"
}
)
def update_site_backward(apps, schema_editor):
"""Revert site domain and name to default."""
Site = apps.get_model("sites", "Site")
Site.objects.update_or_create(
id=settings.SITE_ID,
defaults={
"domain": "example.com",
"name": "example.com"
}
)
class Migration(migrations.Migration):
dependencies = [
('sites', '0001_initial'),
]
operations = [
migrations.RunPython(update_site_forward, update_site_backward),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.contrib.sites.models
class Migration(migrations.Migration):
dependencies = [
('sites', '0002_set_site_domain_and_name'),
]
operations = [
migrations.AlterModelManagers(
name='site',
managers=[
('objects', django.contrib.sites.models.SiteManager()),
],
),
]

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,22 @@
.alert-debug {
background-color: #fff;
border-color: #d6e9c6;
color: #000; }
.alert-error {
background-color: #f2dede;
border-color: #eed3d7;
color: #b94a48; }
@media (max-width: 47.9em) {
.navbar-nav .nav-item {
display: inline-block;
float: none;
width: 100%; }
.navbar-nav .nav-item + .nav-item {
margin-left: 0; }
.nav.navbar-nav.pull-right {
float: none !important; } }
[hidden][style="display: block;"] {
display: block !important; }

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1 @@
/* Project specific Javascript goes here. */

View file

@ -0,0 +1,51 @@
// project specific CSS goes here
// Alert colors
$white: #fff;
$mint-green: #d6e9c6;
$black: #000;
$pink: #f2dede;
$dark-pink: #eed3d7;
$red: #b94a48;
// bootstrap alert CSS, translated to the django-standard levels of
// debug, info, success, warning, error
.alert-debug {
background-color: $white;
border-color: $mint-green;
color: $black;
}
.alert-error {
background-color: $pink;
border-color: $dark-pink;
color: $red;
}
// This is a fix for the bootstrap4 alpha release
@media (max-width: 47.9em) {
.navbar-nav .nav-item {
display: inline-block;
float: none;
width: 100%;
}
.navbar-nav .nav-item + .nav-item {
margin-left: 0;
}
.nav.navbar-nav.pull-right {
float: none !important;
}
}
// Display django-debug-toolbar.
// See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742
// and https://github.com/pydanny/cookiecutter-django/issues/317
[hidden][style="display: block;"] {
display: block !important;
}

9
ccdb/templates/404.html Normal file
View file

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block title %}Page Not found{% endblock %}
{% block content %}
<h1>Page Not found</h1>
<p>This is not the page you were looking for.</p>
{% endblock content %}

14
ccdb/templates/500.html Normal file
View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block title %}Server Error{% endblock %}
{% block content %}
<h1>Error</h1>
<h3>Sorry about that, it looks like something went wrong.</h3>
<p>
We track these errors automatically, but if the problem persists feel free
to contact us. In the meantime, try refreshing.
</p>
{% endblock content %}

View file

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block title %}
{% block head_title %}
{% endblock head_title %}
{% endblock title %}

View file

@ -0,0 +1,76 @@
{% extends "account/base.html" %}
{% block navbar_class-users:detail %}active{% endblock %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block head_title %}{% trans "Account" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "E-mail Addresses" %}</h2>
{% if user.emailaddress_set.all %}
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
<form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset class="blockLabels">
{% for emailaddress in user.emailaddress_set.all %}
<div class="ctrlHolder">
<label for="email_radio_{{ forloop.counter }}" class="{% if emailaddress.primary %}primary_email{% endif %}">
<input id="email_radio_{{ forloop.counter }}" type="radio" name="email" {% if emailaddress.primary %}checked="checked"{% endif %} value="{{ emailaddress.email }}"/>
{{ emailaddress.email }}
{% if emailaddress.verified %}
<span class="verified">{% trans "Verified" %}</span>
{% else %}
<span class="unverified">{% trans "Unverified" %}</span>
{% endif %}
{% if emailaddress.primary %}
<span class="primary">{% trans "Primary" %}</span>
{% endif %}
</label>
</div>
{% endfor %}
<div class="btn-group" role="group">
<input class="btn btn-primary" type="submit" value="{% trans 'Make Primary' %}" name="action_primary" />
<input class="btn btn-success" type="submit" value="{% trans 'Re-send Verification' %}" name="action_send" />
<input class="btn btn-danger" type="submit" value="{% trans 'Remove' %}" name="action_remove" />
</div>
</fieldset>
</form>
{% else %}
<p>
<strong>{% trans 'Warning:'%}</strong>
{% blocktrans %}
You currently do not have any e-mail address set up. You should
really add an e-mail address so you can receive notifications,
reset your password, etc.
{% endblocktrans %}
</p>
{% endif %}
<h2>{% trans "Add E-mail Address" %}</h2>
<form method="post" action="." class="add_email">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" name="action_add" type="submit">{% trans "Add E-mail" %}</button>
</form>
</div>
</div>
{% endblock %}
{% block extra_body %}
<script type="text/javascript">
(function() {
var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}";
var actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function(e) {
if (! confirm(message)) {
e.preventDefault();
}
});
}
})();
</script>
{% endblock %}

View file

@ -0,0 +1,35 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-xs-12">
<h2>{% trans "Confirm E-mail Address" %}</h2>
{% if confirmation %}
{% user_display confirmation.email_address.user as user_display %}
<p>
{% blocktrans with confirmation.email_address.email as email %}
Please confirm that <a href="mailto:{{ email }}">{{ email }}</a>
is an e-mail address for user {{ user_display }}.
{% endblocktrans %}
</p>
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="{% trans 'Confirm' %}" />
</form>
{% else %}
{% url 'account_email' as email_url %}
<p>
{% blocktrans %}
This e-mail confirmation link expired or is invalid. Please
<a href="{{ email_url }}">issue a new e-mail confirmation request</a>.
{% endblocktrans %}
</p>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-xs-12">
<h2>{% trans "Confirm E-mail Address" %}</h2>
{% user_display email_address.user as user_display %}
<p>
{% blocktrans with email_address.email as email %}
You have confirmed that <a href="mailto:{{ email }}">{{ email }}</a>
is an e-mail address for user {{ user_display }}.
{% endblocktrans %}
</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,25 @@
{% extends "account/base.html" %}
{% block navbar_class-account_login %}active{% endblock %}
{% load i18n %}
{% load account %}
{% load crispy_forms_tags %}
{% block head_title %}{% trans "Sign In" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Sign In" %}</h2>
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{{ form|crispy }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button id="sign-in-button" class="btn btn-primary" type="submit">{% trans "Sign In" %}</button>
<a class="button secondaryAction" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends "account/base.html" %}
{% block navbar_class-account_logout %}active{% endblock %}
{% load i18n %}
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Sign Out" %}</h2>
<p>{% trans 'Are you sure you want to sign out?' %}</p>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button class="btn btn-danger" type="submit">{% trans 'Sign Out' %}</button>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,18 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Change Password" %}</h2>
<form method="POST" action="./" class="password_change">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit" name="action">{% trans "Change Password" %}</button>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,38 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% load crispy_forms_tags %}
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Password Reset" %}</h2>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>
{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}
</p>
<form method="POST" action="./" class="password_reset">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit">{% trans "Reset My Password" %}</button>
</form>
<p>
{% blocktrans %}
Please contact us if you have any trouble resetting your password.
{% endblocktrans %}
</p>
</div>
</div>
{% endblock %}
{% block javascript %}
{{ block.super }}
<script>
$("#id_email").focus();
</script>
{% endblock javascript %}

View file

@ -0,0 +1,23 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-xs-12">
<h2>{% trans "Password Reset" %}</h2>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>
{% blocktrans %}
We have sent you an e-mail. Please contact us if you do not
receive it within a few minutes.
{% endblocktrans %}
</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,40 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-xs-12">
<h2>
{% if token_fail %}
{% trans "Bad Token" %}
{% else %}
{% trans "Change Password" %}
{% endif %}
</h2>
{% if token_fail %}
{% url 'account_reset_password' as passwd_reset_url %}
<p>
{% blocktrans %}
The password reset link was invalid, possibly because it has already
been used. Please request a <a href="{{ passwd_reset_url }}">new
password reset</a>.
{% endblocktrans %}
</p>
{% else %}
{% if form %}
<form method="POST" action="./">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit" name="action">{% trans "change password" %}</button>
</form>
{% else %}
<p>{% trans 'Your password is now changed.' %}</p>
{% endif %}
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Change Password" %}</h2>
<p>{% trans 'Your password is now changed.' %}</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,19 @@
{% extends "account/base.html" %}
{% load i18n crispy_forms_tags %}
{% block head_title %}{% trans "Set Password" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Set Password" %}</h2>
<form method="POST" action="./" class="password_set">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" name="action" value="{% trans "Set Password" %}"/>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,29 @@
{% extends "account/base.html" %}
{% block navbar_class-account_signup %}active{% endblock %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{% trans "Signup" %}{% endblock title %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h1>{% trans "Sign Up" %}</h1>
<p>
{% blocktrans %}
Already have an account? Then please
<a href="{{ login_url }}">sign in</a>.
{% endblocktrans %}
</p>
<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{{ form|crispy }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button id="sign-up-button" class="btn btn-primary" type="submit">{% trans "Sign Up" %}</button>
</form>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,14 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Sign Up Closed" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Sign Up Closed" %}</h2>
<p>{% trans "We are sorry, but the sign up is currently closed." %}</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Verify Your E-mail Address" %}</h2>
<p>
{% blocktrans %}
We have sent an e-mail to <a href="mailto:{{ email }}">{{ email }}</a>
for verification. Follow the link provided to finalize the signup
process. Please contact us if you do not receive it within a few minutes.
{% endblocktrans %}
</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,34 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-5">
<h2>{% trans "Verify Your E-mail Address" %}</h2>
{% url 'account_email' as email_url %}
<p>
{% blocktrans %}
This part of the site requires us to verify that you are who you claim
to be. For this purpose, we require that you verify ownership of your
e-mail address.
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
We have sent an e-mail to you for verification. Please click on the
link inside this e-mail. Please contact us if you do not receive it
within a few minutes.
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
<strong>Note:</strong> you can still <a href="{{ email_url }}">change
your e-mail address</a>.
{% endblocktrans %}
</p>
</div>
</div>
{% endblock %}

101
ccdb/templates/base.html Normal file
View file

@ -0,0 +1,101 @@
{% load staticfiles %}
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{% block title %}CCDB{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
{% block css %}
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/flatly/bootstrap.min.css" rel="stylesheet" integrity="sha256-sHwgyDk4CGNYom267UJX364ewnY4Bh55d53pxP5WDug= sha512-mkkeSf+MM3dyMWg3k9hcAttl7IVHe2BA1o/5xKLl4kBaP0bih7Mzz/DBy4y6cNZCHtE2tPgYBYH/KtEjOQYKxA==" crossorigin="anonymous">
<link href="{% static 'css/project.css' %}" rel="stylesheet">
{% endblock %}
</head>
<body>
<div>
<nav class="navbar navbar-default navbar-static-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'home' %}">CCDB</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
{% if request.user.is_authenticated %}
{% comment %}
<li class="nav-item {% block navbar_class-projects %}{% endblock %}">
<a class="nav-link" href="{% url 'projects:project_list' %}">
{% trans "Projects" %}
</a>
</li>
{% endcomment %}
{% endif %}
</ul>
<ul class="nav navbar-nav pull-right">
{% if request.user.is_authenticated %}
<li class="nav-item {% block navbar_class-users:detail %}{% endblock %}">
<a class="nav-link" href="{% url 'users:detail' request.user.username %}">
{% trans "My Profile" %}
</a>
</li>
<li class="nav-item {% block navbar_class-account_logout %}{% endblock %}">
<a class="nav-link" href="{% url 'account_logout' %}">
{% trans "Logout" %}
</a>
</li>
{% else %}
<li class="nav-item {% block navbar_class-account_signup %}{% endblock %}">
<a id="sign-up-link" class="nav-link" href="{% url 'account_signup' %}">
{% trans "Sign Up" %}
</a>
</li>
<li class="nav-item {% block navbar_class-account_login %}{% endblock %}">
<a id="log-in-link" class="nav-link" href="{% url 'account_login' %}">
{% trans "Log In" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
</div>
<div class="container">
{% if messages %}
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}
<p>PLACEHOLDER</p>
{% endblock content %}
</div>
{% block modal %}{% endblock modal %}
{% block javascript %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/js/bootstrap.js"></script>
<script src="{% static 'js/project.js' %}"></script>
{% endblock javascript %}
</body>
</html>

View file

@ -0,0 +1,58 @@
{% load querystring from django_tables2 %}
{% load title from django_tables2 %}
{% load trans blocktrans from i18n %}
{% load bootstrap3 %}
{% if table.page %}
<div class="table-container">
{% endif %}
{% block table %}
<table class="table table-striped"{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
{% block table.thead %}
<thead>
<tr>
{% for column in table.columns %}
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}><a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header|title }}</a></th>
{% else %}
<th {{ column.attrs.th.as_html }}>{{ column.header|title }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
{% endblock table.thead %}
{% block table.tbody %}
<tbody>
{% for row in table.page.object_list|default:table.rows %} {# support pagination #}
{% block table.tbody.row %}
<tr class="{% cycle "odd" "even" %}">
{% for column, cell in row.items %}
<td {{ column.attrs.td.as_html }}>{{ cell }}</td>
{% endfor %}
</tr>
{% endblock table.tbody.row %}
{% empty %}
{% if table.empty_text %}
{% block table.tbody.empty_text %}
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
{% endblock table.tbody.empty_text %}
{% endif %}
{% endfor %}
</tbody>
{% endblock table.tbody %}
{% block table.tfoot %}
<tfoot></tfoot>
{% endblock table.tfoot %}
</table>
{% endblock table %}
{% if table.page %}
{% block pagination %}
{% bootstrap_pagination table.page url=request.get_full_path %}
{% endblock pagination %}
{% endif %}
{% if table.page %}
</div>
{% endif %}

View file

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block navbar_class-about %}active{% endblock %}
{% block content %}
<h1>About</h1>
{% endblock content %}

View file

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
<h1>CCDB</h1>
<p>This page will have overview data.</p>
{% endblock content %}

View file

@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block navbar_class-users:detail %}active{% endblock %}
{% load static %}
{% block title %}User: {{ object.username }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<h1>{{ object.username }}</h1>
{% if object.name %}
<p>{{ object.name }}</p>
{% endif %}
</div>
</div>
{% if object == request.user %}
<!-- Action buttons -->
<div class="row">
<div class="col-sm-12 ">
<a class="btn btn-primary" href="{% url 'users:update' username=request.user.username %}">My Info</a>
<a class="btn btn-primary" href="{% url 'account_email' %}">E-Mail</a>
<!-- Your Stuff: Custom user template urls -->
</div>
</div>
<!-- End Action buttons -->
{% endif %}
{% endblock content %}

View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block navbar_class-users:detail %}active{% endblock %}
{% load crispy_forms_tags %}
{% load static %}
{% block title %}{{ user.username }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<h1>{{ user.username }}</h1>
<form class="form" method="POST" action="">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load static %}{% load i18n %}
{% block title %}Members{% endblock %}
{% block content %}
<h2>Users</h2>
<div class="list-group">
{% for user in user_list %}
<a href="{% url 'users:detail' user.username %}" class="list-group-item">
<h4 class="list-group-item-heading">{{ user.username }}</h4>
</a>
{% endfor %}
</div>
{% endblock content %}

1
ccdb/users/__init__.py Normal file
View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

37
ccdb/users/admin.py Normal file
View file

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from .models import User
class MyUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = User
class MyUserCreationForm(UserCreationForm):
error_message = UserCreationForm.error_messages.update({
'duplicate_username': 'This username has already been taken.'
})
class Meta(UserCreationForm.Meta):
model = User
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
@admin.register(User)
class UserAdmin(AuthUserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm

13
ccdb/users/middleware.py Normal file
View file

@ -0,0 +1,13 @@
import pytz
from django.utils import timezone
class TimezoneMiddleware(object):
def process_request(self, request):
if not request.user.is_anonymous():
tzname = request.user.timezone
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.utils.timezone
import django.contrib.auth.models
import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status', default=False)),
('username', models.CharField(max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], verbose_name='username', error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True)),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)),
('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', verbose_name='staff status', default=False)),
('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active', default=True)),
('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)),
('groups', models.ManyToManyField(related_name='user_set', blank=True, verbose_name='groups', to='auth.Group', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_query_name='user')),
('user_permissions', models.ManyToManyField(related_name='user_set', blank=True, verbose_name='user permissions', to='auth.Permission', help_text='Specific permissions for this user.', related_query_name='user')),
('name', models.CharField(max_length=255, verbose_name='Name of User', blank=True)),
],
options={
'verbose_name': 'user',
'abstract': False,
'verbose_name_plural': 'users',
},
managers=[
(b'objects', django.contrib.auth.models.UserManager()),
],
),
]

File diff suppressed because one or more lines are too long

View file

21
ccdb/users/models.py Normal file
View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.models import AbstractUser
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
import pytz
class User(AbstractUser):
name = models.CharField(_("Name of User"), blank=True, max_length=255)
timezone = models.CharField(_("Current Timezone"), max_length=255,
default="UTC", choices=[(x, x) for x in pytz.common_timezones],
blank=False)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('users:detail', kwargs={'username': self.username})

View file

View file

@ -0,0 +1,11 @@
import factory
class UserFactory(factory.django.DjangoModelFactory):
username = factory.Sequence(lambda n: 'user-{0}'.format(n))
email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n))
password = factory.PostGenerationMethodCall('set_password', 'password')
class Meta:
model = 'users.User'
django_get_or_create = ('username', )

View file

@ -0,0 +1,39 @@
from test_plus.test import TestCase
from ..admin import MyUserCreationForm
class TestMyUserCreationForm(TestCase):
def setUp(self):
self.user = self.make_user()
def test_clean_username_success(self):
# Instantiate the form with a new username
form = MyUserCreationForm({
'username': 'alamode',
'password1': '123456',
'password2': '123456',
})
# Run is_valid() to trigger the validation
valid = form.is_valid()
self.assertTrue(valid)
# Run the actual clean_username method
username = form.clean_username()
self.assertEqual('alamode', username)
def test_clean_username_false(self):
# Instantiate the form with the same username as self.user
form = MyUserCreationForm({
'username': self.user.username,
'password1': '123456',
'password2': '123456',
})
# Run is_valid() to trigger the validation, which is going to fail
# because the username is already taken
valid = form.is_valid()
self.assertFalse(valid)
# The form.errors dict should contain a single error called 'username'
self.assertTrue(len(form.errors) == 1)
self.assertTrue('username' in form.errors)

View file

@ -0,0 +1,18 @@
from test_plus.test import TestCase
class TestUser(TestCase):
def setUp(self):
self.user = self.make_user()
def test__str__(self):
self.assertEqual(
self.user.__str__(),
"testuser" # This is the default username for self.make_user()
)
def test_get_absolute_url(self):
self.assertEqual(
self.user.get_absolute_url(),
'/users/testuser/'
)

View file

@ -0,0 +1,66 @@
from django.test import RequestFactory
from test_plus.test import TestCase
from ..views import (
UserRedirectView,
UserUpdateView
)
class BaseUserTestCase(TestCase):
def setUp(self):
self.user = self.make_user()
self.factory = RequestFactory()
class TestUserRedirectView(BaseUserTestCase):
def test_get_redirect_url(self):
# Instantiate the view directly. Never do this outside a test!
view = UserRedirectView()
# Generate a fake request
request = self.factory.get('/fake-url')
# Attach the user to the request
request.user = self.user
# Attach the request to the view
view.request = request
# Expect: '/users/testuser/', as that is the default username for
# self.make_user()
self.assertEqual(
view.get_redirect_url(),
'/users/testuser/'
)
class TestUserUpdateView(BaseUserTestCase):
def setUp(self):
# call BaseUserTestCase.setUp()
super(TestUserUpdateView, self).setUp()
# Instantiate the view directly. Never do this outside a test!
self.view = UserUpdateView()
# Generate a fake request
request = self.factory.get('/fake-url')
# Attach the user to the request
request.user = self.user
# Attach the request to the view
self.view.request = request
def test_get_success_url(self):
# Expect: '/users/testuser/', as that is the default username for
# self.make_user()
self.assertEqual(
self.view.get_success_url(),
'/users/testuser/'
)
# TODO: write this test
# def test_get_redirected(self):
# Expect '/users/testuser01/' to redirect, because that isn't the
# currently logged in user
def test_get_object(self):
# Expect: self.user, as that is the request's user object
self.assertEqual(
self.view.get_object(),
self.user
)

14
ccdb/users/urls.py Normal file
View file

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django.conf.urls import url
from . import views
urlpatterns = [
url(regex=r'^$', view=views.UserListView.as_view(), name='list'),
url(regex=r'^redirect/$', view=views.UserRedirectView.as_view(), name='redirect'),
url(regex=r'^(?P<username>[\w.@+-]+)/$', view=views.UserDetailView.as_view(), name='detail'),
url(regex=r'^(?P<username>[\w.@+-]+)/update/$', view=views.UserUpdateView.as_view(), name='update'),
]

51
ccdb/users/views.py Normal file
View file

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django.core.urlresolvers import reverse
from django.views.generic import DetailView, ListView, RedirectView, UpdateView
from django.shortcuts import redirect
from braces.views import LoginRequiredMixin
from .models import User
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = "username"
slug_url_kwarg = "username"
class UserRedirectView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self):
return reverse("users:detail",
kwargs={"username": self.request.user.username})
class UserUpdateView(LoginRequiredMixin, UpdateView):
fields = ['name', 'timezone']
model = User
def dispatch(self, request, *args, **kwargs):
if request.user.username != kwargs.pop("username", None):
return redirect(reverse("users:detail",
kwargs={"username": request.user.username}))
return super(UserUpdateView, self).dispatch(request, *args, **kwargs)
def get_success_url(self):
return reverse("users:detail",
kwargs={"username": self.request.user.username})
def get_object(self):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
class UserListView(LoginRequiredMixin, ListView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = "username"
slug_url_kwarg = "username"

0
config/__init__.py Normal file
View file

View file

230
config/settings/base.py Normal file
View file

@ -0,0 +1,230 @@
"""
Django settings for CCDB
"""
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext_lazy as _
import environ
ROOT_DIR = environ.Path(__file__) - 3 # (/a/b/myfile.py - 3 = /)
APPS_DIR = ROOT_DIR.path('ccdb')
env = environ.Env()
# APP CONFIGURATION
# ------------------------------------------------------------------------------
DJANGO_APPS = (
# Default Django apps:
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Admin
'django.contrib.admin',
)
THIRD_PARTY_APPS = (
'crispy_forms', # Form layouts
'allauth', # registration
'allauth.account', # registration
'bootstrap3', # bootstrappin'
'django_tables2', # data grids
)
# Apps specific for this project go here.
LOCAL_APPS = (
'ccdb.users', # custom users app
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
# MIDDLEWARE CONFIGURATION
# ------------------------------------------------------------------------------
MIDDLEWARE_CLASSES = (
# Make sure djangosecure.middleware.SecurityMiddleware is listed first
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'ccdb.users.middleware.TimezoneMiddleware',
)
# MIGRATIONS CONFIGURATION
# ------------------------------------------------------------------------------
MIGRATION_MODULES = {
'sites': 'ccdb.contrib.sites.migrations'
}
# DEBUG
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = env.bool("DJANGO_DEBUG", False)
# FIXTURE CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = (
str(APPS_DIR.path('fixtures')),
)
# EMAIL CONFIGURATION
# ------------------------------------------------------------------------------
EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
# MANAGER CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = (
("""Matthew Ryan Dillon""", 'matthewrdillon@gmail.com'),
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS
# DATABASE CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
'default': env.db("DATABASE_URL", default="postgres:///ccdbdjango"),
}
DATABASES['default']['ATOMIC_REQUESTS'] = True
# GENERAL CONFIGURATION
# ------------------------------------------------------------------------------
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'UTC'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
LANGUAGE_CODE = 'en'
LANGUAGES = (
('en', _('English')),
)
LOCALE_PATHS = [
'locale',
]
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
# TEMPLATE CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#templates
TEMPLATES = [
{
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
'DIRS': [
str(APPS_DIR.path('templates')),
],
'OPTIONS': {
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
'debug': DEBUG,
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
# https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types
'loaders': [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
],
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
],
},
},
]
# See: http://django-crispy-forms.readthedocs.org/en/latest/install.html#template-packs
CRISPY_TEMPLATE_PACK = 'bootstrap3'
# STATIC FILE CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = str(ROOT_DIR('staticfiles'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = (
str(APPS_DIR.path('static')),
)
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
# MEDIA CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = str(APPS_DIR('media'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/'
# URL Configuration
# ------------------------------------------------------------------------------
ROOT_URLCONF = 'config.urls'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = 'config.wsgi.application'
# AUTHENTICATION CONFIGURATION
# ------------------------------------------------------------------------------
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
ACCOUNT_AUTHENTICATION_METHOD = 'username'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
# Custom user app defaults
# Select the correct user model
AUTH_USER_MODEL = 'users.User'
LOGIN_REDIRECT_URL = 'users:redirect'
LOGIN_URL = 'account_login'
# SLUGLIFIER
AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify'
# Location of root django.contrib.admin URL, use {% url 'admin:index' %}
ADMIN_URL = r'^admin/'
# Your common stuff: Below this line define 3rd party library settings

48
config/settings/local.py Normal file
View file

@ -0,0 +1,48 @@
'''
Local settings
- Run in Debug mode
- Use console backend for emails
- Add Django Debug Toolbar
'''
from .base import * # noqa
# DEBUG
# ------------------------------------------------------------------------------
DEBUG = env.bool('DJANGO_DEBUG', default=True)
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
# SECRET CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key only used for development and testing.
SECRET_KEY = env("DJANGO_SECRET_KEY", default='t69v7lq5ayk^k_)uyvjvpo(sljrcnbh)&$(rsqqjg-87160@^%')
# Mail settings
# ------------------------------------------------------------------------------
EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND',
default='django.core.mail.backends.console.EmailBackend')
# django-debug-toolbar
# ------------------------------------------------------------------------------
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
INSTALLED_APPS += ('debug_toolbar', )
INTERNAL_IPS = ('127.0.0.1', )
DEBUG_TOOLBAR_CONFIG = {
'DISABLE_PANELS': [
'debug_toolbar.panels.redirects.RedirectsPanel',
],
'SHOW_TEMPLATE_CONTEXT': True,
}
# TESTING
# ------------------------------------------------------------------------------
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
# Your local stuff: Below this line define 3rd party library settings

View file

@ -0,0 +1,165 @@
'''
Production Configurations
- Use Amazon's S3 for storing static files and uploaded media
- Use mailgun to send emails
'''
from __future__ import absolute_import, unicode_literals
from boto.s3.connection import OrdinaryCallingFormat
from django.utils import six
from .base import * # noqa
# SECRET CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Raises ImproperlyConfigured exception if DJANGO_SECRET_KEY not in os.environ
SECRET_KEY = env("DJANGO_SECRET_KEY")
# This ensures that Django will be able to detect a secure connection
# properly on Heroku.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Make sure djangosecure.middleware.SecurityMiddleware is listed first
MIDDLEWARE_CLASSES = SECURITY_MIDDLEWARE + MIDDLEWARE_CLASSES
# set this to 60 seconds and then to 518400 when you can prove it works
SECURE_HSTS_SECONDS = 60
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
"DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True)
SECURE_FRAME_DENY = env.bool("DJANGO_SECURE_FRAME_DENY", default=True)
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True)
SECURE_BROWSER_XSS_FILTER = True
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True
SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True)
# SITE CONFIGURATION
# ------------------------------------------------------------------------------
# Hosts/domain names that are valid for this site
# See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts
ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['ccdb.info'])
# END SITE CONFIGURATION
INSTALLED_APPS += ("gunicorn", )
# STORAGE CONFIGURATION
# ------------------------------------------------------------------------------
# Uploaded Media Files
# ------------------------
# See: http://django-storages.readthedocs.org/en/latest/index.html
INSTALLED_APPS += (
'storages',
)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = env('DJANGO_AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = env('DJANGO_AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = env('DJANGO_AWS_STORAGE_BUCKET_NAME')
AWS_AUTO_CREATE_BUCKET = True
AWS_QUERYSTRING_AUTH = False
AWS_S3_CALLING_FORMAT = OrdinaryCallingFormat()
# AWS cache settings, don't change unless you know what you're doing:
AWS_EXPIRY = 60 * 60 * 24 * 7
# TODO See: https://github.com/jschneier/django-storages/issues/47
# Revert the following and use str after the above-mentioned bug is fixed in
# either django-storage-redux or boto
AWS_HEADERS = {
'Cache-Control': six.b('max-age=%d, s-maxage=%d, must-revalidate' % (
AWS_EXPIRY, AWS_EXPIRY))
}
# URL that handles the media served from MEDIA_ROOT, used for managing
# stored files.
MEDIA_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME
# Static Assets
# ------------------------
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'
# EMAIL
# ------------------------------------------------------------------------------
DEFAULT_FROM_EMAIL = env('DJANGO_DEFAULT_FROM_EMAIL',
default='CCDB Admin <noreply@ccdb.info>')
EMAIL_BACKEND = 'django_mailgun.MailgunBackend'
MAILGUN_ACCESS_KEY = env('DJANGO_MAILGUN_API_KEY')
MAILGUN_SERVER_NAME = env('DJANGO_MAILGUN_SERVER_NAME')
EMAIL_SUBJECT_PREFIX = env("DJANGO_EMAIL_SUBJECT_PREFIX", default='[ccdb] ')
SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL)
# TEMPLATE CONFIGURATION
# ------------------------------------------------------------------------------
# See:
# https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.loaders.cached.Loader
TEMPLATES[0]['OPTIONS']['loaders'] = [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]),
]
# DATABASE CONFIGURATION
# ------------------------------------------------------------------------------
# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
DATABASES['default'] = env.db("DATABASE_URL")
# LOGGING CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s '
'%(process)d %(thread)d %(message)s'
},
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True
},
'django.security.DisallowedHost': {
'level': 'ERROR',
'handlers': ['console', 'mail_admins'],
'propagate': True
}
}
}
# Custom Admin URL, use {% url 'admin:index' %}
ADMIN_URL = env('DJANGO_ADMIN_URL')
# Your production stuff: Below this line define 3rd party library settings

32
config/urls.py Normal file
View file

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic import TemplateView
from django.views import defaults as default_views
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='pages/home.html'), name="home"),
url(r'^about/$', TemplateView.as_view(template_name='pages/about.html'), name="about"),
# Django Admin, use {% url 'admin:index' %}
url(settings.ADMIN_URL, include(admin.site.urls)),
# User management
url(r'^users/', include("ccdb.users.urls", namespace="users")),
url(r'^accounts/', include('allauth.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
# This allows the error pages to be debugged during development, just visit
# these url in browser to see how these error pages look like.
urlpatterns += [
url(r'^400/$', default_views.bad_request),
url(r'^403/$', default_views.permission_denied),
url(r'^404/$', default_views.page_not_found),
url(r'^500/$', default_views.server_error),
]

41
config/wsgi.py Normal file
View file

@ -0,0 +1,41 @@
"""
WSGI config for CCDB.
This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.
Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.
"""
import os
from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
application = get_wsgi_application()
# Use Whitenoise to serve static files
# See: https://whitenoise.readthedocs.org/
application = DjangoWhiteNoise(application)
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)

10
manage.py Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

32
requirements/base.txt Normal file
View file

@ -0,0 +1,32 @@
django==1.8.6
# Configuration
django-environ==0.4.0
django-secure==1.0.1
whitenoise==2.0.4
# Forms
django-braces==1.8.1
django-crispy-forms==1.5.2
# Views
django-extra-views==0.7.1
# DB
psycopg2==2.6.1
# User Registration
django-allauth==0.24.1
# Unicode slugification
unicode-slugify==0.1.3
django-autoslug==1.9.3
# Time zones support
pytz==2015.7
# Bootstrap
django-bootstrap3==6.2.2
# Data grids
django-tables2==1.0.4

4
requirements/local.txt Normal file
View file

@ -0,0 +1,4 @@
-r base.txt
Werkzeug==0.10.4
django-debug-toolbar==1.4

View file

@ -0,0 +1,12 @@
-r base.txt
# WSGI Handler
gevent==1.0.2
gunicorn==19.3.0
# Static and Media Storage
boto==2.38.0
django-storages-redux==1.3
# Mailgun Support
django-mailgun==0.8.0