diff --git a/edbob/pyramid/static/css/crud.css b/edbob/pyramid/static/css/crud.css
index ccb9dd3..1422d63 100644
--- a/edbob/pyramid/static/css/crud.css
+++ b/edbob/pyramid/static/css/crud.css
@@ -13,7 +13,6 @@
div.crud {
font-size: 10pt;
margin: auto;
- width: 950px;
}
@@ -41,7 +40,7 @@ div.crud div.field-couple {
div.crud div.field-couple label {
display: block;
float: left;
- width: 135px;
+ width: 140px;
font-weight: bold;
margin-top: 2px;
white-space: nowrap;
diff --git a/edbob/pyramid/templates/people/index.mako b/edbob/pyramid/templates/people/index.mako
index f02a31f..577fedf 100644
--- a/edbob/pyramid/templates/people/index.mako
+++ b/edbob/pyramid/templates/people/index.mako
@@ -3,9 +3,9 @@
<%def name="title()">People%def>
-<%def name="menu()">
+<%def name="context_menu_items()">
% if request.has_perm('people.create'):
-
${h.link_to("Create a new Person", url('person.new'))}
+ ${h.link_to("Create a new Person", url('person.new'))}
% endif
%def>
diff --git a/edbob/pyramid/templates/roles/index.mako b/edbob/pyramid/templates/roles/index.mako
index 597c8f7..773153a 100644
--- a/edbob/pyramid/templates/roles/index.mako
+++ b/edbob/pyramid/templates/roles/index.mako
@@ -3,8 +3,8 @@
<%def name="title()">Roles%def>
-<%def name="menu()">
- ${h.link_to("Create a new Role", url('role.new'))}
+<%def name="context_menu_items()">
+ ${h.link_to("Create a new Role", url('role.new'))}
%def>
${parent.body()}
diff --git a/edbob/pyramid/templates/users/user.mako b/edbob/pyramid/templates/users/crud.mako
similarity index 58%
rename from edbob/pyramid/templates/users/user.mako
rename to edbob/pyramid/templates/users/crud.mako
index 73e93e8..c9314e9 100644
--- a/edbob/pyramid/templates/users/user.mako
+++ b/edbob/pyramid/templates/users/crud.mako
@@ -3,8 +3,8 @@
<%def name="crud_name()">User%def>
-<%def name="menu()">
- ${h.link_to("Back to Users", url('users.list'))}
+<%def name="context_menu_items()">
+ ${h.link_to("Back to Users", url('users.list'))}
%def>
${parent.body()}
diff --git a/edbob/pyramid/templates/users/edit.mako b/edbob/pyramid/templates/users/edit.mako
new file mode 100644
index 0000000..77fd24c
--- /dev/null
+++ b/edbob/pyramid/templates/users/edit.mako
@@ -0,0 +1,2 @@
+<%inherit file="/users/crud.mako" />
+${parent.body()}
diff --git a/edbob/pyramid/templates/users/index.mako b/edbob/pyramid/templates/users/index.mako
index 6494aaf..493a309 100644
--- a/edbob/pyramid/templates/users/index.mako
+++ b/edbob/pyramid/templates/users/index.mako
@@ -3,8 +3,8 @@
<%def name="title()">Users%def>
-<%def name="menu()">
- ${h.link_to("Create a new User", url('user.new'))}
+<%def name="context_menu_items()">
+ ${h.link_to("Create a new User", url('user.new'))}
%def>
${parent.body()}
diff --git a/edbob/pyramid/templates/users/new.mako b/edbob/pyramid/templates/users/new.mako
new file mode 100644
index 0000000..77fd24c
--- /dev/null
+++ b/edbob/pyramid/templates/users/new.mako
@@ -0,0 +1,2 @@
+<%inherit file="/users/crud.mako" />
+${parent.body()}
diff --git a/edbob/pyramid/views/crud.py b/edbob/pyramid/views/crud.py
index 0c9abfc..3b34e94 100644
--- a/edbob/pyramid/views/crud.py
+++ b/edbob/pyramid/views/crud.py
@@ -28,8 +28,8 @@
# from pyramid.renderers import render_to_response
# from pyramid.httpexceptions import HTTPException, HTTPFound, HTTPOk, HTTPUnauthorized
-import transaction
-from pyramid.httpexceptions import HTTPFound, HTTPException
+# import transaction
+from pyramid.httpexceptions import HTTPFound
# import sqlahelper
@@ -97,6 +97,9 @@ class Crud(object):
def post_sync(self, fs):
pass
+ def validation_failed(self, fs):
+ pass
+
def crud(self, obj=None):
if obj is None:
obj = self.mapped_class
@@ -126,15 +129,14 @@ class Crud(object):
result = None
- with transaction.manager:
- fs.sync()
- result = self.post_sync(fs)
- if not result:
- Session.add(fs.model)
- Session.flush()
- self.request.session.flash('%s "%s" has been %s.' % (
- fs.crud_title, fs.get_display_text(),
- 'updated' if fs.edit else 'created'))
+ fs.sync()
+ result = self.post_sync(fs)
+ if not result:
+ Session.add(fs.model)
+ Session.flush()
+ self.request.session.flash('%s "%s" has been %s.' % (
+ fs.crud_title, fs.get_display_text(),
+ 'updated' if fs.edit else 'created'))
if result:
return result
@@ -144,6 +146,8 @@ class Crud(object):
return HTTPFound(location=self.home_url)
+ self.validation_failed(fs)
+
# TODO: This probably needs attention.
if not fs.edit:
fs.allow_continue = True
@@ -163,8 +167,7 @@ class Crud(object):
uuid = self.request.matchdict['uuid']
obj = Session.query(self.mapped_class).get(uuid) if uuid else None
assert obj
- with transaction.manager:
- Session.delete(obj)
+ Session.delete(obj)
return HTTPFound(location=self.home_url)
@classmethod
@@ -201,109 +204,109 @@ class Crud(object):
permission=kw['permission'], http_cache=0)
-def crud(request, cls, fieldset_factory, home=None, delete=None, post_sync=None, pre_render=None):
- """
- Adds a common CRUD mechanism for objects.
+# def crud(request, cls, fieldset_factory, home=None, delete=None, post_sync=None, pre_render=None):
+# """
+# Adds a common CRUD mechanism for objects.
- ``cls`` should be a SQLAlchemy-mapped class, presumably deriving from
- :class:`edbob.Object`.
+# ``cls`` should be a SQLAlchemy-mapped class, presumably deriving from
+# :class:`edbob.Object`.
- ``fieldset_factory`` must be a callable which accepts the fieldset's
- "model" as its only positional argument.
+# ``fieldset_factory`` must be a callable which accepts the fieldset's
+# "model" as its only positional argument.
- ``home`` will be used as the redirect location once a form is fully
- validated and data saved. If you do not speficy this parameter, the
- user will be redirected to be the CRUD page for the new object (e.g. so
- an object may be created before certain properties may be edited).
+# ``home`` will be used as the redirect location once a form is fully
+# validated and data saved. If you do not speficy this parameter, the
+# user will be redirected to be the CRUD page for the new object (e.g. so
+# an object may be created before certain properties may be edited).
- ``delete`` may either be a string containing a URL to which the user
- should be redirected after the object has been deleted, or else a
- callback which will be executed *instead of* the normal algorithm
- (which is merely to delete the object via the Session).
+# ``delete`` may either be a string containing a URL to which the user
+# should be redirected after the object has been deleted, or else a
+# callback which will be executed *instead of* the normal algorithm
+# (which is merely to delete the object via the Session).
- ``post_sync`` may be a callback which will be executed immediately
- after :meth:`FieldSet.sync()` is called, i.e. after validation as well.
+# ``post_sync`` may be a callback which will be executed immediately
+# after :meth:`FieldSet.sync()` is called, i.e. after validation as well.
- ``pre_render`` may be a callback which will be executed after any POST
- processing has occured, but just before rendering.
- """
+# ``pre_render`` may be a callback which will be executed after any POST
+# processing has occured, but just before rendering.
+# """
- uuid = request.params.get('uuid')
- obj = Session.query(cls).get(uuid) if uuid else cls
- assert obj
+# uuid = request.params.get('uuid')
+# obj = Session.query(cls).get(uuid) if uuid else cls
+# assert obj
- if request.params.get('delete'):
- if delete:
- if isinstance(delete, basestring):
- with transaction.manager:
- Session.delete(obj)
- return HTTPFound(location=delete)
- with transaction.manager:
- res = delete(obj)
- if res:
- return res
- else:
- with transaction.manager:
- Session.delete(obj)
- if not home:
- raise ValueError("Must specify 'home' or 'delete' url "
- "in call to crud()")
- return HTTPFound(location=home)
+# if request.params.get('delete'):
+# if delete:
+# if isinstance(delete, basestring):
+# with transaction.manager:
+# Session.delete(obj)
+# return HTTPFound(location=delete)
+# with transaction.manager:
+# res = delete(obj)
+# if res:
+# return res
+# else:
+# with transaction.manager:
+# Session.delete(obj)
+# if not home:
+# raise ValueError("Must specify 'home' or 'delete' url "
+# "in call to crud()")
+# return HTTPFound(location=home)
- fs = fieldset_factory(obj)
+# fs = fieldset_factory(obj)
- # if not fs.readonly and self.request.params.get('fieldset'):
- # fs.rebind(data=self.request.params)
- # if fs.validate():
- # fs.sync()
- # if post_sync:
- # res = post_sync(fs)
- # if isinstance(res, HTTPFound):
- # return res
- # if self.request.params.get('partial'):
- # self.Session.flush()
- # return self.json_success(uuid=fs.model.uuid)
- # return HTTPFound(location=self.request.route_url(objects, action='index'))
+# # if not fs.readonly and self.request.params.get('fieldset'):
+# # fs.rebind(data=self.request.params)
+# # if fs.validate():
+# # fs.sync()
+# # if post_sync:
+# # res = post_sync(fs)
+# # if isinstance(res, HTTPFound):
+# # return res
+# # if self.request.params.get('partial'):
+# # self.Session.flush()
+# # return self.json_success(uuid=fs.model.uuid)
+# # return HTTPFound(location=self.request.route_url(objects, action='index'))
- if not fs.readonly and request.POST:
- fs.rebind(data=request.params)
- if fs.validate():
- with transaction.manager:
- fs.sync()
- if post_sync:
- res = post_sync(fs)
- if res:
- return res
+# if not fs.readonly and request.POST:
+# fs.rebind(data=request.params)
+# if fs.validate():
+# with transaction.manager:
+# fs.sync()
+# if post_sync:
+# res = post_sync(fs)
+# if res:
+# return res
- if request.params.get('partial'):
- # Session.flush()
- # return self.json_success(uuid=fs.model.uuid)
- assert False, "need to fix this"
+# if request.params.get('partial'):
+# # Session.flush()
+# # return self.json_success(uuid=fs.model.uuid)
+# assert False, "need to fix this"
- if not home:
- fs.model = Session.merge(fs.model)
- home = request.current_route_url(uuid=fs.model.uuid)
- request.session.flash("%s \"%s\" has been %s." % (
- fs.crud_title, fs.get_display_text(),
- 'updated' if fs.edit else 'created'))
- return HTTPFound(location=home)
+# if not home:
+# fs.model = Session.merge(fs.model)
+# home = request.current_route_url(uuid=fs.model.uuid)
+# request.session.flash("%s \"%s\" has been %s." % (
+# fs.crud_title, fs.get_display_text(),
+# 'updated' if fs.edit else 'created'))
+# return HTTPFound(location=home)
- data = {'fieldset': fs, 'crud': True}
+# data = {'fieldset': fs, 'crud': True}
- if pre_render:
- res = pre_render(fs)
- if res:
- if isinstance(res, HTTPException):
- return res
- data.update(res)
+# if pre_render:
+# res = pre_render(fs)
+# if res:
+# if isinstance(res, HTTPException):
+# return res
+# data.update(res)
- # data = {'fieldset':fs}
- # if self.request.params.get('partial'):
- # return render_to_response('/%s/crud_partial.mako' % objects,
- # data, request=self.request)
- # return data
+# # data = {'fieldset':fs}
+# # if self.request.params.get('partial'):
+# # return render_to_response('/%s/crud_partial.mako' % objects,
+# # data, request=self.request)
+# # return data
- return data
+# return data
# class needs_perm(object):
diff --git a/edbob/pyramid/views/users.py b/edbob/pyramid/views/users.py
index ff647e8..68f0551 100644
--- a/edbob/pyramid/views/users.py
+++ b/edbob/pyramid/views/users.py
@@ -26,78 +26,63 @@
``edbob.pyramid.views.users`` -- User Views
"""
-import transaction
-from pyramid.httpexceptions import HTTPFound
+from webhelpers.html import literal, tags
import formalchemy
from formalchemy.fields import SelectFieldRenderer
-from webhelpers.html import literal
-from webhelpers.html.tags import hidden, link_to, password
import edbob
from edbob.db.auth import set_user_password
-from edbob.pyramid import filters
-from edbob.pyramid import forms
-from edbob.pyramid import grids
from edbob.pyramid import Session
+from edbob.pyramid.filters import filter_ilike
+from edbob.pyramid.grids import sorter
+from edbob.pyramid.views import GridView
+from edbob.pyramid.views.crud import Crud
-def filter_map():
- return filters.get_filter_map(
- edbob.User,
- ilike=['username'],
- person=filters.filter_ilike(edbob.Person.display_name))
+class UserGrid(GridView):
-def search_config(request, fmap):
- return filters.get_search_config(
- 'users.list', request, fmap,
- include_filter_username=True,
- filter_type_username='lk')
+ mapped_class = edbob.User
+ route_name = 'users.list'
+ route_prefix = 'user'
-def search_form(config):
- return filters.get_search_form(config)
+ def join_map(self):
+ return {
+ 'person':
+ lambda q: q.outerjoin(edbob.Person),
+ }
-def grid_config(request, search, fmap):
- return grids.get_grid_config(
- 'users.list', request, search,
- filter_map=fmap, sort='username')
+ def filter_map(self):
+ return self.make_filter_map(
+ ilike=['username'],
+ person=filter_ilike(edbob.Person.display_name))
-def sort_map():
- return grids.get_sort_map(
- edbob.User, ['username'],
- person=grids.sorter(edbob.Person.display_name))
+ def search_config(self, fmap):
+ return self.make_search_config(
+ fmap,
+ include_filter_username=True,
+ filter_type_username='lk',
+ include_filter_person=True,
+ filter_type_person='lk')
-def query(config):
- jmap = {'person': lambda q: q.outerjoin(edbob.Person)}
- smap = sort_map()
- q = Session.query(edbob.User)
- q = filters.filter_query(q, config, jmap)
- q = grids.sort_query(q, config, smap, jmap)
- return q
+ def grid_config(self, search, fmap):
+ return self.make_grid_config(search, fmap,
+ sort='username')
+ def sort_map(self):
+ return self.make_sort_map(
+ 'username',
+ person=sorter(edbob.Person.display_name))
-def users(context, request):
-
- fmap = filter_map()
- config = search_config(request, fmap)
- search = search_form(config)
- config = grid_config(request, search, fmap)
- users = grids.get_pager(query, config)
-
- g = forms.AlchemyGrid(
- edbob.User, users, config,
- gridurl=request.route_url('users.list'),
- objurl='user.edit')
-
- g.configure(
- include=[
- g.username,
- g.person,
- ],
- readonly=True)
-
- grid = g.render(class_='clickable users')
- return grids.render_grid(request, grid, search)
+ def grid(self, data, config):
+ g = self.make_grid(data, config)
+ g.configure(
+ include=[
+ g.username,
+ g.person,
+ ],
+ readonly=True)
+ return g
class _RolesFieldRenderer(SelectFieldRenderer):
@@ -108,8 +93,8 @@ class _RolesFieldRenderer(SelectFieldRenderer):
for uuid in self.value:
role = roles.get(uuid)
res += literal('%s' % (
- link_to(role.name,
- self.request.route_url('role.edit', uuid=role.uuid))))
+ tags.link_to(role.name,
+ self.request.route_url('role.edit', uuid=role.uuid))))
res += literal('')
return res
@@ -146,8 +131,8 @@ class _ProtectedPersonRenderer(formalchemy.FieldRenderer):
def render_readonly(self, **kwargs):
res = str(self.person)
- res += hidden('User--person_uuid',
- value=self.field.parent.person_uuid.value)
+ res += tags.hidden('User--person_uuid',
+ value=self.field.parent.person_uuid.value)
return res
@@ -161,18 +146,19 @@ def ProtectedPersonRenderer(uuid):
class _LinkedPersonRenderer(formalchemy.FieldRenderer):
def render_readonly(self, **kwargs):
- return link_to(str(self.raw_value),
- self.request.route_url('person.edit', uuid=self.value))
+ return tags.link_to(str(self.raw_value),
+ self.request.route_url('person.edit', uuid=self.value))
def LinkedPersonRenderer(request):
- return type('LinkedPersonRenderer', (_LinkedPersonRenderer,), {'request': request})
+ return type('LinkedPersonRenderer', (_LinkedPersonRenderer,),
+ {'request': request})
class PasswordFieldRenderer(formalchemy.PasswordFieldRenderer):
def render(self, **kwargs):
- return password(self.name, value='', maxlength=self.length, **kwargs)
+ return tags.password(self.name, value='', maxlength=self.length, **kwargs)
def passwords_match(value, field):
@@ -196,94 +182,42 @@ class PasswordField(formalchemy.Field):
set_user_password(self.model, password)
-def user_fieldset(user, request):
- fs = forms.make_fieldset(user, url=request.route_url,
- url_action=request.current_route_url(),
- route_name='users.list')
+class UserCrud(Crud):
- fs.append(PasswordField('password'))
- fs.append(formalchemy.Field('confirm_password',
- renderer=PasswordFieldRenderer))
+ mapped_class = edbob.User
+ home_route = 'users.list'
- fs.append(RolesField(
- 'roles', renderer=RolesFieldRenderer(request)))
+ def fieldset(self, user):
+ fs = self.make_fieldset(user)
- fs.configure(
- include=[
- fs.person,
- fs.username,
- fs.password.label("Set Password"),
- fs.confirm_password,
- fs.roles,
- ])
+ fs.append(PasswordField('password'))
+ fs.append(formalchemy.Field('confirm_password',
+ renderer=PasswordFieldRenderer))
+ fs.append(RolesField('roles',
+ renderer=RolesFieldRenderer(self.request)))
- if isinstance(user, edbob.User) and user.person:
- fs.person.set(readonly=True,
- renderer=LinkedPersonRenderer(request))
+ fs.configure(
+ include=[
+ fs.username,
+ fs.person,
+ fs.password.label("Set Password"),
+ fs.confirm_password,
+ fs.roles,
+ ])
- return fs
+ # if fs.edit and user.person:
+ if isinstance(user, edbob.User) and user.person:
+ fs.person.set(readonly=True,
+ renderer=LinkedPersonRenderer(self.request))
+ return fs
-def new_user(request):
- """
- View for creating a new :class:`edbob.User` instance.
- """
-
- fs = user_fieldset(edbob.User, request)
- if request.POST:
- fs.rebind(data=request.params)
- if fs.validate():
-
- with transaction.manager:
- Session.add(fs.model)
- fs.sync()
- request.session.flash("%s \"%s\" has been %s." % (
- fs.crud_title, fs.get_display_text(),
- 'updated' if fs.edit else 'created'))
- home = request.route_url('users.list')
-
- return HTTPFound(location=home)
-
- if fs.person_uuid.value:
+ def validation_failed(self, fs):
+ if not fs.edit and fs.person_uuid.value:
fs.person.set(readonly=True,
renderer=ProtectedPersonRenderer(fs.person_uuid.value))
- return {'fieldset': fs, 'crud': True}
-
-
-def edit_user(request):
- uuid = request.matchdict['uuid']
- user = Session.query(edbob.User).get(uuid) if uuid else None
- assert user
-
- fs = user_fieldset(user, request)
- if request.POST:
- fs.rebind(data=request.params)
- if fs.validate():
-
- with transaction.manager:
- Session.add(fs.model)
- fs.sync()
- request.session.flash("%s \"%s\" has been %s." % (
- fs.crud_title, fs.get_display_text(),
- 'updated' if fs.edit else 'created'))
- home = request.route_url('users.list')
-
- return HTTPFound(location=home)
-
- return {'fieldset': fs, 'crud': True}
-
def includeme(config):
-
- config.add_route('users.list', '/users')
- config.add_view(users, route_name='users.list', renderer='/users/index.mako',
- permission='users.list', http_cache=0)
-
- config.add_route('user.new', '/users/new')
- config.add_view(new_user, route_name='user.new', renderer='/users/user.mako',
- permission='users.create', http_cache=0)
-
- config.add_route('user.edit', '/users/{uuid}/edit')
- config.add_view(edit_user, route_name='user.edit', renderer='/users/user.mako',
- permission='users.edit', http_cache=0)
+ UserGrid.add_route(config, 'users.list', '/users')
+ UserCrud.add_routes(config)