diff --git a/edbob/pyramid/templates/edbob/form.mako b/edbob/pyramid/templates/edbob/form.mako
new file mode 100644
index 0000000..ad9e14a
--- /dev/null
+++ b/edbob/pyramid/templates/edbob/form.mako
@@ -0,0 +1,16 @@
+<%inherit file="/base.mako" />
+
+<%def name="buttons()">%def>
+
+
diff --git a/edbob/pyramid/templates/edbob/form_body.mako b/edbob/pyramid/templates/edbob/form_body.mako
new file mode 100644
index 0000000..a16a25e
--- /dev/null
+++ b/edbob/pyramid/templates/edbob/form_body.mako
@@ -0,0 +1,58 @@
+<% _focus_rendered = False %>
+
+
+
+
diff --git a/edbob/pyramid/templates/edbob/login.mako b/edbob/pyramid/templates/edbob/login.mako
index cd5753d..fcc4dc1 100644
--- a/edbob/pyramid/templates/edbob/login.mako
+++ b/edbob/pyramid/templates/edbob/login.mako
@@ -40,21 +40,21 @@ ${h.image(logo_url, "${self.global_title()} logo", id='login-logo', **logo_kwarg
$(function() {
$('form').submit(function() {
- if (! $('#username').val()) {
- with ($('#username').get(0)) {
- select();
- focus();
- }
- return false;
- }
- if (! $('#password').val()) {
- with ($('#password').get(0)) {
- select();
- focus();
- }
- return false;
- }
- return true;
+ if (! $('#username').val()) {
+ with ($('#username').get(0)) {
+ select();
+ focus();
+ }
+ return false;
+ }
+ if (! $('#password').val()) {
+ with ($('#password').get(0)) {
+ select();
+ focus();
+ }
+ return false;
+ }
+ return true;
});
$('#username').focus();
diff --git a/edbob/pyramid/templates/form.mako b/edbob/pyramid/templates/form.mako
new file mode 100644
index 0000000..cae0447
--- /dev/null
+++ b/edbob/pyramid/templates/form.mako
@@ -0,0 +1,2 @@
+<%inherit file="/edbob/form.mako" />
+${parent.body()}
diff --git a/edbob/pyramid/templates/forms/field_autocomplete.mako b/edbob/pyramid/templates/forms/field_autocomplete.mako
index d65673a..33498ce 100644
--- a/edbob/pyramid/templates/forms/field_autocomplete.mako
+++ b/edbob/pyramid/templates/forms/field_autocomplete.mako
@@ -1,6 +1,6 @@
- ${hidden(fieldname, id=fieldname, value=fieldvalue)}
- ${text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
+ ${h.hidden(fieldname, id=fieldname, value=fieldvalue)}
+ ${h.text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
class_='autocomplete-textbox', style='display: none;' if fieldvalue else '')}
${display}
@@ -9,18 +9,18 @@
diff --git a/edbob/pyramid/templates/forms/fieldset.mako b/edbob/pyramid/templates/forms/fieldset.mako
index a0a325a..365bb37 100644
--- a/edbob/pyramid/templates/forms/fieldset.mako
+++ b/edbob/pyramid/templates/forms/fieldset.mako
@@ -52,7 +52,7 @@
diff --git a/edbob/pyramid/templates/forms/form.mako b/edbob/pyramid/templates/forms/form.mako
new file mode 100644
index 0000000..63b7d50
--- /dev/null
+++ b/edbob/pyramid/templates/forms/form.mako
@@ -0,0 +1,62 @@
+<% _focus_rendered = False %>
+
+
+
+
diff --git a/edbob/pyramid/views/__init__.py b/edbob/pyramid/views/__init__.py
index debf617..b42d01f 100644
--- a/edbob/pyramid/views/__init__.py
+++ b/edbob/pyramid/views/__init__.py
@@ -27,67 +27,15 @@
"""
from pyramid.httpexceptions import HTTPFound
-from pyramid.renderers import render_to_response
from pyramid.security import authenticated_userid
from webhelpers.html import literal
from webhelpers.html.tags import link_to
-from edbob.pyramid import Session
-from edbob.pyramid.forms.formalchemy import AutocompleteFieldRenderer
-from edbob.util import requires_impl
+from edbob.pyramid.views.autocomplete import *
+from edbob.pyramid.views.form import *
-class Autocomplete(object):
-
- def __init__(self, request):
- self.request = request
-
- @property
- @requires_impl(is_property=True)
- def mapped_class(self):
- raise NotImplementedError
-
- @property
- @requires_impl(is_property=True)
- def fieldname(self):
- raise NotImplementedError
-
- @property
- @requires_impl(is_property=True)
- def route_name(self):
- raise NotImplementedError
-
- def __call__(self):
- query = self.request.params['query']
- q = Session.query(self.mapped_class)
- q = q.filter(getattr(self.mapped_class, self.fieldname).ilike('%%%s%%' % query))
- objs = q.order_by(getattr(self.mapped_class, self.fieldname)).all()
- data = dict(
- query=query,
- suggestions=[getattr(x, self.fieldname) for x in objs],
- data=[x.uuid for x in objs],
- )
- response = render_to_response('json', data, request=self.request)
- response.headers['Content-Type'] = 'application/json'
- return response
-
- @classmethod
- def add_route(cls, config, url):
- # def add_routes(cls, config, route_prefix, url_prefix, template_prefix=None, permission_prefix=None):
- """
- Add 'autocomplete' route to the config object.
- """
-
- config.add_route(cls.route_name, url)
- config.add_view(cls, route_name=cls.route_name, http_cache=0)
-
- @classmethod
- def renderer(cls, request):
- return AutocompleteFieldRenderer(request.route_url(cls.route_name),
- (cls.mapped_class, cls.fieldname))
-
-
def forbidden(request):
"""
The forbidden view. This is triggered whenever access rights are denied
diff --git a/edbob/pyramid/views/autocomplete.py b/edbob/pyramid/views/autocomplete.py
new file mode 100644
index 0000000..8f627d1
--- /dev/null
+++ b/edbob/pyramid/views/autocomplete.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+################################################################################
+#
+# edbob -- Pythonic Software Framework
+# Copyright © 2010-2012 Lance Edgar
+#
+# This file is part of edbob.
+#
+# edbob is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# edbob 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 Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with edbob. If not, see
.
+#
+################################################################################
+
+"""
+``edbob.pyramid.views.autocomplete`` -- Autocomplete View
+"""
+
+from pyramid.renderers import render_to_response
+
+from edbob.pyramid import Session
+from edbob.pyramid.forms.formalchemy import AutocompleteFieldRenderer
+from edbob.util import requires_impl
+
+
+__all__ = ['Autocomplete']
+
+
+class Autocomplete(object):
+
+ route = None
+ url = None
+
+ def __init__(self, request):
+ self.request = request
+
+ @property
+ @requires_impl(is_property=True)
+ def mapped_class(self):
+ raise NotImplementedError
+
+ @property
+ @requires_impl(is_property=True)
+ def fieldname(self):
+ raise NotImplementedError
+
+ @classmethod
+ def get_route(cls):
+ if not cls.route:
+ name = cls.mapped_class.__name__.lower()
+ cls.route = '%ss.autocomplete' % name
+ return cls.route
+
+ def filter_query(self, q):
+ return q
+
+ def make_query(self, query):
+ q = Session.query(self.mapped_class)
+ q = self.filter_query(q)
+ q = q.filter(getattr(self.mapped_class, self.fieldname).ilike('%%%s%%' % query))
+ q = q.order_by(getattr(self.mapped_class, self.fieldname))
+ return q
+
+ def query(self, query):
+ return self.make_query(query)
+
+ def __call__(self):
+ query = self.request.params['query']
+ objs = self.query(query).all()
+ data = dict(
+ query=query,
+ suggestions=[getattr(x, self.fieldname) for x in objs],
+ data=[x.uuid for x in objs],
+ )
+ response = render_to_response('json', data, request=self.request)
+ response.headers['Content-Type'] = 'application/json'
+ return response
+
+ @classmethod
+ def add_route(cls, config):
+ """
+ Add 'autocomplete' route to the config object.
+ """
+
+ name = cls.mapped_class.__name__.lower()
+ route = cls.get_route()
+ url = cls.url or '/%ss/autocomplete' % name
+
+ config.add_route(route, url)
+ config.add_view(cls, route_name=route, http_cache=0)
+
+ @classmethod
+ def renderer(cls, request):
+ return AutocompleteFieldRenderer(request.route_url(cls.get_route()),
+ (cls.mapped_class, cls.fieldname))
diff --git a/edbob/pyramid/views/crud.py b/edbob/pyramid/views/crud.py
index 3be0b60..c2e4a3e 100644
--- a/edbob/pyramid/views/crud.py
+++ b/edbob/pyramid/views/crud.py
@@ -37,6 +37,7 @@ from pyramid.httpexceptions import HTTPFound, HTTPException
# from rattail.db.perms import has_permission
# from rattail.pyramid.forms.formalchemy import Grid
+import edbob
from edbob.db import Base
from edbob.pyramid import forms
from edbob.pyramid import Session
@@ -45,28 +46,42 @@ from edbob.util import requires_impl
class Crud(object):
+ routes = ['new', 'edit', 'delete']
+
+ route_prefix = None
+ url_prefix = None
+ template_prefix = None
+
def __init__(self, request):
self.request = request
@property
@requires_impl(is_property=True)
def mapped_class(self):
- raise NotImplementedError
+ pass
@property
@requires_impl(is_property=True)
- def list_route(self):
- raise NotImplementedError
+ def home_route(self):
+ pass
@property
- def list_url(self):
- return self.request.route_url(self.list_route)
+ def home_url(self):
+ return self.request.route_url(self.home_route)
+
+ @property
+ def cancel_route(self):
+ return None
+
+ @property
+ def permission_prefix(self):
+ return self.route_prefix + 's'
def make_fieldset(self, model, **kwargs):
if 'action_url' not in kwargs:
kwargs['action_url'] = self.request.current_route_url()
- if 'list_url' not in kwargs:
- kwargs['list_url'] = self.list_url
+ if 'home_url' not in kwargs:
+ kwargs['home_url'] = self.home_url
return forms.make_fieldset(model, **kwargs)
def fieldset(self, obj):
@@ -80,25 +95,55 @@ class Crud(object):
fs = self.make_fieldset(obj)
return fs
- def crud(self, obj):
+ def post_sync(self, fs):
+ pass
+
+ def crud(self, obj=None):
+ if obj is None:
+ obj = self.mapped_class
+
+ # fs = self.fieldset(obj)
+ # if not fs.readonly and self.request.POST:
+ # fs.rebind(data=self.request.params)
+ # if fs.validate():
+
+ # with transaction.manager:
+ # fs.sync()
+ # 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 self.request.params.get('add-another') == '1':
+ # return HTTPFound(location=self.request.current_route_url())
+
+ # return HTTPFound(location=self.home_url)
fs = self.fieldset(obj)
if not fs.readonly and self.request.POST:
fs.rebind(data=self.request.params)
if fs.validate():
+ result = None
+
with transaction.manager:
fs.sync()
- 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'))
+ 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
if self.request.params.get('add-another') == '1':
return HTTPFound(location=self.request.current_route_url())
- return HTTPFound(location=self.list_url)
+ return HTTPFound(location=self.home_url)
# TODO: This probably needs attention.
if not fs.edit:
@@ -121,31 +166,40 @@ class Crud(object):
assert obj
with transaction.manager:
Session.delete(obj)
- return HTTPFound(location=self.request.route_url(self.list_route))
+ return HTTPFound(location=self.home_url)
@classmethod
- def add_routes(cls, config, route_prefix, url_prefix, template_prefix=None, permission_prefix=None):
+ def add_routes(cls, config):
"""
- Add standard routes (i.e. 'new', 'edit' and 'delete') for the mapped
- class to the config object.
+ Add routes to the config object.
"""
- if not template_prefix:
- template_prefix = url_prefix
- if not permission_prefix:
- permission_prefix = route_prefix + 's'
+ routes = cls.routes
+ if isinstance(routes, list):
+ _routes = routes
+ routes = {}
+ for route in _routes:
+ routes[route] = {}
- config.add_route('%s.new' % route_prefix, '%s/new' % url_prefix)
- config.add_view(cls, attr='new', route_name='%s.new' % route_prefix, renderer='%s/crud.mako' % template_prefix,
- permission='%s.create' % permission_prefix, http_cache=0)
+ route_prefix = cls.route_prefix or cls.mapped_class.__name__.lower()
+ url_prefix = cls.url_prefix or '/%ss' % route_prefix
+ template_prefix = cls.template_prefix or url_prefix
+ permission_prefix = cls.permission_prefix or '%ss' % route_prefix
- config.add_route('%s.edit' % route_prefix, '%s/{uuid}/edit' % url_prefix)
- config.add_view(cls, attr='edit', route_name='%s.edit' % route_prefix, renderer='%s/crud.mako' % template_prefix,
- permission='%s.edit' % permission_prefix, http_cache=0)
-
- config.add_route('%s.delete' % route_prefix, '%s/{uuid}/delete' % url_prefix)
- config.add_view(cls, attr='delete', route_name='%s.delete' % route_prefix,
- permission='%s.delete' % permission_prefix, http_cache=0)
+ for action in routes:
+ kw = dict(
+ route='%s.%s' % (route_prefix, action),
+ renderer='%s/%s.mako' % (template_prefix, action),
+ permission='%s.%s' % (permission_prefix, dict(new='create').get(action, action)),
+ )
+ if action == 'new':
+ kw['url'] = '%s/new' % url_prefix
+ else:
+ kw['url'] = '%s/{uuid}/%s' % (url_prefix, action)
+ kw.update(routes[action])
+ config.add_route(kw['route'], kw['url'])
+ config.add_view(cls, attr=action, route_name=kw['route'], renderer=kw['renderer'],
+ permission=kw['permission'], http_cache=0)
def crud(request, cls, fieldset_factory, home=None, delete=None, post_sync=None, pre_render=None):
diff --git a/edbob/pyramid/views/form.py b/edbob/pyramid/views/form.py
new file mode 100644
index 0000000..3eec8a4
--- /dev/null
+++ b/edbob/pyramid/views/form.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+################################################################################
+#
+# edbob -- Pythonic Software Framework
+# Copyright © 2010-2012 Lance Edgar
+#
+# This file is part of edbob.
+#
+# edbob is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# edbob 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 Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with edbob. If not, see
.
+#
+################################################################################
+
+"""
+``edbob.pyramid.views.form`` -- Generic Form View
+"""
+
+import transaction
+from pyramid.httpexceptions import HTTPFound
+
+from edbob.pyramid import Session
+from edbob.pyramid.forms import SimpleForm
+from edbob.util import requires_impl
+
+
+__all__ = ['FormView']
+
+
+class FormView(object):
+ """
+ This view provides basic form processing goodies.
+ """
+
+ route = None
+ url = None
+ template = None
+ permission = None
+
+ def __init__(self, request):
+ self.request = request
+
+ def __call__(self):
+ """
+ Callable for the view. This method creates the underlying form and
+ processes data if any was submitted.
+ """
+
+ f = self.form(self.request)
+ if not f.readonly and self.request.POST:
+ f.rebind(data=self.request.params)
+ if f.validate():
+
+ with transaction.manager:
+ f.save(Session)
+ Session.flush()
+ self.request.session.flash('The book "%s" has been loaned.' % f.book.value)
+
+ if self.request.params.get('keep-going') == '1':
+ return HTTPFound(location=self.request.current_route_url())
+
+ return HTTPFound(location=f.home_url)
+
+ return {'form': f}
+
+ def make_form(self, request, **kwargs):
+ """
+ Returns a :class:`edbob.pyramid.forms.Form` instance.
+ """
+ template = kwargs.pop('template', self.template or '%s.mako' % self.url)
+ return SimpleForm(request, template=template, **kwargs)
+
+ def form(self, request):
+ """
+ Should create and return a :class:`edbob.pyramid.forms.Form` instance
+ for the view.
+ """
+ return self.make_form(request)
+
+ @classmethod
+ def add_route(cls, config, **kwargs):
+ route = kwargs.get('route', cls.route)
+ url = kwargs.get('url', cls.url)
+ permission = kwargs.get('permission', cls.permission or route)
+ template = kwargs.get('template', cls.template or '%s.mako' % url)
+ config.add_route(route, url)
+ config.add_view(cls, route_name=route, renderer=template,
+ permission=permission, http_cache=0)