1
0
mirror of https://github.com/krislamo/DJ-BaseSite synced 2024-09-19 20:20:35 +00:00

First Commit

This commit is contained in:
Kris Lamoureux 2012-10-13 07:35:48 -07:00
commit 80cdb11749
33 changed files with 1385 additions and 0 deletions

22
.gitattributes vendored Normal file
View File

@ -0,0 +1,22 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

40
.gitignore vendored Normal file
View File

@ -0,0 +1,40 @@
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Database
*.db
# Batch
Py2Start_SetupProject.bat
DjangoBatchManager.bat
# My personal config
config_.txt

10
LICENSE Normal file
View File

@ -0,0 +1,10 @@
Copyright (c) 2012, Kris Lamoureux
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name DJ-BaseSite nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

31
README Normal file
View File

@ -0,0 +1,31 @@
DJ-BaseSite was written in Python 2.7 and Django 1.4
Description:
DJ-BaseSite (or Django-BaseSite) is a customizable login and register system with required email activation. While most people just use django-registration, I decided to write my own to learn more about Django.
NOTE TO DEVS:
A deactivation system hasn't been added. The login system needs to check attempts and display a CAPTCHA after a certain amount, this isn't included either. It also doesn't have an account recovery option. I plan to implement all of these things in future updates.
DJ-BaseSite is released under the New BSD License (The BSD 3-Clause License), refer to the LICENSE file.
Instructions
1. Install Python 2.7 (python.org) and Django 1.4 (djangoproject.com)
2. Open up the config.txt file and change the data under CUSTOM VARIABLES to your information. The configuration variable meanings are below.
3. Execute the SetupProject.py script and enter the project name, it will create a project based on your configuration.
4. Run the SyncDB script or "python manage.py syncdb" and create a super user.
5. Run the RunServer script or "python manage.py runserver" and check out your new website at localhost:8000 or 127.0.0.1:8000 in your browser.
DJ-BaseSite uses Recaptcha to prevent bots from creating accounts, so you'll need to get private and public keys from the website: http://www.google.com/recaptcha
Configuration Variable Meanings.
baseurl - It is used to create activation and deactivation links ( in views.py in register_user() )
admin_name/email - adds a name and email to the ADMINS tuple in settings.py ( http://docs.djangoproject.com/en/dev/ref/settings/#admins )
secret_key - A secret randomly generated string of characters (don't use spaces) ( http://www.random.org/passwords/?mode=advanced )
captcha_publickey/privatekey - DJ-BaseSite uses Recaptcha to prevent bots from creating accounts, get keys at google.com/recaptcha
HOSTsmpt - The SMPT server address with the email used to send activation emails. If your email doesn't support SMPT I highly suggest GMAIL.
HOSTemail - The email address
HOSTpass - The email's password
NOTE: It creates a development project, DEBUG is set to True in the settings.py so it is in no condition for deployment.

132
SetupProject.py Normal file
View File

@ -0,0 +1,132 @@
'''
Copyright (c) 2012, Kris Lamoureux
All rights reserved.
Released under the New BSD.
'''
from os import mkdir
from shutil import copy
import os.path
import traceback
PROJECT_HOME = "myproject"
PROJECT = ""
HERE = os.path.dirname(os.path.realpath(__file__))
HERE = HERE.replace('\\','/')
REPLACEM = []
def add_REPLACE(a):
global REPLACEM
REPLACEM.append(a)
def main():
global PROJECT, PROJECT_HOME, HERE, REPLACEM
PROJECT = raw_input("New Project Name: ")
add_REPLACE(['<%myproject%>',PROJECT])
mkdir(PROJECT)
mkdir(PROJECT+'/'+PROJECT)
files = []
move = []
'''
The config system is a little picky, it works
but it can be written better, and I plan to
update it.
'''
f_hand = open("./config.txt")
config_list = f_hand.readlines()
for config_setting in config_list:
# take off '\n' at end of string
config_setting = config_setting[:-1]
# ignore comments.
if config_setting[0:2] == "//":
pass
# ignore empty lines
elif config_setting == "":
pass
# ignore newlines
elif config_setting == "\n":
pass
# add file variable. (format: <%myproject%>)
elif config_setting[0:1] == "v":
config_setting = config_setting.split(' ')
config_setting[1] = "<%"+config_setting[1]+"%>"
try:
add_REPLACE([config_setting[1],config_setting[2]])
except IndexError:
print "You have an empty variable in the configuration."
except:
traceback.print_exc()
raw_input()
quit()
# Create folder for an app.
elif config_setting[0:3] == "dir":
config_setting = config_setting.split(' ')
if not len(config_setting) == 3:
mkdir(HERE+'/'+PROJECT+'/'+PROJECT+'/'+config_setting[1])
else:
mkdir(HERE+'/'+PROJECT+'/'+config_setting[1])
else:
old_path = config_setting.split(' ')[0]
old_path= old_path.replace("%here%",HERE)
new_path = old_path.replace("myproject", PROJECT)
if config_setting.split(' ')[1] == '1':
files.append([old_path, new_path])
elif config_setting.split(' ')[1] == '2':
move.append([old_path, new_path])
else:
print "Something went wrong here.."
raw_input()
exit()
'''
END OF CONFIG SYSTEM.
'''
# Cycle through files, dynamic then static.
for dyn_file in files:
# Open file that has vars to replace (format: <%varname%>)
old_fileh = open(dyn_file[0])
old_file = old_fileh.read()
# Replace vars with data
for var in REPLACEM:
old_file = old_file.replace(var[0],var[1])
# Write new file
f = open(dyn_file[1], "w+")
f.write(old_file)
f.close()
# Static cycle.
stat_file_dir = ""
for sta_file in move:
if sta_file[1].find('.'):
stat_file_dir = os.path.dirname(sta_file[1])
copy(sta_file[0], stat_file_dir)
else:
copy(sta_file[0], sta_file[1])
print "Project: "+PROJECT+" is ready for development.\n"
try:
main()
except:
traceback.print_exc()
print "Press any key to quit."
raw_input()
quit()

95
config.txt Normal file
View File

@ -0,0 +1,95 @@
// Copyright (c) 2012, Kris Lamoureux
// All rights reserved.
// DJ-BaseSite is released under the New BSD License
// Read the LICENSE file.
// Put all comments on new lines on the first 2
// characters of the line.
// CUSTOM VARIABLES
v baseurl http://127.0.0.1:8000
v admin_name Kris619
v admin_email KrisPublicEmail@gmail.com
// Change this to something random and secret
// http://www.random.org/passwords/?mode=advanced
v secret_key WK9zRDBQCUSqzU64Jf5sxta4X9TnKDPHhuXwA7Zj
// Get free captcha keys here: google.com/recaptcha
v captcha_publickey asdf
v captcha_privatekey asdf
v HOSTsmtp smtp.gmail.com
v HOSTemail KrisPublicEmail@gmail.com
v HOSTpass HereItIs_HaveMyAccount
// !!!!!!HEY, PAY ATTENTION FOR A SECOND!!!!!!
// Don't mess with anything below unless you
// know what you are doing.
// DYNAMIC FILES
%here%/myproject/manage.py 1
%here%/myproject/myproject/wsgi.py 1
%here%/myproject/myproject/settings.py 1
%here%/myproject/myproject/urls.py 1
%here%/myproject/manage.py 1
// STATIC FILES
%here%/myproject/myproject/__init__.py 2
%here%/myproject/myproject/views.py 2
%here%/myproject/myproject/validation.py 2
%here%/myproject/myproject/captcha.py 2
%here%/myproject/RunServer.bat 2
%here%/myproject/SyncDB.bat 2
// Activation Email
%here%/myproject/myproject/activation_email.html 2
// Add these dir commands before actually adding
// the files from inside the folders.
// HTML/CSS
dir static
dir static/css
dir templates
%here%/myproject/myproject/static/css/default.css 2
%here%/myproject/myproject/templates/base.html 2
%here%/myproject/myproject/templates/index.html 2
%here%/myproject/myproject/templates/error.html 2
// Auth
dir templates/auth
%here%/myproject/myproject/templates/auth/activated.html 2
%here%/myproject/myproject/templates/auth/disabled.html 2
%here%/myproject/myproject/templates/auth/logged_in.html 2
%here%/myproject/myproject/templates/auth/logged_out.html 2
%here%/myproject/myproject/templates/auth/login.html 2
%here%/myproject/myproject/templates/auth/newaccount.html 2
%here%/myproject/myproject/templates/auth/registration.html 2
// Admin
dir templates/admin
%here%/myproject/myproject/templates/admin/base_site.html 2
// Backend Django app
dir backends root
%here%/myproject/backends/__init__.py 2
%here%/myproject/backends/AuthOverride.py 2
// Profile Django app
dir accountprofile root
%here%/myproject/accountprofile/__init__.py 2
%here%/myproject/accountprofile/models.py 2

1
myproject/RunServer.bat Normal file
View File

@ -0,0 +1 @@
@python manage.py runserver

1
myproject/SyncDB.bat Normal file
View File

@ -0,0 +1 @@
@python manage.py syncdb

View File

View File

@ -0,0 +1,7 @@
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
activated = models.BooleanField()
activatekey = models.CharField(max_length=25, blank=True)

View File

@ -0,0 +1,13 @@
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
class CaseInsensitiveModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username__iexact=username)
if user.check_password(password):
return user
else:
return None
except User.DoesNotExist:
return None

View File

10
myproject/manage.py Normal file
View File

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

View File

View File

@ -0,0 +1,7 @@
Hello <$user>,
Welcome to the website. Please activate your account "<$user>"
Activation link: <$activatelink>
If this is not you, I apologize for any inconveniences and you can disable the account and users will not be able to create another one.
Disable link: <$disablelink>

View File

@ -0,0 +1,94 @@
import urllib2, urllib
API_SSL_SERVER="https://www.google.com/recaptcha/api"
API_SERVER="http://www.google.com/recaptcha/api"
VERIFY_SERVER="www.google.com"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def displayhtml (public_key,
use_ssl = False,
error = None):
"""Gets the HTML to display for reCAPTCHA
public_key -- The public api key
use_ssl -- Should the request be sent over ssl?
error -- An error message to display (from RecaptchaResponse.error_code)"""
error_param = ''
if error:
error_param = '&error=%s' % error
if use_ssl:
server = API_SSL_SERVER
else:
server = API_SERVER
return """<script type="text/javascript" src="%(ApiServer)s/challenge?k=%(PublicKey)s%(ErrorParam)s"></script>
<noscript>
<iframe src="%(ApiServer)s/noscript?k=%(PublicKey)s%(ErrorParam)s" height="300" width="500" frameborder="0"></iframe><br />
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type='hidden' name='recaptcha_response_field' value='manual_challenge' />
</noscript>
""" % {
'ApiServer' : server,
'PublicKey' : public_key,
'ErrorParam' : error_param,
}
def submit (recaptcha_challenge_field,
recaptcha_response_field,
private_key,
remoteip):
"""
Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
for the request
recaptcha_challenge_field -- The value of recaptcha_challenge_field from the form
recaptcha_response_field -- The value of recaptcha_response_field from the form
private_key -- your reCAPTCHA private key
remoteip -- the user's ip address
"""
if not (recaptcha_response_field and recaptcha_challenge_field and
len (recaptcha_response_field) and len (recaptcha_challenge_field)):
return RecaptchaResponse (is_valid = False, error_code = 'incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, unicode):
return s.encode('utf-8')
return s
params = urllib.urlencode ({
'privatekey': encode_if_necessary(private_key),
'remoteip' : encode_if_necessary(remoteip),
'challenge': encode_if_necessary(recaptcha_challenge_field),
'response' : encode_if_necessary(recaptcha_response_field),
})
request = urllib2.Request (
url = "http://%s/recaptcha/api/verify" % VERIFY_SERVER,
data = params,
headers = {
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urllib2.urlopen (request)
return_values = httpresp.read ().splitlines ();
httpresp.close();
return_code = return_values [0]
if (return_code == "true"):
return RecaptchaResponse (is_valid=True)
else:
return RecaptchaResponse (is_valid=False, error_code = return_values [1])

View File

@ -0,0 +1,169 @@
'''
Copyright (c) 2012, Kris Lamoureux
All rights reserved.
DJ-BaseSite is released under the New BSD Liscense.
Please take a momemt to read the short 3 Clause LICENSE file.
'''
# Django settings for the "<%myproject%>" project.
import os
ROOTDIR = os.getcwd().replace('\\','/')
DEBUG = True
TEMPLATE_DEBUG = DEBUG
baseurl = "<%baseurl%>" # "example.com"
'''
You need to sign up at http://recaptcha.net/ for a public/private key to use their CAPTCHA service.
'''
captcha_publickey = "<%captcha_publickey%>"
captcha_privatekey = "<%captcha_privatekey%>"
EMAIL_USE_TLS = True
EMAIL_HOST = "<%HOSTsmtp%>"
EMAIL_HOST_USER = "<%HOSTemail%>"
EMAIL_HOST_PASSWORD = "<%HOSTpass%>"
EMAIL_PORT = 587
EMAIL_MESSAGE = ROOTDIR + "/<%myproject%>/activation_email.html"
ADMINS = (
("<%admin_name%>", "<%admin_email%>"),
)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': ROOTDIR + '/data.db',
}
}
# 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 = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = True
# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = True
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for static files.
# Example: "http://media.lawrence.com/static/"
STATIC_URL = '/static/'
# Additional locations of static files
STATICFILES_DIRS = (
ROOTDIR + "/<%myproject%>/static/",
)
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = "<%secret_key%>"
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = '<%myproject%>.urls'
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = '<%myproject%>.wsgi.application'
TEMPLATE_DIRS = (
ROOTDIR + "/<%myproject%>/templates/",
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'accountprofile'
)
AUTH_PROFILE_MODULE = "accountprofile.UserProfile"
# 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'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
AUTHENTICATION_BACKENDS = ('backends.AuthOverride.CaseInsensitiveModelBackend',)

View File

@ -0,0 +1,113 @@
/* APPLYS TO ALL TEMPLATES */
body {
font-family:"Comic Sans MS", cursive, sans-serif;
background-color: #FFFFFF;
color: #000000;
padding: 0;
margin: 0;
}
.content {
padding: 10px;
text-align: center;
}
.usernav a:link {color:#FFFFFF;} /* unvisited link */
.usernav a:visited {color:#FFFFFF;} /* visited link */
.usernav a:hover {color:#FFFFFF;} /* mouse over link */
.usernav a:active {color:#FFFFFF;} /* selected link */
.globalnav a:link {color:#000000;} /* unvisited link */
.globalnav a:visited {color:#000000;} /* visited link */
.globalnav a:hover {color:#000000;} /* mouse over link */
.globalnav a:active {color:#000000;} /* selected link */
.usernav {
text-align: right;
padding: 10px;
background-color: #000000;
}
.globalnav {
text-align: center;
font-size: 20px;
}
.humantest {
text-align: center;
}
/* HOME PAGE CSS */
/* LOGIN CSS */
.loginform {
text-align: center;
}
table.loginform td {
padding: 5px;
}
.loginout {
font-size: 25px;
}
.returnlink a:link {color:#000000;} /* unvisited link */
.returnlink a:visited {color:#000000;} /* visited link */
.returnlink a:hover {color:#000000;} /* mouse over link */
.returnlink a:active {color:#000000;} /* selected link */
/* REGISTER CSS */
.register_form {
text-align: center;
}
table.register_form td {
padding: 5px;
}
.asterisk {
color: #FF0000;
font-size: 17px;
padding: 3px;
}
.tagpsnrequire {
padding-top: 10px;
}
.newaccount {
font-size: 25px;
padding: 10px;
}
/* ERROR RELATED CSS */
.error {
color: #FF0000;
font-size: 25px;
padding: 12px;
text-align: center;
background-color: #000000;
font-family:"Lucida Console", Monaco, monospace
}
.register_error {
color: #FF0000;
font-size: 18px;
padding: 6px;
text-align: center;
background-color: #000000;
font-family:"Lucida Console", Monaco, monospace
}
.login_error {
color: #FF0000;
font-size: 18px;
padding: 6px;
text-align: center;
background-color: #000000;
font-family:"Lucida Console", Monaco, monospace
}

View File

@ -0,0 +1,10 @@
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans 'Admin Site' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'Administration' %}</h1>
{% endblock %}
{% block nav-global %}{% endblock %}

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<div class="activated">
Your account "{{user_name}}" has been activated.
<div class="activated_login"><a href="/login/">Login</a></div>
</div>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
<div>Your account has been disabled.</div>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block content %}
<div class="loginout">You have been logged in.</div>
<div class="returnlink"><a href="/">Return to the Main Page</a></div>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block content %}
<div class="loginout">You have been logged out.</div>
<div class="returnlink"><a href="/">Return to the Main Page</a></div>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block content %}
{{LOGIN_ERROR|safe}}
{% if login_errors %}
<div class="login_error">
Username and password did not match.
</div>
{% endif %}
<h1>Login</h1>
<form action="/login/" method="post">
{% csrf_token %}
<table class="loginform" align="center">
<tr>
<td>User</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>Pass</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Login"></td>
</tr>
</table>
</form>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
<div class="newaccount">
Account "{{accountname}}" successfully created. Check your email to activate it so you can start using the features on the site.
</div>
{% endblock %}

View File

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block content %}
{% if registration_errors %}
<div class="register_error">
<ul>
{% for an_error in registration_errors %}
<li>{{ an_error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<h1>Registration</h1>
<form action="/register/" method="post">
{% csrf_token %}
<table class="register_form" align="center">
<tr>
<td>User<span class="asterisk">*</span></td>
<td><input type="text" name="usern" /></td>
</tr>
<tr>
<td>Pass<span class="asterisk">*</span></td>
<td><input type="password" name="passw" /></td>
</tr>
<tr>
<td>Pass (again)<span class="asterisk">*</span></td>
<td><input type="password" name="repassw" /></td>
</tr>
<tr>
<td>Email<span class="asterisk">*</span></td>
<td><input type="text" name="email" /></td>
</tr>
<tr>
<td colspan="2" class="humantest">
{{captcha_test|safe}}
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Register"></td>
</tr>
</table>
<div class="required">Fields with the asterick '<span class="asterisk">*</span>' are required</div>
</form>
{% endblock %}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>{{title}}</title>
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="/static/css/default.css" />
</head>
<body>
<div class="usernav">
{{user_navigation|safe}}
</div>
<div class="globalnav">
{{global_navigation|safe}}
</div>
<div class="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
<div class="error">{{error}}</div>
<div class="returnlink"><a href="/">Return to the Main Page</a></div>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
<h1>Index Page, no content yet.</h1>
{% endblock %}

View File

@ -0,0 +1,31 @@
'''
Copyright (c) 2012, Kris Lamoureux
All rights reserved.
DJ-BaseSite is released under the New BSD Liscense.
Please take a momemt to read the short 3 Clause LICENSE file.
'''
from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('<%myproject%>.views',
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
url(r'^$', 'index'),
url(r'^login/$', 'login_user'),
url(r'^logout/$', 'logout_user'),
url(r'^register/$', 'register_user'),
url(r'^activate/$', 'activate_user'),
#url(r'^deactivate/$', 'activate_user'), Deactivate needs to be written.
)
urlpatterns += staticfiles_urlpatterns()

View File

@ -0,0 +1,72 @@
from re import match
from django.contrib.auth.models import User
'''
Return Meanings
None (no data to check)
-1 (data too long/short)
-2 (user exists)
False (no match)
data (matched, data is valid)
'''
def clean_username(data,length=30):
if data == "":
return None
if len(data) <= length:
if match("^[\w\d_]+$", data):
try:
user = User.objects.get(username__iexact=data)
return -2
except User.DoesNotExist:
return data
else:
return False
else:
return -1
def clean_email(data,length=254):
if data == "":
return None
if len(data) <= length:
if match("^[\w\d._%-+]+@[\w\d._%-]+.[\w]{2,6}$", data):
try:
user = User.objects.get(email=data)
return -2
except User.DoesNotExist:
return data
else:
return False
else:
return -1
def clean_password(data,length_min=5):
if data == "":
return None
if len(data) >= length_min:
return data
else:
return -1
def clean_key(data, length=25):
if data == "":
return None
if len(data) == 25:
if match("^[a-zABCDEFG0-9]+$", data):
return data
else:
return False
else:
return -1
def clean_usernameRE(data):
if match("^[\w\d_]+$", data):
return data
else:
return False

View File

@ -0,0 +1,359 @@
'''
Copyright (c) 2012, Kris Lamoureux
All rights reserved.
DJ-BaseSite is released under the New BSD Liscense.
Please take a momemt to read the short 3 Clause LICENSE file.
'''
# Built in imports
import random
import hashlib
# Responce imports
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, RequestContext
# Authentication/Session/Validation imports
from django.contrib.auth import authenticate, login, logout
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
# Below expected to be in same directory
import validation as v
import captcha
# Email imports
from django.core.mail import EmailMessage
from django.core import mail
# Variables from Settings.py
from settings import EMAIL_HOST_USER, baseurl, EMAIL_MESSAGE
# User Profile model
from accountprofile.models import UserProfile
# Website base title
base_title = "Base Site - "
# Global Site Navigation
def global_nav():
return '<a href="/">Home</a>'
# User Site Navigation
def user_nav(user):
if not user:
return '<a href="/login/">Login</a> | <a href="/register/">Register</a>'
else:
return '<a href="/account/">'+user+'</a> | <a href="/logout/">Logout</a>'
def get_or_create_profile(user):
try:
profile = user.get_profile()
except ObjectDoesNotExist:
profile = UserProfile(activated=True, user=user)
profile.save()
return profile
def get_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def UserActivationKey():
random.seed()
choices = "abcdefghijklmnopqrstuvwxyzABCDEFG0123456789"
word = ""
for i in range(25):
x = random.randrange(0,len(choices))
word = word + choices[x]
return word
''' PAGE REQUESTS '''
def index(request):
global base_title
global global_nav, user_nav
# Load Global Navigation and title.
title = base_title + "Home"
global_navigation=global_nav()
# Load user navigation based on the session
if request.user.is_authenticated():
user_navigation = user_nav(request.user.username)
else:
user_navigation = user_nav(False)
responce = render_to_response('index.html', locals())
return responce
def logout_user(request):
logout(request)
return render_to_response('auth/logged_out.html', locals())
def login_user(request):
global base_title
global global_nav, user_nav
title = base_title + "Login"
global_navigation=global_nav()
# If user is not logged on
if not request.user.is_authenticated():
# Return user navigation for an anonymous session
user_navigation = user_nav(False)
# If user has sent POST data
if request.method == 'POST':
# Collect user data and return user object
usern = request.POST['username']
passw = request.POST['password']
''' CLEAN '''
user = authenticate(username=usern, password=passw)
if user is not None:
# If user object exists it means the user is authenticated
# but you still need to check if the user.is_active
if user.is_active:
# Account is active and not disabled.
# ... but is it activated?
user_profile = get_or_create_profile(user)
if user_profile.activated:
# User account is activated (via email)
login(request, user)
user_name = user.username
responce = render_to_response('auth/logged_in.html', locals())
else:
# The account is not activated via email
error = "Please activate your account through email."
responce = render_to_response('error.html', locals())
else:
# The account is disabled. No login.
message = "Your account has been disabled."
responce = render_to_response('auth/disabled.html', locals())
else:
# No object so the username and password are invalid.
login_errors = True
responce = render_to_response(
'auth/login.html',
locals(),
context_instance=RequestContext(request)
)
else:
# User isn't online and hasn't sent any POST data, give them a login form.
responce = render_to_response(
'auth/login.html',
locals(),
context_instance=RequestContext(request)
)
else:
# User is logged on, don't let them login until he's logged out.
user_navigation = user_nav(request.user.username)
error = "You're already logged on."
responce = render_to_response(
'error.html',
locals()
)
return responce
def register_user(request):
global base_title
global global_nav, user_nav
title = base_title + "Register"
global_navigation=global_nav()
# If user is not logged on
if not request.user.is_authenticated():
# Return user navigation for an anonymous session
user_navigation = user_nav(False)
# Set up captcha html.
from settings import captcha_publickey, captcha_privatekey
captcha_test = captcha.displayhtml(captcha_publickey)
# If user has sent POST data (not logged in)
if request.method == 'POST':
registration_errors = [] # Error list
''' Check and validate data '''
# Is human?
HumanTestResult = captcha.submit(
request.POST["recaptcha_challenge_field"],
request.POST["recaptcha_response_field"],
captcha_privatekey,
get_ip(request)
)
# If not human: display errors
if HumanTestResult.is_valid:
# Matching passwords?
password = v.clean_password(request.POST["passw"])
if not request.POST["passw"] == request.POST["repassw"]:
registration_errors.append("Passwords don't match.")
if password == None:
registration_errors.append("No password entered.")
elif password == -1:
registration_errors.append("Passwords have to be at least 5 characters.")
# Username related errors
username = v.clean_username(request.POST["usern"])
if username == None:
registration_errors.append("No username entered.")
elif username == -2:
registration_errors.append("This username isn't available.")
elif username == -1:
registration_errors.append("Username's can only be 30 characters.")
elif username == False:
registration_errors.append("Username wasn't just characters numbers ")
# Email related errors
email = v.clean_email(request.POST["email"])
if email == None:
registration_errors.append("No email entered.")
elif email == -2:
registration_errors.append("This email already has an account.")
elif email == -1:
registration_errors.append("Emails can only be 245 characters.")
elif email == False:
registration_errors.append("Invalid email.")
# Invalid CAPTCHA, display only that error giving no more information to the bot
else:
registration_errors.append("Invalid human verification code.")
captcha_test = captcha.displayhtml(
captcha_publickey,
False,
HumanTestResult.error_code)
# If no errors: create user.
if len(registration_errors) == 0:
new_user = User.objects.create_user(
username,
email,
request.POST["repassw"]
)
new_user.is_active = True
new_user.save()
# Create activation key and user profile
activation_key = UserActivationKey()
profile = UserProfile(
activatekey=activation_key,
activated=False,
user=new_user)
profile.save()
# User is created and saved. Send an activation link via email
connection = mail.get_connection()
connection.open()
message_activateurl = baseurl+"/activate/?key="+str(activation_key)+"&user="+str(new_user.username)
message_deactivateurl = baseurl+"/deactivate/?key="+str(activation_key)+"&user="+str(new_user.username)
f = open(EMAIL_MESSAGE, 'r')
message = f.read()
message = message.replace("<$user>", str(new_user.username))
message = message.replace("<$activatelink>", message_activateurl)
message = message.replace("<$disablelink>", message_deactivateurl)
email = EmailMessage(
"Account Activation",
message,
EMAIL_HOST_USER,
[new_user.email])
email.send()
connection.close()
# Return new account page
accountname = new_user.username
responce = render_to_response(
'auth/newaccount.html',
locals(),
context_instance=RequestContext(request)
)
else:
# Return registration form with errors in registration_errors
responce = render_to_response(
'auth/registration.html',
locals(),
context_instance=RequestContext(request)
)
# If user hasn't sent POST data (not logged on)
else:
responce = render_to_response(
'auth/registration.html',
locals(),
context_instance=RequestContext(request)
)
# User is logged on
else:
user_navigation = user_nav(request.user.username)
error = "You cannot register while logged in."
responce = render_to_response(
'error.html',
locals()
)
return responce
def activate_user(request):
if request.method == 'GET':
# Check if data could be valid through regex
key = v.clean_key(request.GET["key"])
u_name = v.clean_usernameRE(request.GET["user"])
# If key and username are valid
if request.GET["key"] == key and u_name:
try:
# Check profile for key and compare.
user = User.objects.get(username=u_name)
user_profile = get_or_create_profile(user)
if user_profile.activatekey == key:
# Activate user
user_profile.activated = True
user_profile.save()
key_correct = True
else:
key_correct = False
except ObjectDoesNotExist:
key_correct = False
else:
key_correct = False
if key_correct:
user_name = user.username
responce = render_to_response(
'auth/activated.html',
locals()
)
else:
error = "Activation failed."
responce = render_to_response(
'error.html',
locals()
)
return responce

View File

@ -0,0 +1,28 @@
"""
WSGI config for <%myproject%> project.
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
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<%myproject%>.settings")
# 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.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)