Started the slow process of cleaning up the boiler plate TG2 web
appearance.
Added a "Register" link to the toolbar with the Login/Logout links.
Created the new RESTful controller, UserController. An unauthenticated
user can create a new account view the UserController.new method.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
web/projxp/controllers/root.py | 4 +-
web/projxp/controllers/user.py | 58 ++++++++++++++++++++++++++
web/projxp/templates/authentication.html | 67 ------------------------------
web/projxp/templates/header.html | 18 ++++----
web/projxp/templates/master.html | 59 +++++++++++++-------------
web/projxp/templates/register.html | 26 +++++++++++
6 files changed, 125 insertions(+), 107 deletions(-)
create mode 100644 web/projxp/controllers/user.py
delete mode 100644 web/projxp/templates/authentication.html
create mode 100644 web/projxp/templates/register.html
diff --git a/web/projxp/controllers/root.py b/web/projxp/controllers/root.py
index a4458b7..8634bad 100644
--- a/web/projxp/controllers/root.py
+++ b/web/projxp/controllers/root.py
@@ -29,6 +29,7 @@ from projxp.model import DBSession, metadata
from projxp.controllers.error import ErrorController
from projxp import model
from projxp.controllers.secure import SecureController
+from projxp.controllers.user import UserController
__all__ = ['RootController']
@@ -48,10 +49,9 @@ class RootController(BaseController):
"""
secc = SecureController()
-
admin = Catwalk(model, DBSession)
-
error = ErrorController()
+ users = UserController()
@expose('projxp.templates.index')
def index(self):
diff --git a/web/projxp/controllers/user.py b/web/projxp/controllers/user.py
new file mode 100644
index 0000000..271fd98
--- /dev/null
+++ b/web/projxp/controllers/user.py
@@ -0,0 +1,58 @@
+# user.py
+# Copyright (C) 2010, Darryl L. Pierce
+#
+# This file is part of ProjXP.
+#
+# ProjXP is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ProjXP is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ProjXP. If not, see <
http://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+"""Provides RESTful methods for working with users."""
+
+# turbogears imports
+from tg import expose, redirect, request, validate
+from formencode.validators import NotEmpty
+
+# third party imports
+#from pylons.i18n import ugettext as _
+#from repoze.what import predicates
+
+# project specific imports
+from tg.controllers import RestController
+from projxp.model import DBSession, metadata
+from projxp.model.auth import User
+
+class UserController(RestController):
+ #Uncomment this line if your controller requires an authenticated user
+ # allow_only = authorize.not_anonymous()
+
+ @expose('projxp.templates.register')
+ def new(self, **kw):
+ # TODO need to write a unit test to enforce that authorized users cannot
register
+ if request.identity:
+ redirect("/")
+ return dict(values=kw)
+
+ @validate({'login':NotEmpty,
+ 'email':NotEmpty,
+ 'password':NotEmpty,
+ 'confirmpassword':NotEmpty}, error_handler=new)
+ @expose()
+ def post(self, login, email, password, confirmpassword):
+ # TODO need to write a unit test to enforce that authorized users cannot
register
+ if request.identity:
+ redirect("/")
+ user = User(user_name=login, email_address=email)
+ user.password = password
+ DBSession.add(user)
+ redirect('/login')
diff --git a/web/projxp/templates/authentication.html
b/web/projxp/templates/authentication.html
deleted file mode 100644
index d86204b..0000000
--- a/web/projxp/templates/authentication.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html
xmlns="http://www.w3.org/1999/xhtml"
-
xmlns:py="http://genshi.edgewall.org/"
-
xmlns:xi="http://www.w3.org/2001/XInclude">
-
- <xi:include href="master.html" />
-
-<head>
- <meta content="text/html; charset=UTF-8"
http-equiv="content-type" py:replace="''"/>
- <title>Learning TurboGears 2.0: Quick guide to authentication.</title>
-</head>
-
-<body>
- ${sidebar_top()}
- ${sidebar_bottom()}
- <div id="getting_started">
- <h2>Authentication & Authorization in a TG2 site.</h2>
- <p>If you have access to this page, this means you have enabled authentication
and authorization
- in the quickstart to create your project.</p>
- <p>
- The paster command will have created a few specific controllers for you. But before
you
- go to play with those controllers you'll need to make sure your application has
been
- properly bootstapped.
- This is dead easy, here is how to do this:
- </p>
-
- <span class="code">
- paster setup-app development.ini
- </span>
-
- <p>
- inside your application's folder and you'll get a database setup (using the
preferences you have
- set in your development.ini file). This database will also have been prepopulated
with some
- default logins/passwords so that you can test the secured controllers and methods.
- </p>
- <p>
- To change the comportement of this setup-app command you just need to edit the
<span class="code">websetup.py</span> file.
- </p>
- <p>
- Now try to visiting the <a
href="${tg.url('/manage_permission_only')}">manage_permission_only</a>
URL. You will be challenged with a login/password form.
- </p>
- <p>
- Only managers are authorized to visit this method. You will need to log-in using:
- <p>
- <span class="code">
- login: manager
- </span>
- </p>
- <p>
- <span class="code">
- password: managepass
- </span>
- </p>
- </p>
- <p>
- Another protected resource is <a
href="${tg.url('/editor_user_only')}">editor_user_only</a>.
This one is protected by a different set of permissions.
- You will need to be <span class="code">editor</span> with a
password of <span class="code">editpass</span> to be able to access
it.
- </p>
- <p>
- The last kind of protected resource in this quickstarted app is a full so called
<a href="${tg.url('/secc')}">secure controller</a>. This
controller is protected globally.
- Instead of having a @require decorator on each method, we have set an allow_only
attribute at the class level. All the methods in this controller will
- require the same level of access. You need to be manager to access <a
href="${tg.url('/secc')}">secc</a> or <a
href="${tg.url('/secc/some_where')}">secc/some_where</a>.
- </p>
- </div>
-</body>
-</html>
diff --git a/web/projxp/templates/header.html b/web/projxp/templates/header.html
index 0429cd0..119acb2 100644
--- a/web/projxp/templates/header.html
+++ b/web/projxp/templates/header.html
@@ -1,12 +1,12 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
-<py:def function="header">
- <div id="header">
- <h1>
- Welcome to TurboGears 2
- <span class="subtitle">The Python web metaframework</span>
- </h1>
- </div>
-</py:def>
-</html>
\ No newline at end of file
+ <py:def function="header">
+ <div id="header">
+ <h1>
+ ProjXP
+ <span class="subtitle">The agile project management
system</span>
+ </h1>
+ </div>
+ </py:def>
+</html>
diff --git a/web/projxp/templates/master.html b/web/projxp/templates/master.html
index e267395..21d9008 100644
--- a/web/projxp/templates/master.html
+++ b/web/projxp/templates/master.html
@@ -1,44 +1,45 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <xi:include href="header.html" />
- <xi:include href="sidebars.html" />
- <xi:include href="footer.html" />
-<head py:match="head" py:attrs="select('@*')">
+ <xi:include href="header.html" />
+ <xi:include href="sidebars.html" />
+ <xi:include href="footer.html" />
+ <head py:match="head" py:attrs="select('@*')">
<meta content="text/html; charset=UTF-8"
http-equiv="content-type" py:replace="''"/>
<title py:replace="''">Your title goes here</title>
<meta py:replace="select('*')"/>
<link rel="stylesheet" type="text/css"
media="screen" href="${tg.url('/css/style.css')}" />
-</head>
+ </head>
-<body py:match="body" py:attrs="select('@*')">
- ${header()}
- <ul id="mainmenu">
- <li class="first"><a href="${tg.url('/')}"
class="${('', 'active')[defined('page') and
page==page=='index']}">Welcome</a></li>
- <li><a href="${tg.url('/about')}"
class="${('', 'active')[defined('page') and
page==page=='about']}">About</a></li>
- <li py:if="tg.auth_stack_enabled"><a
href="${tg.url('/auth')}" class="${('',
'active')[defined('page') and
page==page=='auth']}">Authentication</a></li>
- <li><a
href="http://groups.google.com/group/turbogears">Contact<...
- <span py:if="tg.auth_stack_enabled" py:strip="True">
+ <body py:match="body" py:attrs="select('@*')">
+ ${header()}
+ <ul id="mainmenu">
+ <li class="first"><a href="${tg.url('/')}"
class="${('', 'active')[defined('page') and
page==page=='index']}">Welcome</a></li>
+ <li><a href="${tg.url('/about')}"
class="${('', 'active')[defined('page') and
page==page=='about']}">About</a></li>
+ <li py:if="tg.auth_stack_enabled"><a
href="${tg.url('/auth')}" class="${('',
'active')[defined('page') and
page==page=='auth']}">Authentication</a></li>
+ <li><a
href="http://groups.google.com/group/turbogears">Contact<...
+ <span py:if="tg.auth_stack_enabled" py:strip="True">
<li py:if="not request.identity" id="login"
class="loginlogout"><a
href="${tg.url('/login')}">Login</a></li>
+ <li py:if="not request.identity" id="register"
class="loginlogout"><a
href="${tg.url('/register')}">Register</a></li>
<li py:if="request.identity" id="login"
class="loginlogout"><a
href="${tg.url('/logout_handler')}">Logout</a></li>
<li py:if="request.identity" id="admin"
class="loginlogout"><a
href="${tg.url('/admin')}">Admin</a></li>
- </span>
- </ul>
- <div id="content">
- <py:if test="defined('page')">
- <div class="currentpage">
- Now Viewing: <span py:replace="page"/>
- </div>
- </py:if>
- <py:with vars="flash=tg.flash_obj.render('flash',
use_js=False)">
+ </span>
+ </ul>
+ <div id="content">
+ <py:if test="defined('page')">
+ <div class="currentpage">
+ Now Viewing: <span py:replace="page"/>
+ </div>
+ </py:if>
+ <py:with vars="flash=tg.flash_obj.render('flash',
use_js=False)">
<div py:if="flash" py:content="XML(flash)" />
- </py:with>
- <div py:replace="select('*|text()')"/>
- <!-- End of content -->
- ${footer()}
- </div>
-</body>
+ </py:with>
+ <div py:replace="select('*|text()')"/>
+ <!-- End of content -->
+ ${footer()}
+ </div>
+ </body>
</html>
diff --git a/web/projxp/templates/register.html b/web/projxp/templates/register.html
new file mode 100644
index 0000000..b293e1b
--- /dev/null
+++ b/web/projxp/templates/register.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html
xmlns="http://www.w3.org/1999/xhtml"
+
xmlns:py="http://genshi.edgewall.org/"
+
xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+<meta content="text/html; charset=UTF-8" http-equiv="content-type"
py:replace="''"/>
+<title>User Registration Form</title>
+</head>
+
+<body>
+<div id="loginform">
+<form action="./" method="POST">
+ <h2><span>Login</span></h2>
+ <label for="login">Username:</label><input
type="text" id="login" name="login"
class="text"></input><br />
+ <label for="email">Email Address:</label><input
type="text" id="email" name="email"
class="text"></input><br />
+ <label for="password">Password:</label><input
type="password" id="password" name="password"
class="text"></input><br />
+ <label for="confirmpassword">Confirm Password:</label><input
type="password" id="confirmpassword" name="confirmpassword"
class="text"></input>
+ <input type="submit" id="submit" value="Register"
/>
+</form>
+</div>
+</body>
+</html>
--
1.6.6